From f09297bda7e93926527929feaee0102ec1e3a87b Mon Sep 17 00:00:00 2001 From: trupthi1403 Date: Fri, 29 Aug 2025 17:11:08 +0530 Subject: [PATCH] RDKEMW-7489: Adding Href support Reason for change: Added Support for Random Num Generator, Local Storage class, Updated node-fetch file for header support Risks: Low Priority: P1 --- include/IExternalApplicationHandler.h | 30 ++++++ include/JavaScriptContextBase.h | 4 + include/NativeJSRenderer.h | 36 +++---- include/jsc/JavaScriptContext.h | 14 +-- include/jsc/JavaScriptUtils.h | 2 + src/JavaScriptContextBase.cpp | 16 ++- src/NativeJSRenderer.cpp | 106 ++++++++++--------- src/jsc/JavaScriptContext.cpp | 90 +++++++++------- src/jsc/JavaScriptUtils.cpp | 89 ++++++++++++---- src/jsc/modules/lib/URL.js | 4 + src/jsc/modules/lib/url-state-machine.js | 8 +- src/jsc/modules/linkedjsdomwrapper.js | 28 +++++ src/jsc/modules/node-fetch.js | 127 ++++++++++++++--------- src/jsc/modules/url.js | 4 +- src/jsc/modules/windowwrapper.js | 33 ++++++ 15 files changed, 401 insertions(+), 190 deletions(-) create mode 100644 include/IExternalApplicationHandler.h diff --git a/include/IExternalApplicationHandler.h b/include/IExternalApplicationHandler.h new file mode 100644 index 0000000..3ab7619 --- /dev/null +++ b/include/IExternalApplicationHandler.h @@ -0,0 +1,30 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* 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. +**/ + +#ifndef IEXTERNALAPPLICATION_HANDLER_H +#define IEXTERNALAPPLICATION_HANDLER_H + +#include +class IExternalApplicationHandler +{ + public: + virtual void runExternalApplication(std::string url, uint32_t id) = 0; +}; + +#endif diff --git a/include/JavaScriptContextBase.h b/include/JavaScriptContextBase.h index a1e573d..6a57d25 100644 --- a/include/JavaScriptContextBase.h +++ b/include/JavaScriptContextBase.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include //#include @@ -57,6 +59,7 @@ class JavaScriptContextBase:public IJavaScriptContext, public JavaScriptKeyListe virtual void onKeyPress(struct JavaScriptKeyDetails& details); virtual void onKeyRelease(struct JavaScriptKeyDetails& details); ModuleSettings getModuleSettings(); + void setExternalApplicationHandler(std::shared_ptr handler); protected: virtual void processKeyEvent(struct JavaScriptKeyDetails& details, bool keyPress) = 0; virtual bool evaluateScript(const char* script, const char* name, const char *args, bool module=false) = 0; @@ -70,6 +73,7 @@ class JavaScriptContextBase:public IJavaScriptContext, public JavaScriptKeyListe bool mEmbedWebBridge; bool mEnableWebSockerServer; ModuleSettings mModuleSettings; + std::shared_ptr mExternalApplicationHandler; static std::string sModulesPath; static void populateModulesPath(); }; diff --git a/include/NativeJSRenderer.h b/include/NativeJSRenderer.h index 91816ca..e4d9fdc 100644 --- a/include/NativeJSRenderer.h +++ b/include/NativeJSRenderer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -76,19 +77,19 @@ namespace JsRuntime { IJavaScriptContext* consoleContext = nullptr; ModuleSettings moduleSettings{}; }; - + struct ApplicationDetails{ uint32_t id; std::string url; }; - + enum RequestType{ CREATE=0, RUN, TERMINATE, RUNSCRIPT }; - + struct ApplicationRequest { ApplicationRequest(uint32_t id, RequestType requestType, std::string url="", bool enableHttp=false, bool enableXHR=false, bool enableWebSocket=false, bool enableWebSocketEnhanced=false, bool enableFetch=false, bool enableJSDOM=false, bool enableWindow=false, bool enablePlayer=false): mId(id), mRequestType(requestType), mUrl(url), mEnableHttp(enableHttp), mEnableXHR(enableXHR), mEnableWebSocket(enableWebSocket), mEnableWebSocketEnhanced(enableWebSocketEnhanced), mEnableFetch(enableFetch), mEnableJSDOM(enableJSDOM), mEnableWindow(enableWindow), mEnablePlayer(enablePlayer) @@ -105,7 +106,7 @@ namespace JsRuntime { bool mEnableJSDOM; bool mEnableWindow; bool mEnablePlayer; - }; + }; struct ApplicationData{ std::string url; IJavaScriptContext* context; @@ -121,19 +122,20 @@ namespace JsRuntime { void run(); void setEnvForConsoleMode(ModuleSettings& moduleSettings); bool runApplication(uint32_t id, std::string url); - bool runJavaScript(uint32_t id, std::string code); - uint32_t createApplication(ModuleSettings& moduleSettings) ; + bool runJavaScript(uint32_t id, std::string code); + uint32_t createApplication(ModuleSettings& moduleSettings) ; bool terminateApplication(uint32_t id); - std::list getApplications(); - private: - bool downloadFile(std::string& url, MemoryStruct& chunk); + std::list getApplications(); + void setExternalApplicationHandler(std::shared_ptr handler); + private: + bool downloadFile(std::string& url, MemoryStruct& chunk); void processDevConsoleRequests(); void runDeveloperConsole(ModuleSettings moduleSettings); void createApplicationInternal(ApplicationRequest& appRequest); void runApplicationInternal(ApplicationRequest& appRequest); void terminateApplicationInternal(ApplicationRequest& appRequest); - void runJavaScriptInternal(ApplicationRequest& appRequest); - uint32_t createApplicationIdentifier(); + void runJavaScriptInternal(ApplicationRequest& appRequest); + uint32_t createApplicationIdentifier(); static size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream); IJavaScriptEngine* mEngine; bool mRunning; @@ -145,13 +147,9 @@ namespace JsRuntime { bool mEnableWebSocketServer; bool mEssosInitialized; bool mConsoleMode; - std::mutex mUserMutex; + std::mutex mUserMutex; std::map mContextMap; - std::vector gPendingRequests; - }; + std::vector gPendingRequests; + std::shared_ptr mExternalApplicationHandler; + }; }; - - - - - diff --git a/include/jsc/JavaScriptContext.h b/include/jsc/JavaScriptContext.h index 86559b4..e679b71 100644 --- a/include/jsc/JavaScriptContext.h +++ b/include/jsc/JavaScriptContext.h @@ -36,7 +36,6 @@ #include "rtScriptJSCPrivate.h" #include #include - #include #include #ifdef ENABLE_JSRUNTIME_PLAYER @@ -58,11 +57,11 @@ struct AAMPJSBindings extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef); -struct PerformanceMetrics { +struct PerformanceMetrics { double createApplicationStartTime=0.0; double createApplicationEndTime=0.0; double executionStartTime=0.0; - double executionEndTime=0.0; + double executionEndTime=0.0; double playbackStartTime=0.0; }; @@ -75,12 +74,12 @@ class JavaScriptContext: public JavaScriptContextBase, public NetworkMetricsList JavaScriptContext(JavaScriptContextFeatures& features, std::string url, IJavaScriptEngine* jsEngine); virtual ~JavaScriptContext(); - + rtValue get(const char *name); rtError add(const char *name, rtValue const& val); bool has(const char *name); JSGlobalContextRef getContext() { return mContext; } - + virtual void onMetricsData (NetworkMetrics *net) override; rtMapObject* getNetworkMetricsData() const { return mNetworkMetricsData; } void dumpNetworkMetricData(NetworkMetrics *metrics, std::string appUrl); @@ -91,6 +90,8 @@ class JavaScriptContext: public JavaScriptContextBase, public NetworkMetricsList void setAppdata(uint32_t id, const std::string& url); double getExecutionDuration() const; + void handleExternalApplication(const std::string& url); + private: bool evaluateScript(const char *script, const char *name, const char *args = nullptr, bool module = false); void processKeyEvent(struct JavaScriptKeyDetails& details, bool keyPress); @@ -117,6 +118,7 @@ class JavaScriptContext: public JavaScriptContextBase, public NetworkMetricsList rtRef m_readBinaryBinding; rtRef m_setVideoStartTimeBinding; rtRef m_JSRuntimeDownloadMetrics; - + rtRef m_setExternalAppHandlerBinding; + rtRef m_getRandomValuesBinding; }; #endif diff --git a/include/jsc/JavaScriptUtils.h b/include/jsc/JavaScriptUtils.h index 622dda8..9eb6747 100644 --- a/include/jsc/JavaScriptUtils.h +++ b/include/jsc/JavaScriptUtils.h @@ -71,6 +71,8 @@ rtError rtHttpGetBinding(int numArgs, const rtValue* args, rtValue* result, void rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, void* context); rtError rtSetVideoStartTimeBinding(int numArgs, const rtValue* args, rtValue* result, void* context); rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* result, void* context); +rtError rtSetExternalAppHandlerBinding(int numArgs, const rtValue* args, rtValue* result, void* context); +rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* result, void* context); JSValueRef requireCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception); #endif /* JAVASCRIPTMISC_H */ diff --git a/src/JavaScriptContextBase.cpp b/src/JavaScriptContextBase.cpp index 88ea331..086b7c9 100644 --- a/src/JavaScriptContextBase.cpp +++ b/src/JavaScriptContextBase.cpp @@ -25,12 +25,12 @@ #ifdef ENABLE_ESSOS #include #endif -#include +#include #include std::string JavaScriptContextBase::sThunderJSCode = ""; std::string JavaScriptContextBase::sWebBridgeCode = ""; -std::string JavaScriptContextBase::sModulesPath = ""; +std::string JavaScriptContextBase::sModulesPath = ""; JavaScriptContextFeatures::JavaScriptContextFeatures(bool embedThunderJS, bool embedWebBridge, bool enableWebSockerServer, ModuleSettings& moduleSettings):mEmbedThunderJS(embedThunderJS), mEmbedWebBridge(embedWebBridge), mEnableWebSockerServer(enableWebSockerServer), mModuleSettings(moduleSettings) { @@ -125,7 +125,7 @@ bool JavaScriptContextBase::runScript(const char *script, bool isModule, std::st } std::string JavaScriptContextBase::getUrl() -{ +{ return mApplicationUrl; } @@ -146,10 +146,11 @@ void JavaScriptContextBase::onKeyRelease(struct JavaScriptKeyDetails& details) ModuleSettings JavaScriptContextBase::getModuleSettings() { - return mModuleSettings; + return mModuleSettings; } -void JavaScriptContextBase::populateModulesPath(){ +void JavaScriptContextBase::populateModulesPath() +{ if(getenv("JSRUNTIME_MODULES_PATH")) { std::cout<<"JSRUNTIME_MODULES_PATH variable is set"< handler) +{ + mExternalApplicationHandler = handler; +} diff --git a/src/NativeJSRenderer.cpp b/src/NativeJSRenderer.cpp index a646360..5e7df7e 100644 --- a/src/NativeJSRenderer.cpp +++ b/src/NativeJSRenderer.cpp @@ -119,15 +119,15 @@ NativeJSRenderer::NativeJSRenderer(std::string waylandDisplay): mEngine(nullptr) if (nullptr != testfile) { mTestFileName = testfile; - } + } char* testfiledomsupport = getenv("NATIVEJS_ENABLE_TEST_FILE_DOMSUPPORT"); if (nullptr != testfiledomsupport) { if (strcmp(testfiledomsupport, "1") == 0) - { + { mEnableTestFileDOMSupport = true; } - } + } char* embedThunderJS = getenv("NATIVEJS_EMBED_THUNDERJS"); if (embedThunderJS) { @@ -141,15 +141,15 @@ NativeJSRenderer::NativeJSRenderer(std::string waylandDisplay): mEngine(nullptr) { mEmbedThunderJS = true; NativeJSLogger::log(INFO, "ThunderJS enabled via file presence\n"); - } - } + } + } std::ifstream f(NATIVEJS_EMBED_WEBBRIDGE); if (f.good()) { mEmbedRdkWebBridge = true; NativeJSLogger::log(INFO, "rdk WebBridge enabled via file presence\n"); - } + } char* enableWebSocketServer = getenv("NATIVEJS_ENABLE_WEBSOCKET_SERVER"); if (enableWebSocketServer) { @@ -163,15 +163,15 @@ NativeJSRenderer::NativeJSRenderer(std::string waylandDisplay): mEngine(nullptr) { mEnableWebSocketServer = true; NativeJSLogger::log(INFO, "WebSocket server enabled via file presence\n"); - } - } + } + } } NativeJSRenderer::~NativeJSRenderer() { - gPendingRequests.clear(); + gPendingRequests.clear(); if (mEngine) - { + { delete mEngine; mEngine = nullptr; } @@ -258,7 +258,7 @@ std::list NativeJSRenderer::getApplications() } mUserMutex.unlock(); return runningApplication; - + } bool NativeJSRenderer::terminateApplication(uint32_t id) @@ -272,7 +272,7 @@ bool NativeJSRenderer::terminateApplication(uint32_t id) void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest) { - double startTime = getTimeInMilliSec(); + double startTime = getTimeInMilliSec(); ModuleSettings settings; settings.enableHttp = appRequest.mEnableHttp; @@ -282,9 +282,9 @@ void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest) settings.enableFetch = appRequest.mEnableFetch; settings.enableJSDOM = appRequest.mEnableJSDOM; settings.enableWindow = appRequest.mEnableWindow; - settings.enablePlayer = appRequest.mEnablePlayer; + settings.enablePlayer = appRequest.mEnablePlayer; uint32_t id= appRequest.mId; - + JavaScriptContextFeatures features(mEmbedThunderJS, mEmbedRdkWebBridge, mEnableWebSocketServer, settings); JavaScriptContext* context = new JavaScriptContext(features, "" , mEngine); if(NULL == context) @@ -293,26 +293,29 @@ void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest) return ; } NativeJSLogger::log(DEBUG, "Context created for ID: %d\n", id); - + if (mExternalApplicationHandler) { + context->setExternalApplicationHandler(mExternalApplicationHandler); + } + double endTime = getTimeInMilliSec(); context->setCreateApplicationStartTime(startTime); context->setCreateApplicationEndTime(endTime, id); mContextMap[id].context=context; - mUserMutex.unlock(); + mUserMutex.unlock(); } void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) { uint32_t id = appRequest.mId; std::string url = appRequest.mUrl; - + if (mContextMap.find(id) == mContextMap.end()) { - return; + return; } mContextMap[id].url = url; - + if(!url.empty()) { NativeJSLogger::log(INFO, "Before launching app\n"); @@ -342,10 +345,10 @@ void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) NativeJSLogger::log(INFO, "Execution duration(runApplicationDuration) for ID %d | URL %s : %.3f ms\n", id, url.c_str(), duration); } else - { + { NativeJSLogger::log(INFO, "About to launch local app\n"); JavaScriptContext* context = (JavaScriptContext*)mContextMap[id].context; - context->setUrl(mContextMap[id].url); + context->setUrl(mContextMap[id].url); if(context->getModuleSettings().enableJSDOM) { std::stringstream window; @@ -359,7 +362,7 @@ void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) double duration = context->getExecutionDuration(); context->setAppdata(id, url); NativeJSLogger::log(INFO, "Execution duration(runApplicationDuration) for ID %d | URL %s : %.3f ms\n", id, url.c_str(), duration); - } + } } else{ NativeJSLogger::log(WARN, "nativeJS application url not proper\n"); @@ -371,19 +374,23 @@ void NativeJSRenderer::runJavaScriptInternal(ApplicationRequest& appRequest) { uint32_t id = appRequest.mId; std::string code = appRequest.mUrl; - + if (mContextMap.find(id) == mContextMap.end()) { - return; + return; } - - mContextMap[id].url = code; + + if (mContextMap[id].url.length() == 0) + { + mContextMap[id].url = "JavaScriptCode"; + } + bool isApplication = true, isModule = false; if(!code.empty()) { NativeJSLogger::log(INFO, "Running the JavaScript code\n"); JavaScriptContext* context = (JavaScriptContext*)mContextMap[id].context; std::string rawcode = code ; - bool ret = context-> runScript(rawcode.c_str(),true,"JavaScriptCode",nullptr,true); + bool ret = context->runScript(rawcode.c_str(),isModule,mContextMap[id].url,nullptr,isApplication); double duration = context->getExecutionDuration(); NativeJSLogger::log(INFO, "Execution duration(runJavaScriptDuration) for ID %d | %s : %.3f ms\n", id, code.c_str(), duration); } @@ -400,21 +407,21 @@ void NativeJSRenderer::terminateApplicationInternal(ApplicationRequest& AppReque { JavaScriptContext* context = (JavaScriptContext*)mContextMap[id].context; NativeJSLogger::log(INFO, "Terminating the application with id: %d\n", id); - if (NULL != context) { + if (NULL != context) { NativeJSLogger::log(INFO, "Deleted context\n"); delete context; - } - mContextMap.erase(mapEntry); + } + mContextMap.erase(mapEntry); NativeJSLogger::log(INFO, "Application is terminated\n"); } - + else { NativeJSLogger::log(ERROR, "Unable to find application with id: %d and url: %s\n", id, mContextMap[id].url); return ; } - - + + } size_t NativeJSRenderer::write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) @@ -433,14 +440,14 @@ void NativeJSRenderer::run() { while(mRunning) { - uint32_t id; + uint32_t id; mUserMutex.lock(); if (mConsoleMode) { processDevConsoleRequests(); - } + } for (int i=0; iupdate(); } #endif mEngine->run(); double maxSleepTime = (1000 / 40) * 1000; - usleep(maxSleepTime); + usleep(maxSleepTime); } if (mEngine) - { + { mEngine->terminate(); } } @@ -599,6 +606,11 @@ bool NativeJSRenderer::downloadFile(std::string& url, MemoryStruct& chunk) else { NativeJSLogger::log(ERROR, "Unable to perform download\n"); - } + } return ret; } + +void NativeJSRenderer::setExternalApplicationHandler(std::shared_ptr handler) +{ + mExternalApplicationHandler = handler; +} diff --git a/src/jsc/JavaScriptContext.cpp b/src/jsc/JavaScriptContext.cpp index aa3a1e9..29a8602 100644 --- a/src/jsc/JavaScriptContext.cpp +++ b/src/jsc/JavaScriptContext.cpp @@ -88,7 +88,7 @@ JavaScriptContext::JavaScriptContext(JavaScriptContextFeatures& features, std::s JavaScriptContext::~JavaScriptContext() { rtLogInfo("%s begin", __FUNCTION__); - + #ifdef ENABLE_JSRUNTIME_PLAYER if (mModuleSettings.enablePlayer) { @@ -156,7 +156,7 @@ void JavaScriptContext::loadAAMPJSBindingsLib() { NativeJSLogger::log(ERROR, "failed to load %s and error is %s\n", aampJSBindingsLib, dlerror()); } - + dlclose(jscLibHandle); } } @@ -179,7 +179,7 @@ rtError JavaScriptContext::add(const char *name, rtValue const& val) JSValueRef exception = nullptr; JSObjectSetProperty(mContext, globalObj, jsName, jsVal, kJSPropertyAttributeDontEnum, &exception); JSStringRelease(jsName); - + if (exception) { JSStringRef exceptStr = JSValueToStringCopy(mContext, exception, nullptr); @@ -195,12 +195,12 @@ rtValue JavaScriptContext::get(const char *name) { if (!name) return {}; - + JSStringRef jsName = JSStringCreateWithUTF8CString(name); JSObjectRef globalObj = JSContextGetGlobalObject(mContext); JSValueRef exception = nullptr; rtValue retVal; - + do { JSValueRef valueRef = JSObjectGetProperty(mContext, globalObj, jsName, &exception); @@ -211,7 +211,7 @@ rtValue JavaScriptContext::get(const char *name) break; } while(0); JSStringRelease(jsName); - + if (exception) { JSStringRef exceptStr = JSValueToStringCopy(mContext, exception, nullptr); @@ -220,7 +220,7 @@ rtValue JavaScriptContext::get(const char *name) rtLogError("Failed to get %s from rtScript context, error='%s'\n", name, errorStr.cString()); return rtValue(); } - + return retVal; } @@ -241,22 +241,22 @@ bool JavaScriptContext::evaluateScript(const char* script, const char* name, con mPerformanceMetrics.executionStartTime = getTimeInMilliSec(); if (nullptr != name) - { + { rtLogInfo("JavaScriptContext::evaluateScript name=%s", name); } JSValueRef exception = nullptr; JSStringRef codeStr = JSStringCreateWithUTF8CString(script); JSObjectRef globalObj = JSContextGetGlobalObject(mContext); JSStringRef fileStr = nullptr; - + if (name) { fileStr = JSStringCreateWithUTF8CString(name); JSGlobalContextSetName(mContext, fileStr); - } - + } + //MADANA BIG CHANGE - if (module) + if (false) { JSStringRef fileNameProperty = JSStringCreateWithUTF8CString("entryPointModuleName"); JSStringRef fileName = JSStringCreateWithUTF8CString(name); @@ -267,11 +267,11 @@ bool JavaScriptContext::evaluateScript(const char* script, const char* name, con functionLoadModule(mContext, globalObj, (char *) script, strlen(script), (char*)name); } else - { + { JSValueRef result = JSEvaluateScript(mContext, codeStr, globalObj, fileStr, 0, &exception); JSStringRelease(codeStr); if (nullptr != fileStr) - { + { JSStringRelease(fileStr); } if (exception) @@ -293,13 +293,13 @@ bool JavaScriptContext::evaluateScript(const char* script, const char* name, con void JavaScriptContext::processKeyEvent(struct JavaScriptKeyDetails& details, bool keyPress) { JSStringRef str = JSStringCreateWithUTF8CString(keyPress?"jsruntime.onKeyDown":"jsruntime.onKeyUp"); - + JSValueRef func = JSEvaluateScript(mContext, str, 0, 0, 0, 0); - + if (JSValueIsObject(mContext, func)) { JSObjectRef funcObj = JSValueToObject(mContext, func, 0); - + if (funcObj && JSObjectIsFunction(mContext, funcObj)) { rtObjectRef e = new rtMapObject; @@ -315,23 +315,23 @@ void JavaScriptContext::processKeyEvent(struct JavaScriptKeyDetails& details, bo e.set("metaKey", details.metaKey); e.set("repeat", details.repeat); e.set("keyCode", details.keyCode); - - + + const JSValueRef args[] = { rtToJs(mContext, e) }; - + size_t num_args = sizeof(args) / sizeof(JSValueRef*); - + JSValueRef exception = 0; - - JSValueRef result = JSObjectCallAsFunction(mContext, funcObj, 0, - num_args, args, + + JSValueRef result = JSObjectCallAsFunction(mContext, funcObj, 0, + num_args, args, &exception); - + if (exception) { NativeJSLogger::log(ERROR, "received exception during key handling\n"); } - + if (result) { // Handle result (if any) here. @@ -345,7 +345,7 @@ void JavaScriptContext::registerUtils() m_webSocketBinding = new rtFunctionCallback(rtWebSocketBinding, nullptr); #ifdef WS_SERVER_ENABLED if (mEnableWebSockerServer) - { + { m_webSocketServerBinding = new rtFunctionCallback(rtWebSocketServerBinding, nullptr); } #endif @@ -358,10 +358,12 @@ void JavaScriptContext::registerUtils() m_readBinaryBinding = new rtFunctionCallback(rtReadBinaryBinding, nullptr); m_setVideoStartTimeBinding = new rtFunctionCallback(rtSetVideoStartTimeBinding, this); m_JSRuntimeDownloadMetrics = new rtFunctionCallback(rtJSRuntimeDownloadMetrics, this); + m_setExternalAppHandlerBinding = new rtFunctionCallback(rtSetExternalAppHandlerBinding, this); + m_getRandomValuesBinding = new rtFunctionCallback(rtGetRandomValuesBinding, nullptr); add("webSocket", m_webSocketBinding.getPtr()); #ifdef WS_SERVER_ENABLED if (mEnableWebSockerServer) - { + { add("webSocketServer", m_webSocketServerBinding.getPtr()); } #endif @@ -374,7 +376,9 @@ void JavaScriptContext::registerUtils() add("readBinary", m_readBinaryBinding.getPtr()); add("setVideoStartTime", m_setVideoStartTimeBinding.getPtr()); add("JSRuntimeDownloadMetrics", m_JSRuntimeDownloadMetrics.getPtr()); - + add("setExternalAppHandler", m_setExternalAppHandlerBinding.getPtr()); + add("getRandomValuesCpp", m_getRandomValuesBinding.getPtr()); + #ifdef ENABLE_JSRUNTIME_PLAYER if (mModuleSettings.enablePlayer) { @@ -451,6 +455,7 @@ if (mModuleSettings.enablePlayer) runFile("linkedjsdom.js", nullptr/*, true*/); runFile("linkedjsdomwrapper.js", nullptr/*, true*/); runFile("windowwrapper.js", nullptr/*, true*/); + runFile("url.js", nullptr/*, true*/); if(getenv("FIREBOLT_ENDPOINT")!=NULL) { auto FireboltEndpoint = std::string(getenv("FIREBOLT_ENDPOINT")); @@ -484,15 +489,15 @@ void JavaScriptContext::dumpNetworkMetricData(NetworkMetrics *metrics, std::stri { std::ofstream file("/tmp/jsruntimenetworkmetrics.log", std::ios::app); - if (!file.is_open()) + if (!file.is_open()) { rtLogError("Failed to open jsruntimenetworkmetrics log file"); return; } - static bool isAppUrlLogged = false; + static bool isAppUrlLogged = false; if(!isAppUrlLogged) { - isAppUrlLogged = true; + isAppUrlLogged = true; file << appUrl << ":"; } auto now = std::chrono::system_clock::now(); @@ -506,18 +511,18 @@ void JavaScriptContext::dumpNetworkMetricData(NetworkMetrics *metrics, std::stri ss << " url: " << metrics->url << "," << std::endl; ss << " method: " << metrics->method << "," << std::endl; ss << " headers: ["; - for (size_t j = 0; j < metrics->headers.size(); ++j) + for (size_t j = 0; j < metrics->headers.size(); ++j) { ss << metrics->headers[j]; - if (j != metrics->headers.size() - 1) + if (j != metrics->headers.size() - 1) { ss << ", "; } } ss << "]," << std::endl; ss << " statusCode: " << metrics->statusCode << std::endl; - for (const auto& pair : metrics->timeMetricsData) - { + for (const auto& pair : metrics->timeMetricsData) + { ss << " " << pair.first.cString() << ": " << pair.second.toString().cString() << "\n"; } ss << "]" << std::endl; @@ -534,7 +539,7 @@ void JavaScriptContext::setCreateApplicationEndTime(double time, uint32_t id) { mPerformanceMetrics.createApplicationEndTime = time; double duration = time-mPerformanceMetrics.createApplicationStartTime; - + NativeJSLogger::log(INFO, "createApplicationDuration for ID %d: %.3f ms\n",id, duration); } @@ -548,7 +553,7 @@ void JavaScriptContext::setAppdata(uint32_t id, const std::string& url) { mIds = id; mUrls = url; -} +} void JavaScriptContext::setPlaybackStartTime(double time) { @@ -556,3 +561,12 @@ void JavaScriptContext::setPlaybackStartTime(double time) double launchTime = mPerformanceMetrics.playbackStartTime - mPerformanceMetrics.createApplicationStartTime; NativeJSLogger::log(INFO, "Launch_Duration for ID %d | URL %s : %.3f ms\n", mIds, mUrls.c_str(), launchTime); } + +void JavaScriptContext::handleExternalApplication(const std::string& url) +{ + if (mExternalApplicationHandler) + { + printf("Got exteranl application set [%s] in id [%d] \n", url.c_str(), mIds); + mExternalApplicationHandler->runExternalApplication(url, mIds); + } +} diff --git a/src/jsc/JavaScriptUtils.cpp b/src/jsc/JavaScriptUtils.cpp index debc108..836aa23 100644 --- a/src/jsc/JavaScriptUtils.cpp +++ b/src/jsc/JavaScriptUtils.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -178,14 +179,17 @@ class rtHttpRequestEx : public rtHttpRequest rtHttpRequestEx(const rtString& url) : rtHttpRequest(url) - { } + { + } rtHttpRequestEx(const rtObjectRef& options) : rtHttpRequest(options) - { } + { + } void onDownloadProgressImpl(double progress) final { + AddRef(); dispatchOnMainLoop( [this, downloadedsize = progress] () @@ -203,10 +207,10 @@ class rtHttpRequestEx : public rtHttpRequest AddRef(); if (!downloadRequest->errorString().isEmpty()) { dispatchOnMainLoop( - [this, errorString = downloadRequest->errorString(), statusCode = downloadRequest->downloadStatusCode()] () + [this, errorString = downloadRequest->errorString(), statusCode = downloadRequest->downloadStatusCode()] () { if (statusCode == 28) - { + { mEmit.send("error", "TIMEDOUT"); } else @@ -221,7 +225,7 @@ class rtHttpRequestEx : public rtHttpRequest resp->setErrorMessage(downloadRequest->errorString()); resp->setHeaders(downloadRequest->headerData(), downloadRequest->headerDataSize()); resp->setDownloadedData(downloadRequest->downloadedData(), downloadRequest->downloadedDataSize()); - + if (mMetricsListener) { rtLogWarn("metriclistener"); @@ -229,11 +233,11 @@ class rtHttpRequestEx : public rtHttpRequest if (!metrics){ rtLogError("Failed to allocate NetworkMetrics"); return; - } + } metrics->url = this->url(); metrics->method = this->method(); std::vector headerValues = this->headers(); - metrics->headers = headerValues; + metrics->headers = headerValues; metrics->statusCode = downloadRequest->httpStatusCode(); rtObjectRef timeMetrics = downloadRequest->downloadMetrics(); @@ -262,15 +266,15 @@ class rtHttpRequestEx : public rtHttpRequest resp->onEnd(); Release(); }); - } - } -}; + } + } + }; + rtDefineObject(rtHttpRequestEx, rtHttpRequest); rtError rtHttpGetBinding(int numArgs, const rtValue* args, rtValue* result, void* context) { UNUSED_PARAM(context); - if (numArgs < 1) { rtLogError("%s: invalid args", __FUNCTION__); return RT_ERROR_INVALID_ARG; @@ -299,7 +303,7 @@ rtError rtHttpGetBinding(int numArgs, const rtValue* args, rtValue* result, void } req->setNetworkMetricsListener(jscContext); - + rtObjectRef ref = req; *result = ref; return RT_OK; @@ -333,12 +337,12 @@ rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, v FILE *ptr = nullptr; ptr = fopen("hello.wasm","rb"); // r for read, b for binary - char *fd = "hello.wasm"; - struct stat buf; - + const char *fd = "hello.wasm"; + struct stat buf; + stat(fd, &buf); int size = buf.st_size; - + buffer = (char*)malloc(size); fread(buffer,size,1,ptr); // read 10 bytes to our buffer fclose(ptr); @@ -466,7 +470,7 @@ rtError getThunderTokenBinding(int numArgs, const rtValue* args, rtValue* result { rtLogError("lost context !!!"); return RT_ERROR; - } + } if (result) { result->setString(""); @@ -475,7 +479,7 @@ rtError getThunderTokenBinding(int numArgs, const rtValue* args, rtValue* result { rtLogError("lost return value !!!"); return RT_ERROR; - } + } unsigned char tokenBuffer[MAX_TOKEN_BUFFER_LENGTH]; memset(tokenBuffer, 0, MAX_TOKEN_BUFFER_LENGTH); std::string appUrl = jsccontext->getUrl(); @@ -690,7 +694,7 @@ char* JSValueToCString(JSContextRef context, JSValueRef value, JSValueRef* excep return src; } -rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* result, void* context) +rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* result, void* context) { JavaScriptContext* jscContext = (JavaScriptContext*)context; if (jscContext == nullptr) { @@ -717,7 +721,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re rtLogWarn("No url found in the network metrics data."); return RT_FAIL; } - + size_t count = keysArray->length(); for (size_t i = 0; i < count; ++i) { @@ -745,7 +749,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re rtArrayObject* timeMetricsArray = new rtArrayObject(); for (const auto& metric : metrics->timeMetricsData) { - rtObjectRef timeMetricObj = new rtMapObject(); + rtObjectRef timeMetricObj = new rtMapObject(); timeMetricObj->Set(metric.first.cString(), &metric.second); timeMetricsArray->pushBack(rtValue(timeMetricObj)); delete timeMetricObj; @@ -767,3 +771,46 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re return RT_OK; } +rtError rtSetExternalAppHandlerBinding(int numArgs, const rtValue* args, rtValue* result, void* context) +{ + if (context == nullptr || numArgs < 1) { + rtLogError("Invalid context or missing arguments"); + return RT_ERROR_INVALID_ARG; + } + + JavaScriptContext* jscContext = (JavaScriptContext*)context; + + if (args[0].getType() != RT_stringType) { + rtLogError("Requires a string argument"); + return RT_ERROR_INVALID_ARG; + } + + rtString url = args[0].toString(); + jscContext->handleExternalApplication(url.cString()); + + if (result) + *result = true; + + return RT_OK; +} + +rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* /*result*/, void* /*context*/) +{ + if (numArgs < 1 || args[0].getType() != RT_objectType) { + rtLogError("rtGetRandomValuesBinding: Invalid arguments"); + return RT_ERROR_INVALID_ARG; + } + + rtObjectRef arrObj = args[0].toObject(); + + uint32_t len = arrObj.get("length"); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist; + + for (uint32_t i = 0; i < len; i++) { + arrObj.set(i, (double)dist(gen)); + } + return RT_OK; +} diff --git a/src/jsc/modules/lib/URL.js b/src/jsc/modules/lib/URL.js index 26b1a3e..6106601 100644 --- a/src/jsc/modules/lib/URL.js +++ b/src/jsc/modules/lib/URL.js @@ -220,6 +220,10 @@ exports.implementation = class URLImpl { toJSON() { return this.href; } + + toString() { + return this.href; + } _potentiallyStripTrailingSpacesFromAnOpaquePath() { if (!usm.hasAnOpaquePath(this._url)) { diff --git a/src/jsc/modules/lib/url-state-machine.js b/src/jsc/modules/lib/url-state-machine.js index 16a7660..d98879d 100644 --- a/src/jsc/modules/lib/url-state-machine.js +++ b/src/jsc/modules/lib/url-state-machine.js @@ -1068,9 +1068,11 @@ URLStateMachine.prototype["parse query"] = function parseQuery(c, cStr) { } if ((!this.stateOverride && c === p("#")) || isNaN(c)) { - const queryPercentEncodePredicate = isSpecial(this.url) ? isSpecialQueryPercentEncode : isQueryPercentEncode; - this.url.query += utf8PercentEncodeString(this.buffer, queryPercentEncodePredicate); - + + // TODO: Check for encryption + //const queryPercentEncodePredicate = isSpecial(this.url) ? isSpecialQueryPercentEncode : isQueryPercentEncode; + //this.url.query += utf8PercentEncodeString(this.buffer, queryPercentEncodePredicate); + this.url.query += this.buffer; this.buffer = ""; if (c === p("#")) { diff --git a/src/jsc/modules/linkedjsdomwrapper.js b/src/jsc/modules/linkedjsdomwrapper.js index 1281bb9..476986f 100755 --- a/src/jsc/modules/linkedjsdomwrapper.js +++ b/src/jsc/modules/linkedjsdomwrapper.js @@ -55,3 +55,31 @@ console.log(window.screen); console.log(window.URL); console.log(window.URLSearchParams); */ +document.location = window.location; + +function getRandom(arr) { + getRandomValuesCpp(arr); +} + +crypto = {} +crypto.getRandomValues = getRandom; + +(function setupUrlChangeDetection() { + let currentHref = window.location.href; + + const checkUrlChange = () => { + let newHref = window.location.href; + if (document && document.location && document.location.href && newHref !== document.location.href) { + newHref = document.location.href; + } + + if (newHref !== currentHref) { + if (typeof setExternalAppHandler === 'function') { + setExternalAppHandler(newHref); + } + currentHref = newHref; + } + }; + + setInterval(checkUrlChange, 500); +})(); diff --git a/src/jsc/modules/node-fetch.js b/src/jsc/modules/node-fetch.js index 62a9fed..75b2c10 100755 --- a/src/jsc/modules/node-fetch.js +++ b/src/jsc/modules/node-fetch.js @@ -1802,7 +1802,12 @@ class Headers { return null; } - return this[MAP][key].join(', '); + const value = this[MAP][key]; + // Always ensure we return a string, not an array + if (Array.isArray(value)) { + return value.join(', '); + } + return String(value); } /** @@ -1841,7 +1846,7 @@ class Headers { validateName(name); validateValue(value); const key = find(this[MAP], name); - this[MAP][key !== undefined ? key : name] = [value]; + this[MAP][key !== undefined ? key : name] = [value]; } /** @@ -2021,11 +2026,16 @@ Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { function exportNodeCompatibleHeaders(headers) { const obj = Object.assign({ __proto__: null }, headers[MAP]); - // http.request() only supports string as Host header. This hack makes - // specifying custom Host header possible. - const hostHeaderKey = find(headers[MAP], 'Host'); - if (hostHeaderKey !== undefined) { - obj[hostHeaderKey] = obj[hostHeaderKey][0]; + // Convert all header values from arrays to strings for Node.js compatibility + for (const key in obj) { + if (Array.isArray(obj[key])) { + // Join array elements with comma and space, or take first element if single + obj[key] = obj[key].length === 1 ? obj[key][0] : obj[key].join(', '); + } + else if (obj[key] !== undefined) { + // Ensure non-array values are also converted to strings + obj[key] = String(obj[key]); + } } return obj; @@ -2381,8 +2391,12 @@ function getNodeRequestOptions(request) { } // HTTP-network-or-cache fetch step 2.15 - if (request.compress && !headers.has('Accept-Encoding')) { + // Only request compression if zlib is available for decompression + const zlibAvailable = typeof zlib !== 'undefined'; + if (request.compress && zlibAvailable && !headers.has('Accept-Encoding')) { headers.set('Accept-Encoding', 'gzip,deflate'); + } else if (request.compress && !zlibAvailable) { + console.log('[DEBUG] Compression requested but zlib not available - requesting uncompressed response'); } let agent = request.agent; @@ -2706,53 +2720,62 @@ function fetch(url, opts) { // servers send slightly invalid responses that are still accepted // by common browsers. // Always using Z_SYNC_FLUSH is what cURL does. - /* - const zlibOptions = { - flush: zlib.Z_SYNC_FLUSH, - finishFlush: zlib.Z_SYNC_FLUSH - }; - - // for gzip - if (codings == 'gzip' || codings == 'x-gzip') { - body = body.pipe(zlib.createGunzip(zlibOptions)); - response = new Response(body, response_options); - resolve(response); - return; - } - - // for deflate - if (codings == 'deflate' || codings == 'x-deflate') { - // handle the infamous raw deflate response from old servers - // a hack for old IIS and Apache servers - const raw = res.pipe(new PassThrough$1()); - raw.once('data', function (chunk) { - // see http://stackoverflow.com/questions/37519828 - if ((chunk[0] & 0x0F) === 0x08) { - body = body.pipe(zlib.createInflate()); - } else { - body = body.pipe(zlib.createInflateRaw()); - } + + // Check if zlib is available (not available in all environments) + const zlibAvailable = typeof zlib !== 'undefined'; + + if (zlibAvailable) { + const zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + }; + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { + body = body.pipe(zlib.createGunzip(zlibOptions)); response = new Response(body, response_options); resolve(response); - }); - raw.on('end', function () { - // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted. - if (!response) { + return; + } + + // for deflate + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = res.pipe(new PassThrough$1()); + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = body.pipe(zlib.createInflate()); + } else { + body = body.pipe(zlib.createInflateRaw()); + } response = new Response(body, response_options); resolve(response); - } - }); - return; - } + }); + raw.on('end', function () { + // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted. + if (!response) { + response = new Response(body, response_options); + resolve(response); + } + }); + return; + } - // for br - if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { - body = body.pipe(zlib.createBrotliDecompress()); - response = new Response(body, response_options); - resolve(response); - return; + // for br + if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { + body = body.pipe(zlib.createBrotliDecompress()); + response = new Response(body, response_options); + resolve(response); + return; + } + } else { + // zlib not available - log warning for compressed responses + if (codings && codings !== 'identity') { + console.warn(`[DEBUG] Content-Encoding '${codings}' detected but zlib not available for decompression`); + } } - */ // otherwise, use response as-is response = new Response(body, response_options); console.log(response); @@ -8608,6 +8631,12 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ var __webpack_exports__ = __webpack_require__(568); /******/ FetchLib = __webpack_exports__; fetch = FetchLib; + + Headers = FetchLib.Headers; + Request = FetchLib.Request; + Response = FetchLib.Response; + FetchError = FetchLib.FetchError; + AbortError = FetchLib.AbortError; /******/ /******/ })() ; diff --git a/src/jsc/modules/url.js b/src/jsc/modules/url.js index db0ba07..4015022 100644 --- a/src/jsc/modules/url.js +++ b/src/jsc/modules/url.js @@ -22,7 +22,7 @@ const URLRet = require("./lib/URL"); var URL = URLRet.implementation; //JSRUNTIME KEEP IT FOR FUTURE USE -/* + const URLSearchParamsRet = require("./lib/URLSearchParams"); const URLSearchParams = URLSearchParamsRet.implementation; @@ -44,4 +44,4 @@ var cannotHaveAUsernamePasswordPort = urlStateMachine.cannotHaveAUsernamePasswor var hasAnOpaquePath = urlStateMachine.hasAnOpaquePath; var percentDecodeString = percentEncoding.percentDecodeString; var percentDecodeBytes = percentEncoding.percentDecodeBytes; -*/ + diff --git a/src/jsc/modules/windowwrapper.js b/src/jsc/modules/windowwrapper.js index 8bc43f2..dd87e0a 100755 --- a/src/jsc/modules/windowwrapper.js +++ b/src/jsc/modules/windowwrapper.js @@ -100,3 +100,36 @@ catch(err) //console.log(window.origin); //console.log(window.Navigator); //console.log(window.Storage); + +class LocalStorage { + constructor() { + this.data = {}; + } + + getItem(key) { + return this.data[key] || null; + } + setItem(key, value) { + this.data[key] = String(value); + } + + removeItem(key) { + delete this.data[key]; + } + + clear() { + this.data = {}; + } + + get length() { + return Object.keys(this.data).length; + } + + key(index) { + const keys = Object.keys(this.data); + return keys[index] || null; + } +} + +const localStorage = new LocalStorage(); +