From 1b79edd28446df4a9ab0740e3f3a56fc2628ac97 Mon Sep 17 00:00:00 2001 From: tboova469_comcast Date: Mon, 8 Dec 2025 17:43:54 +0530 Subject: [PATCH 01/10] test-L1: Do not merge Reason for change: For testing purpose only --- .github/workflows/jsruntime_L1tests.yml | 106 ------------------------ .github/workflows/native-full-build.yml | 89 -------------------- src/JavaScriptContextBase.cpp | 43 +++++++++- 3 files changed, 42 insertions(+), 196 deletions(-) delete mode 100644 .github/workflows/jsruntime_L1tests.yml delete mode 100644 .github/workflows/native-full-build.yml diff --git a/.github/workflows/jsruntime_L1tests.yml b/.github/workflows/jsruntime_L1tests.yml deleted file mode 100644 index fd7d3c4..0000000 --- a/.github/workflows/jsruntime_L1tests.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: L1 Unit Tests for rdkNativeScript -permissions: - contents: read - checks: write - -on: - push: - branches: [develop] - pull_request: - branches: [develop] - workflow_dispatch: - -env: - AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }} - AUTOMATICS_PASSCODE: ${{ secrets.AUTOMATICS_PASSCODE }} - -jobs: - build-and-test-l1: - runs-on: ubuntu-latest - - steps: - - name: Checkout source repository - uses: actions/checkout@v3 - - - name: Checkout rdkNativeScript_tests repository - uses: actions/checkout@v3 - with: - repository: rdk-e/rdkNativeScript_tests - ref: topic/RDKEMW-5610 - path: rdkNativeScript_tests - token: ${{ secrets.GH_PAT }} - - - name: Install dependencies - run: | - sudo apt-get update && sudo apt-get install -y \ - g++ \ - cmake \ - build-essential \ - libcurl4-openssl-dev \ - libcjson-dev \ - libgtest-dev \ - libssl-dev \ - zlib1g-dev \ - libuv1-dev \ - lcov \ - libglib2.0-dev - - - name: Build Google Test and Google Mock - run: | - cd /usr/src/googletest - sudo cmake -S . -B build - sudo cmake --build build - sudo cp build/lib/libgmock.a /usr/lib - sudo cp build/lib/libgmock_main.a /usr/lib - sudo cp build/lib/libgtest.a /usr/lib - sudo cp build/lib/libgtest_main.a /usr/lib - - - name: Configure and Build L1 - run: | - mkdir -p build_l1 - cd build_l1 - cmake -DCMAKE_BUILD_TYPE=Debug \ - -DRUN_L1=ON \ - -DRUN_L2=OFF \ - -DENABLE_JSRUNTIME_ESSOS=ON \ - -DENABLE_JSRUNTIME_PLAYER=ON \ - -DENABLE_AAMP_JSBINDINGS=ON \ - -DENABLE_AAMP_JSBINDINGS_DYNAMIC=ON \ - -DENABLE_AAMP_JSBINDINGS_STATIC=OFF \ - -DJSRUNTIME_ENGINE_NAME=jsc \ - -Djsruntime_source=.. ../rdkNativeScript_tests - make -j$(nproc) - - - name: Run L1 Tests and Generate JUnit Results - run: | - cd build_l1 - #ctest -R RunL1Tests --output-on-failure --no-compress-output --output-junit ctest-results.xml - ./L1_tests --gtest_output=xml:ctest-results.xml - - - name: Publish L1 test results - uses: dorny/test-reporter@v1 - with: - name: Unit Test Results - path: build_l1/ctest-results.xml - reporter: java-junit - - - name: Generate coverage report - run: | - cd build_l1 - lcov --capture --directory . --output-file coverage.info --ignore-errors mismatch --rc geninfo_unexecuted_blocks=1 - lcov --remove coverage.info '/usr/*' '*/test/*' '*/tests/*' '*/L1/*' '*/mocks/*' '*/gtest/*' '*/gmock/*' '*/googletest/*' '*/include/*' --output-file coverage.cleaned.info --ignore-errors unused - lcov --extract coverage.cleaned.info '*/src/*' --output-file coverage.final.info - genhtml coverage.final.info --output-directory html_coverage_report - - - name: Upload test result file (JUnit XML) - uses: actions/upload-artifact@v4 - with: - name: ctest-results-l1-${{ github.run_id }} - path: build_l1/ctest-results.xml - - - name: Upload coverage report - uses: actions/upload-artifact@v4 - with: - name: l1-html-coverage-report - path: build_l1/html_coverage_report - if-no-files-found: warn diff --git a/.github/workflows/native-full-build.yml b/.github/workflows/native-full-build.yml deleted file mode 100644 index f8137e6..0000000 --- a/.github/workflows/native-full-build.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Build Component in Native Environment -permissions: - contents: read - checks: write - -on: - workflow_dispatch: - push: - branches: [develop] - pull_request: - branches: [develop] - -env: - AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }} - AUTOMATICS_PASSCODE: ${{ secrets.AUTOMATICS_PASSCODE }} - -jobs: - build-nativescript: - runs-on: ubuntu-latest - - steps: - - name: Checkout main source repository - uses: actions/checkout@v3 - - - name: Install dependencies - run: | - sudo apt-get update && sudo apt-get install -y \ - git cmake gperf ruby-dev libglew-dev libglut-dev \ - libglib2.0-dev libglib2.0-0 g++ meson bison libjpeg-dev libpng-dev \ - libfreetype6-dev libicu-dev autoconf libtool libxml2-dev \ - libcurl4-openssl-dev libexpat1-dev doxygen vim libcjson-dev \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libwayland-bin libwayland-dev wayland-protocols \ - libreadline-dev net-tools libmount1 libpcre3 libselinux1 zlib1g \ - libunwind-dev libboost-dev libboost-system-dev libboost-thread-dev libboost-chrono-dev libwebsocketpp-dev jq - - - name: Get asset ID for externals.zip - id: asset - run: | - ASSET_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GH_PAT }}" \ - https://api.github.com/repos/rdk-e/rdkNativeScript_tests/releases/tags/extlibs | \ - jq '.assets[] | select(.name=="externals.zip") | .id') - echo "ASSET_ID=$ASSET_ID" >> $GITHUB_ENV - - - name: Download externals.zip using asset ID - run: | - curl -L \ - -H "Authorization: Bearer ${{ secrets.GH_PAT }}" \ - -H "Accept: application/octet-stream" \ - -o externals.zip \ - https://api.github.com/repos/rdk-e/rdkNativeScript_tests/releases/assets/$ASSET_ID - - - name: Check externals.zip download - run: | - file externals.zip - ls -lh externals.zip - unzip -l externals.zip | head -20 - - - name: Extract externals.zip - run: | - unzip -o externals.zip - - - name: Set LD_LIBRARY_PATH for ICU 66 - run: echo "LD_LIBRARY_PATH=$(pwd)/externals/extlibs/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV - - - name: Configure and build nativescript - run: | - mkdir -p build - cd build - cmake .. \ - -DENABLE_JSRUNTIME_ESSOS=ON \ - -DJSRUNTIME_ENGINE_NAME=jsc \ - -DENABLE_JSRUNTIME_PLAYER=ON \ - -DENABLE_AAMP_JSBINDINGS_DYNAMIC=ON \ - -DBUILD_JSRUNTIME_CLIENT=ON \ - -DENABLE_JSRUNTIME_SERVER=ON \ - -DCMAKE_CXX_FLAGS="-I../externals/extlibs/include/rtcore" - cmake --build . - - - name: Upload build outputs to artifact - uses: actions/upload-artifact@v4 - with: - name: jsruntime-build-artifacts - path: | - build/JSRuntimeClient - build/JSRuntimeJSC - build/libjsclib.so - build/libJSRuntimeJSC.so - if-no-files-found: error diff --git a/src/JavaScriptContextBase.cpp b/src/JavaScriptContextBase.cpp index 086b7c9..3128195 100644 --- a/src/JavaScriptContextBase.cpp +++ b/src/JavaScriptContextBase.cpp @@ -77,7 +77,7 @@ void JavaScriptContextBase::registerCommonUtils() } } -std::string JavaScriptContextBase::readFile(const char *file) +/*std::string JavaScriptContextBase::readFile(const char *file) { bool isModule = true; std::ifstream src_file; @@ -97,6 +97,47 @@ std::string JavaScriptContextBase::readFile(const char *file) src_script << src_file.rdbuf(); } return src_script.str(); +}*/ +std::string JavaScriptContextBase::readFile(const char *file) +{ + bool isModule = true; + std::ifstream src_file; + std::stringstream src_script; + struct stat path; + + // Try CWD first + if(stat(file, &path) == 0){ + isModule = false; + } + if(!isModule){ + src_file.open(file); + src_script << src_file.rdbuf(); + return src_script.str(); // <--- Early return if found in CWD! + } + + // Try sModulesPath + file + std::string fileName = sModulesPath + std::string(file); + std::cout << "[DEBUG] JS module loader: Trying " << fileName << std::endl; + src_file.open(fileName); + if(src_file.is_open()) { + src_script << src_file.rdbuf(); + return src_script.str(); + } + + // If not found, and not ending with '.js', try adding '.js' + std::string fileStr(file); + if(fileStr.size() < 3 || fileStr.substr(fileStr.size()-3) != ".js") { + std::string fileNameJs = sModulesPath + fileStr + ".js"; + std::cout << "[DEBUG] JS module loader: Trying " << fileNameJs << std::endl; + src_file.open(fileNameJs); + if(src_file.is_open()) { + src_script << src_file.rdbuf(); + return src_script.str(); + } + } + + std::cout << "[ERROR] JS module loader: Could not find " << file << " as " << fileName << " or " << fileName << ".js" << std::endl; + return src_script.str(); } bool JavaScriptContextBase::runFile(const char *file, const char* args, bool isApplication) From f2fbbe9eec146be3b91a8aa550dddb26ff43d2ff Mon Sep 17 00:00:00 2001 From: gurpreet319 Date: Fri, 14 Nov 2025 18:47:43 +0530 Subject: [PATCH 02/10] RDKEMW-9355: Add Support to run app widgets in different contexts within a single process Reason for change: Changes related to client/server Test Procedure: build should be successful. Risks: low Priority: P2 --- CMakeLists.txt | 28 ++- include/JSRuntimeContainer.h | 43 +++++ include/JSRuntimeServer.h | 4 +- src/JSRuntimeClientContainer.cpp | 31 ++++ src/JSRuntimeContainer.cpp | 302 +++++++++++++++++++++++++++++++ src/JSRuntimeServer.cpp | 70 ++++++- src/jsruntime.cpp | 2 +- 7 files changed, 467 insertions(+), 13 deletions(-) create mode 100644 include/JSRuntimeContainer.h create mode 100644 src/JSRuntimeClientContainer.cpp create mode 100644 src/JSRuntimeContainer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index df6acc3..de08db5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,8 +118,8 @@ set(JSRUNTIME_APP_FILES if ( ENABLE_JSRUNTIME_SERVER ) add_definitions("-DENABLE_JSRUNTIME_SERVER") - add_definitions("-DWS_SERVER_PORT=9112") - set (JSRUNTIME_APP_FILES ${JSRUNTIME_APP_FILES} + add_definitions("-DWS_SERVER_PORT=5000") + set (JSRUNTIME_COMMON_FILES ${JSRUNTIME_COMMON_FILES} ${JSRUNTIME_COMMON_SOURCE_DIRECTORY}/JSRuntimeServer.cpp ) endif ( ENABLE_JSRUNTIME_SERVER ) @@ -134,6 +134,15 @@ add_library(${JSRUNTIME_LIBRARY_NAME} SHARED ${JSRUNTIME_ENGINE_FILES} ) +#JSRUNTIMECLIENTCONTAINER CHANGES +option(BUILD_JSRUNTIME_CONTAINER "BUILD_JSRUNTIME_CONTAINER" ON) +set(JSRUNTIME_CONTAINER_FILES + ${JSRUNTIME_COMMON_SOURCE_DIRECTORY}/JSRuntimeClientContainer.cpp +) +set(JSRUNTIME_FILES + ${JSRUNTIME_COMMON_SOURCE_DIRECTORY}/JSRuntimeContainer.cpp +) + set(JSRUNTIME_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/${JSRUNTIME_ENGINE_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include/linux ${CMAKE_CURRENT_SOURCE_DIR}/src/jsc/jsc_lib ${JSRUNTIME_ENGINE_INCLUDE_DIRECTORIES}) set(JSRUNTIME_LIBRARY_LINK_DIRECTORIES ${JSRUNTIME_ENGINE_LIBRARY_LINK_DIRECTORIES}) @@ -192,6 +201,21 @@ if (BUILD_JSRUNTIME_CLIENT) target_link_libraries(jsruntime_client ${JSRUNTIME_LIBRARY_LINK_DIRECTORIES} ${JSRUNTIME_LINK_ETHANLIB} -lpthread) endif (BUILD_JSRUNTIME_CLIENT) +set(JSRUNTIMECONTAINER_LIBRARY_NAME "JSRuntimeContainer") + +if (BUILD_JSRUNTIME_CONTAINER) + add_library(${JSRUNTIMECONTAINER_LIBRARY_NAME} SHARED ${JSRUNTIME_FILES}) + target_include_directories(${JSRUNTIMECONTAINER_LIBRARY_NAME} PRIVATE ${JSRUNTIME_INCLUDE_DIRECTORIES}) + target_link_libraries(${JSRUNTIMECONTAINER_LIBRARY_NAME} ${JSRUNTIME_LIBRARY_LINK_DIRECTORIES} -lpthread) + + add_executable(jsruntime_container ${JSRUNTIME_CONTAINER_FILES}) + add_dependencies(jsruntime_container ${JSRUNTIMECONTAINER_LIBRARY_NAME}) + target_include_directories(jsruntime_container PRIVATE ${JSRUNTIME_INCLUDE_DIRECTORIES}) + set_target_properties(jsruntime_container PROPERTIES OUTPUT_NAME "JSRuntimeContainer") + target_link_libraries(jsruntime_container ${JSRUNTIME_LIBRARY_LINK_DIRECTORIES} -l${JSRUNTIMECONTAINER_LIBRARY_NAME} -lpthread) + +endif (BUILD_JSRUNTIME_CONTAINER) + set(UWEBSOCKETS_TARGET "Linux") if (APPLE) set(UWEBSOCKETS_TARGET "Darwin") diff --git a/include/JSRuntimeContainer.h b/include/JSRuntimeContainer.h new file mode 100644 index 0000000..7a0d6cf --- /dev/null +++ b/include/JSRuntimeContainer.h @@ -0,0 +1,43 @@ +#ifndef JSRUNTIMECONTAINER_H +#define JSRUNTIMECONTAINER_H + +#include +#include +#include + +class JSRuntimeContainer +{ +public: + enum Namespace { + NetworkNamespace = 0x01, + MountNamespace = 0x02, + IpcNamespace = 0x04, + PidNamespace = 0x08, + UserNamespace = 0x10, + UtsNamespace = 0x20 + }; + + // Get container IP address + static std::string getContainerIpAddress(const std::string& containerId); + + // Check if container exists + static bool isContainer(const std::string& containerId); + + // Execute function in container namespace + static bool nsEnter(const std::string& containerId, Namespace type, const std::function& func); + + // WebSocket client functions + static bool connectAndSend(const std::string& ip, const std::string& message); + static std::string buildLaunchMessage(const std::string& url, const std::string& options); + static std::string parseAppConfig(const std::string& configPath); + +private: + // Internal implementation functions + static bool nsEnterImpl(const std::string& containerId, Namespace type, const std::function& func); + static pid_t findContainerPid(const std::string& containerId); + static bool nsEnterWithPid(pid_t pid, int nsType, const std::function& func); + static void nsThread(int newNsFd, int nsType, bool* success, const std::function& func); +}; + +#endif // JSRUNTIMECONTAINER_H + diff --git a/include/JSRuntimeServer.h b/include/JSRuntimeServer.h index d9f0616..b56690c 100644 --- a/include/JSRuntimeServer.h +++ b/include/JSRuntimeServer.h @@ -19,6 +19,7 @@ #pragma once #include +#include #ifdef USE_WEBSOCKET_MOCK #include "websocketpp.hpp" @@ -40,7 +41,7 @@ class JSRuntimeServer static JSRuntimeServer *getInstance(); ~JSRuntimeServer() = default; - void initialize(int serverport, std::shared_ptr renderer); + void initialize(int serverport, std::shared_ptr renderer, std::shared_ptr externalHandler = nullptr); bool start(); bool stop(); @@ -77,4 +78,5 @@ class JSRuntimeServer ConnectionSet mConnections; int mServerPort; std::shared_ptr mRenderer; + std::shared_ptr mExternalHandler; }; diff --git a/src/JSRuntimeClientContainer.cpp b/src/JSRuntimeClientContainer.cpp new file mode 100644 index 0000000..f68f96e --- /dev/null +++ b/src/JSRuntimeClientContainer.cpp @@ -0,0 +1,31 @@ +#include "JSRuntimeContainer.h" +#include +#include +#include +#include +#include "NativeJSLogger.h" +int main() +{ + std::string containerId = "com.sky.as.apps_TestApp"; + const std::string basePath = "/opt/twocontext"; // constant base path + const std::vector apps = {"app1", "app2"}; + + std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId); + if (ipAddress.empty()) { + NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container"); + return 1; + } + + for (const auto &app : apps) { + std::string url = basePath + std::string("/") + app + std::string("/index.html"); + if (access(url.c_str(), F_OK) == 0) { + std::string pathAppConfig = basePath + std::string("/") + app + std::string("/app.config"); + std::string options = JSRuntimeContainer::parseAppConfig(pathAppConfig); + std::string message = JSRuntimeContainer::buildLaunchMessage(url, options); + JSRuntimeContainer::connectAndSend(ipAddress, message); + } + } + + return 0; +} + diff --git a/src/JSRuntimeContainer.cpp b/src/JSRuntimeContainer.cpp new file mode 100644 index 0000000..4952eda --- /dev/null +++ b/src/JSRuntimeContainer.cpp @@ -0,0 +1,302 @@ +// JSRuntimeContainer.cpp +#include "JSRuntimeContainer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rapidjson/document.h" +#ifndef USE_WEBSOCKET_MOCK +#include +#include +#endif + +using namespace rapidjson; + +#ifndef USE_WEBSOCKET_MOCK +typedef websocketpp::client SimpleClient; +#endif + +void JSRuntimeContainer::nsThread(int newNsFd, int nsType, bool* success, const std::function& func) +{ + if (setns(newNsFd, nsType) != 0) + { + std::cerr << "Failed to switch into new namespace: " << strerror(errno) << std::endl; + *success = false; + return; + } + + func(); + *success = true; +} + +// Enter namespace using PID +bool JSRuntimeContainer::nsEnterWithPid(pid_t pid, int nsType, const std::function& func) +{ + const char* nsName; + + switch (nsType) + { + case CLONE_NEWIPC: + nsName = "ipc"; + break; + case CLONE_NEWNET: + nsName = "net"; + break; + case CLONE_NEWNS: + nsName = "mnt"; + break; + case CLONE_NEWPID: + nsName = "pid"; + break; + case CLONE_NEWUSER: + case CLONE_NEWUTS: + std::cerr << "Unsupported namespace type: " << nsType << std::endl; + return false; + default: + std::cerr << "Invalid namespace type: " << nsType << std::endl; + return false; + } + + char nsPath[64]; + snprintf(nsPath, sizeof(nsPath), "/proc/%d/ns/%s", pid, nsName); + + int newNsFd = open(nsPath, O_RDONLY | O_CLOEXEC); + if (newNsFd < 0) + { + std::cerr << "Failed to open container namespace: " << nsPath << " - " << strerror(errno) << std::endl; + return false; + } + + bool success = false; + + std::thread thread([=, &success, &func]() { + nsThread(newNsFd, nsType, &success, func); + }); + thread.join(); + + close(newNsFd); + + return success; +} + +// Find a PID inside the container +pid_t JSRuntimeContainer::findContainerPid(const std::string& containerId) +{ + std::string cgroupPath = "/sys/fs/cgroup/memory/" + containerId + "/cgroup.procs"; + + std::ifstream file(cgroupPath); + if (!file.is_open()) + { + return -1; + } + + std::string line; + if (!std::getline(file, line) || line.empty()) + { + return -1; + } + + long pid = std::strtol(line.c_str(), nullptr, 10); + if (pid >= INT32_MAX || pid <= 0) + { + return -1; + } + + return static_cast(pid); +} + +bool JSRuntimeContainer::nsEnterImpl(const std::string& containerId, Namespace type, const std::function& func) +{ + pid_t containerPid = findContainerPid(containerId); + if (containerPid <= 0) + { + return false; + } + + switch (type) + { + case NetworkNamespace: + return nsEnterWithPid(containerPid, CLONE_NEWNET, func); + case MountNamespace: + return nsEnterWithPid(containerPid, CLONE_NEWNS, func); + case IpcNamespace: + return nsEnterWithPid(containerPid, CLONE_NEWIPC, func); + default: + std::cerr << "Unknown namespace type" << std::endl; + return false; + } +} + +bool JSRuntimeContainer::nsEnter(const std::string& containerId, Namespace type, const std::function& func) +{ + return nsEnterImpl(containerId, type, func); +} + +std::string JSRuntimeContainer::getContainerIpAddress(const std::string& containerId) +{ + std::string ipAddress = ""; + + auto getIpAddress = [&ipAddress]() { + int sock = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sock < 0) + { + return; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "eth0"); + + if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) + { + close(sock); + return; + } + + close(sock); + + struct sockaddr_in* ifaceAddr = reinterpret_cast(&ifr.ifr_addr); + char* ip = inet_ntoa(ifaceAddr->sin_addr); + if (ip != nullptr) + { + ipAddress = std::string(ip); + } + }; + + if (nsEnterImpl(containerId, NetworkNamespace, getIpAddress) && !ipAddress.empty()) + { + return ipAddress; + } + + return ""; +} + +bool JSRuntimeContainer::isContainer(const std::string& containerId) +{ + std::string cgroupPath = "/sys/fs/cgroup/memory/" + containerId + "/cgroup.procs"; + return (access(cgroupPath.c_str(), F_OK) == 0); +} + +bool JSRuntimeContainer::connectAndSend(const std::string& ip, const std::string& message) +{ +#ifdef USE_WEBSOCKET_MOCK + // In mock builds, just return true + return true; +#else + websocketpp::lib::error_code ec; + SimpleClient c; + c.clear_access_channels(websocketpp::log::alevel::all); + c.clear_error_channels(websocketpp::log::elevel::all); + c.init_asio(); + + std::string uri = std::string("ws://") + ip + std::string(":") + std::to_string(WS_SERVER_PORT); + SimpleClient::connection_ptr con = c.get_connection(uri, ec); + if (ec) { + return false; + } + + websocketpp::connection_hdl hdl = con->get_handle(); + c.connect(con); + + std::thread t(&SimpleClient::run, &c); + + // Wait briefly for open + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + try { + c.send(hdl, message, websocketpp::frame::opcode::text); + } catch (...) {} + + c.close(hdl, websocketpp::close::status::going_away, "bye", ec); + t.join(); + return true; +#endif +} + +// Build launchApplication JSON payload for a given URL and options +std::string JSRuntimeContainer::buildLaunchMessage(const std::string &url, const std::string& options) +{ + std::ostringstream oss; + oss << "{\"method\": \"launchApplication\", \"params\":{\"url\":\"" << url << "\",\"moduleSettings\":\"" << options << "\"}}"; + std::string message = oss.str(); + return message; +} + +std::string JSRuntimeContainer::parseAppConfig(const std::string& configPath) +{ + + std::string optionsStr; + std::ifstream fd(configPath); + if (!fd.is_open()) { + return optionsStr; + } + + std::stringstream buffer; + buffer << fd.rdbuf(); + std::string configData = buffer.str(); + fd.close(); + + Document configDoc; + if (configDoc.Parse(configData.c_str()).HasParseError()) { + return optionsStr; + } + + if (!configDoc.IsObject()) { + return optionsStr; + } + + if (configDoc.HasMember("features") && configDoc["features"].IsArray()) + { + const Value& flagsarr = configDoc["features"]; + for (SizeType i = 0; i < flagsarr.Size(); i++) { + const Value& flagOption = flagsarr[i]; + if (!flagOption.IsObject()) + continue; + + if (flagOption.HasMember("name") && flagOption.HasMember("enable")) { + const Value& name = flagOption["name"]; + const Value& enable = flagOption["enable"]; + + if (enable == true) { + std::string featureName = name.GetString(); + std::cout << "[DEBUG] parseAppConfig - processing enabled feature: " << featureName << std::endl; + + if (name == "player") + optionsStr += "player,"; + else if (name == "xhr") + optionsStr += "xhr,"; + else if (name == "websocket") + optionsStr += "ws,"; + else if (name == "http") + optionsStr += "http,"; + else if (name == "websocketenhanced") + optionsStr += "wsenhanced,"; + else if (name == "fetch") + optionsStr += "fetch,"; + else if (name == "jsdom") + optionsStr += "jsdom,"; + else if (name == "window") + optionsStr += "window,"; + + } + } + } + } + + if (!optionsStr.empty() && optionsStr.back() == ',') + optionsStr.pop_back(); + + return optionsStr; +} + + diff --git a/src/JSRuntimeServer.cpp b/src/JSRuntimeServer.cpp index 340a070..c9a827d 100644 --- a/src/JSRuntimeServer.cpp +++ b/src/JSRuntimeServer.cpp @@ -114,12 +114,15 @@ JSRuntimeServer::JSRuntimeServer() : mServerPort(0) { } -void JSRuntimeServer::initialize(int serverport, std::shared_ptr renderer) +void JSRuntimeServer::initialize(int serverport, std::shared_ptr renderer, std::shared_ptr externalHandler) { - NativeJSLogger::log(INFO, "Enter: %s\n", __func__); + NativeJSLogger::log(INFO, "JSRuntimeServer::initialize - port=%d, externalHandler=%p\n", serverport, externalHandler.get()); mServerPort = serverport; mRenderer = renderer; + mExternalHandler = externalHandler; + + NativeJSLogger::log(INFO, "JSRuntimeServer initialized - mExternalHandler=%p\n", mExternalHandler.get()); } bool JSRuntimeServer::start() @@ -202,7 +205,42 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg { break; } - if (method == "launchApplication") + + if (method == "runExternalApplication") + { + JsonWrap jParams(jRoot, "params"); + if (jParams.get() == nullptr) + { + result = "error: missing params"; + break; + } + + uint32_t id = jParams.getUint32("id", error); + if (error) + { + result = "error: invalid or missing id"; + break; + } + + std::string url = jParams.getString("url", error); + if (error) + { + result = "error: invalid or missing url"; + break; + } + + if (!mExternalHandler) + { + result = "error: external handler not available"; + NativeJSLogger::log(ERROR, "External handler not initialized\n"); + break; + } + + mExternalHandler->runExternalApplication(url, id); + result = "ok"; + NativeJSLogger::log(INFO, "Queued external application: id=%d, url=%s\n", id, url.c_str()); + } + else if (method == "launchApplication") { JsonWrap jParams(jRoot, "params"); if (jParams.get() == nullptr) @@ -217,13 +255,27 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg std::string options = jParams.getString("moduleSettings", error); ModuleSettings moduleSettings; moduleSettings.fromString(options); - uint32_t id = mRenderer->createApplication(moduleSettings); - mRenderer->runApplication(id, url); + uint32_t id = mRenderer->createApplication(moduleSettings); + + NativeJSLogger::log(INFO, "launchApplication: checking URL=%s for HTML extension\n", url.c_str()); + if (url.find(".html") != std::string::npos || url.find(".htm") != std::string::npos) + { + NativeJSLogger::log(INFO, "Detected HTML file, mExternalHandler=%p\n", mExternalHandler.get()); + if (mExternalHandler) + { + mExternalHandler->runExternalApplication(url, id); + } + } + else + { + mRenderer->runApplication(id, url); + } + std::ostringstream oss; - oss<< "ID : " << id; - result = oss.str(); + oss<< "ID : " << id; + result = oss.str(); } - if (method == "createApplication") + else if (method == "createApplication") { JsonWrap jParams(jRoot, "params"); if (jParams.get() == nullptr) @@ -242,7 +294,7 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg oss<< "ID : " << id; result = oss.str(); } - if (method == "runApplication") + else if (method == "runApplication") { JsonWrap jParams(jRoot, "params"); if (jParams.get() == nullptr) diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index 2198a53..fdbf989 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -122,7 +122,7 @@ int main(int argc, char* argv[]) if (runServer == true) { JSRuntimeServer *server = JSRuntimeServer::getInstance(); - server->initialize(WS_SERVER_PORT, renderer); + server->initialize(WS_SERVER_PORT, renderer, nullptr); server->start(); } #endif From 7e7e35d0d5180306adc7372707db159330d46eb9 Mon Sep 17 00:00:00 2001 From: Vinod Jain <98183059+vjain008@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:07:59 -0600 Subject: [PATCH 03/10] Update src/JSRuntimeServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/JSRuntimeServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSRuntimeServer.cpp b/src/JSRuntimeServer.cpp index c9a827d..670afcb 100644 --- a/src/JSRuntimeServer.cpp +++ b/src/JSRuntimeServer.cpp @@ -270,7 +270,6 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg { mRenderer->runApplication(id, url); } - std::ostringstream oss; oss<< "ID : " << id; result = oss.str(); From ff8f3e911f4684e54c5b9b0ba71ae0efa4de9700 Mon Sep 17 00:00:00 2001 From: Vinod Jain <98183059+vjain008@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:12:12 -0600 Subject: [PATCH 04/10] Update src/JSRuntimeClientContainer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/JSRuntimeClientContainer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JSRuntimeClientContainer.cpp b/src/JSRuntimeClientContainer.cpp index f68f96e..8382be2 100644 --- a/src/JSRuntimeClientContainer.cpp +++ b/src/JSRuntimeClientContainer.cpp @@ -12,8 +12,8 @@ int main() std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId); if (ipAddress.empty()) { - NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container"); - return 1; + NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container"); + return 1; } for (const auto &app : apps) { From 62116afbefdf5b9b036f2d0894c47c763990e175 Mon Sep 17 00:00:00 2001 From: Vinod Jain <98183059+vjain008@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:17:24 -0600 Subject: [PATCH 05/10] Update src/JSRuntimeServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/JSRuntimeServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSRuntimeServer.cpp b/src/JSRuntimeServer.cpp index 670afcb..63a5fd3 100644 --- a/src/JSRuntimeServer.cpp +++ b/src/JSRuntimeServer.cpp @@ -205,7 +205,6 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg { break; } - if (method == "runExternalApplication") { JsonWrap jParams(jRoot, "params"); From 3a565cabb3ebc649cac333ab2993915dffdef3e5 Mon Sep 17 00:00:00 2001 From: Vinod Jain <98183059+vjain008@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:19:35 -0600 Subject: [PATCH 06/10] Update src/JSRuntimeServer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/JSRuntimeServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSRuntimeServer.cpp b/src/JSRuntimeServer.cpp index 63a5fd3..342f386 100644 --- a/src/JSRuntimeServer.cpp +++ b/src/JSRuntimeServer.cpp @@ -213,7 +213,6 @@ void JSRuntimeServer::onMessage(websocketpp::connection_hdl hdl, message_ptr msg result = "error: missing params"; break; } - uint32_t id = jParams.getUint32("id", error); if (error) { From de7bbf39545d91ca8b0fd7c3fca8b9cac9f44eac Mon Sep 17 00:00:00 2001 From: Vinod Kumar Jain Date: Wed, 10 Dec 2025 23:33:37 -0600 Subject: [PATCH 07/10] 2.0 release changelog updates --- CHANGELOG.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 755b629..1f544e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,25 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [2.0](https://github.com/rdkcentral/rdkNativeScript/compare/1.0.10...2.0) + +- RDKEMW-9355: Add Support to run app widgets in different contexts wit… [`#90`](https://github.com/rdkcentral/rdkNativeScript/pull/90) +- RDKEMW-8844: Analyse and Improve L1 coverage [`#79`](https://github.com/rdkcentral/rdkNativeScript/pull/79) +- RDKEMW-9785: Increasing Test Coverage for L1 test [`#95`](https://github.com/rdkcentral/rdkNativeScript/pull/95) +- RDKEMW-11265: Migrate L1 and native build workflow to rdk-e [`#93`](https://github.com/rdkcentral/rdkNativeScript/pull/93) +- RDKEMW-9172: L2testcase for rdkNativeScript [`#91`](https://github.com/rdkcentral/rdkNativeScript/pull/91) +- RDKEMW-9461:Nativescript Minimal build for Coverity [`#84`](https://github.com/rdkcentral/rdkNativeScript/pull/84) +- RDKEMW-9355: Add Support to run app widgets in different contexts within a single process [`f2fbbe9`](https://github.com/rdkcentral/rdkNativeScript/commit/f2fbbe9eec146be3b91a8aa550dddb26ff43d2ff) +- RDKEMW-8844: Improve L1 coverage [`605e0ad`](https://github.com/rdkcentral/rdkNativeScript/commit/605e0ad2b4a9241aeceb5311ac4eaa53dd7b17fd) +- Update src/linux/KeyInput.cpp [`3e0f65c`](https://github.com/rdkcentral/rdkNativeScript/commit/3e0f65cf6a9d560b26e5a0ee6c26350d48046ded) + #### [1.0.10](https://github.com/rdkcentral/rdkNativeScript/compare/1.0.9...1.0.10) -- RDKEMW-9765 : [BUG_FIX]UserAgent string need to be aligned with browser for diff… [`#77`](https://github.com/rdkcentral/rdkNativeScript/pull/77) -- RDKEMW-9765 : [BUG_FIX]UserAgent string need to be aligned with browser for different agents [`881b332`](https://github.com/rdkcentral/rdkNativeScript/commit/881b332528ae114f0a9c2f767c8a1436154d380c) +> 31 October 2025 + +- RDKEMW-9765 : UserAgent string need to be aligned with browser for diff… [`#77`](https://github.com/rdkcentral/rdkNativeScript/pull/77) +- RDKEMW-9765 : UserAgent string need to be aligned with browser for different agents [`881b332`](https://github.com/rdkcentral/rdkNativeScript/commit/881b332528ae114f0a9c2f767c8a1436154d380c) +- 1.0.10 release changelog updates [`6233823`](https://github.com/rdkcentral/rdkNativeScript/commit/62338232511edac45b4aa7c1a0c1622909c29098) - Merge tag '1.0.9' into develop [`b60efc0`](https://github.com/rdkcentral/rdkNativeScript/commit/b60efc01bf49975196c7a55aa3e6736af753916b) #### [1.0.9](https://github.com/rdkcentral/rdkNativeScript/compare/1.0.8...1.0.9) @@ -32,13 +47,11 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). > 30 September 2025 -- DELIA-68967 : Switching between Xumo Fast Channels and Vipa activated… [`#68`](https://github.com/rdkcentral/rdkNativeScript/pull/68) - RDKEMW-5610 : L1 test cases for jsruntime [`#66`](https://github.com/rdkcentral/rdkNativeScript/pull/66) - Deploy fossid_integration_stateless_diffscan_target_repo action [`#67`](https://github.com/rdkcentral/rdkNativeScript/pull/67) - Deploy cla action [`#40`](https://github.com/rdkcentral/rdkNativeScript/pull/40) - Update CODEOWNERS [`#63`](https://github.com/rdkcentral/rdkNativeScript/pull/63) - 1.0.7 release changelog updates [`0f3be4f`](https://github.com/rdkcentral/rdkNativeScript/commit/0f3be4fecad9213a4d86c828e978c29199e5d190) -- DELIA-68967 : Switching between Xumo Fast Channels and Vipa activated Channels [`228cf8b`](https://github.com/rdkcentral/rdkNativeScript/commit/228cf8bfdde6d7f7d991805193038b392bc0e89b) - Merge tag '1.0.6' into develop [`afdf341`](https://github.com/rdkcentral/rdkNativeScript/commit/afdf341c4f81e7019d76f207c7b428dbd0ffc00d) #### [1.0.6](https://github.com/rdkcentral/rdkNativeScript/compare/1.0.5...1.0.6) @@ -115,6 +128,4 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - RDKEMW-2469 : Merging latest changes of rdknativescript [`#4`](https://github.com/rdkcentral/rdkNativeScript/pull/4) - RDKEMW-2469 : Merging latest changes of javascriptcore and rdknativescript for rdke build [`#3`](https://github.com/rdkcentral/rdkNativeScript/pull/3) - Bring latest changes till 2024 [`#2`](https://github.com/rdkcentral/rdkNativeScript/pull/2) -- RDK-56154 Merging all rdknativescript latest changes to rdkcentral github from comcast rdke github [`2542b7c`](https://github.com/rdkcentral/rdkNativeScript/commit/2542b7cac0c0c0dd1aef7d8b158e5dd63b56347e) - Import of Comcast source (develop) [`dfc9b89`](https://github.com/rdkcentral/rdkNativeScript/commit/dfc9b89df42fb0b844e04624b13808333afb19ce) -- RDK-56154: Merging all rdknativescript latest changes [`0d8189e`](https://github.com/rdkcentral/rdkNativeScript/commit/0d8189e76bdfa9f2cf619af300d95e5ec8f4def9) From 0c505e10bf31bdbe12fc3fbaae44de447f333fad Mon Sep 17 00:00:00 2001 From: gsarng517_comcast Date: Mon, 15 Dec 2025 16:04:23 +0530 Subject: [PATCH 08/10] RDKEMW-11507: Viper IPA not working with rdknative widget Reason for change: Fixing undefined errors in during VIPA playback Test Procedure: VIPA JS version should launch using this widget. Risks: low Priority: P2 --- CMakeLists.txt | 3 +- src/jsc/modules/windowwrapper.js | 76 +++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de08db5..35ffbc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,6 +141,7 @@ set(JSRUNTIME_CONTAINER_FILES ) set(JSRUNTIME_FILES ${JSRUNTIME_COMMON_SOURCE_DIRECTORY}/JSRuntimeContainer.cpp + ${JSRUNTIME_COMMON_SOURCE_DIRECTORY}/NativeJSLogger.cpp ) set(JSRUNTIME_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/${JSRUNTIME_ENGINE_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include/linux ${CMAKE_CURRENT_SOURCE_DIR}/src/jsc/jsc_lib ${JSRUNTIME_ENGINE_INCLUDE_DIRECTORIES}) @@ -212,7 +213,7 @@ if (BUILD_JSRUNTIME_CONTAINER) add_dependencies(jsruntime_container ${JSRUNTIMECONTAINER_LIBRARY_NAME}) target_include_directories(jsruntime_container PRIVATE ${JSRUNTIME_INCLUDE_DIRECTORIES}) set_target_properties(jsruntime_container PROPERTIES OUTPUT_NAME "JSRuntimeContainer") - target_link_libraries(jsruntime_container ${JSRUNTIME_LIBRARY_LINK_DIRECTORIES} -l${JSRUNTIMECONTAINER_LIBRARY_NAME} -lpthread) + target_link_libraries(jsruntime_container ${JSRUNTIME_LIBRARY_LINK_DIRECTORIES} -l${JSRUNTIMECONTAINER_LIBRARY_NAME} ${JSRUNTIME_LINK_ETHANLIB} -lpthread) endif (BUILD_JSRUNTIME_CONTAINER) diff --git a/src/jsc/modules/windowwrapper.js b/src/jsc/modules/windowwrapper.js index dd87e0a..f281c4c 100755 --- a/src/jsc/modules/windowwrapper.js +++ b/src/jsc/modules/windowwrapper.js @@ -23,7 +23,7 @@ try { window = jsdom.window; window.location = {"href":"", "host":"127.0.0.1", "protocol":"http"} - /* + window.frames = [] window.screen = { "width":1920, @@ -32,7 +32,79 @@ try "availHeight":1080 } screen = window.screen; - */ + + var BlobPolyfill = function(parts, options) + { + parts = parts || []; + options = options || {}; + this.size = 0; + this.type = options.type || ''; + this._parts = parts; + + for (var i = 0; i < parts.length; i++) + { + if (typeof parts[i] === 'string') + { + this.size += parts[i].length; + } + else if (parts[i] && parts[i].byteLength) + { + this.size += parts[i].byteLength; + } + } + }; + + BlobPolyfill.prototype.slice = function(start, end, contentType) + { + return new BlobPolyfill(this._parts, { type: contentType || this.type }); + }; + + BlobPolyfill.prototype.text = function() + { + var text = ''; + for (var i = 0; i < this._parts.length; i++) + { + if (typeof this._parts[i] === 'string') + { + text += this._parts[i]; + } + } + return Promise.resolve(text); + }; + + BlobPolyfill.prototype.arrayBuffer = function() + { + return Promise.resolve(new ArrayBuffer(0)); + }; + + if (typeof global.Blob === 'undefined') { + global.Blob = BlobPolyfill; + } + if (typeof window !== 'undefined' && typeof window.Blob === 'undefined') { + window.Blob = BlobPolyfill; + } + if (typeof self !== 'undefined' && typeof self.Blob === 'undefined') { + self.Blob = BlobPolyfill; + } + if (typeof this !== 'undefined' && typeof this.Blob === 'undefined') { + this.Blob = BlobPolyfill; + } + if (typeof window !== 'undefined') { + + if (!window.top) window.top = window; + if (!window.parent) window.parent = window; + + if (!window.__tcfapi) { + window.__tcfapi = function(cmd, ver, callback) { + if (callback) callback({gdprApplies: false}, true); + }; + } + if (!window.__uspapi) { + window.__uspapi = function(cmd, ver, callback) { + if (callback) callback({uspString: '1---'}, true); + }; + } + } } } catch(err) From 20c90f07d7bf12362d3919e56de637d171186449 Mon Sep 17 00:00:00 2001 From: Vinod Kumar Jain Date: Mon, 15 Dec 2025 13:58:46 -0600 Subject: [PATCH 09/10] 2.0.1 release changelog updates --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f544e1..61928a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [2.0.1](https://github.com/rdkcentral/rdkNativeScript/compare/2.0...2.0.1) + +- RDKEMW-11507: Viper IPA not working with rdknative widget [`#100`](https://github.com/rdkcentral/rdkNativeScript/pull/100) +- Merge tag '2.0' into develop [`51a0410`](https://github.com/rdkcentral/rdkNativeScript/commit/51a04109cb17643687722ed8b96da89929791cfb) + #### [2.0](https://github.com/rdkcentral/rdkNativeScript/compare/1.0.10...2.0) - RDKEMW-9355: Add Support to run app widgets in different contexts wit… [`#90`](https://github.com/rdkcentral/rdkNativeScript/pull/90) From 9fe6e58b4bca0c57a4b0f0bd1247b8cf1f97acc1 Mon Sep 17 00:00:00 2001 From: tboova469_comcast Date: Mon, 8 Dec 2025 17:43:54 +0530 Subject: [PATCH 10/10] test-L1: Do not merge Reason for change: For testing purpose only --- src/jsruntime.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index fdbf989..9824b49 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -17,6 +17,7 @@ * limitations under the License. **/ +//Test pull-request #include #include #if defined(ENABLE_JSRUNTIME_SERVER)