Răsfoiți Sursa

!10829 浏览器项目合并
Merge pull request !10829 from 80374374/0704_uos_cgroup

Gifur 2 luni în urmă
părinte
comite
ecf52a3f31

+ 0 - 1
Framework/spbase/sp_httpDefine.cpp

@@ -609,7 +609,6 @@ std::pair<bool, std::string> refreshToken(std::string terminalNo, std::string in
 		}
 	}
 	LogWarn(Severity_Low, Error_Bug, GET_TOKEN_ERR, "total getToken failed");
-	DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM).setAPI("refreshToken").setLogCode("QLR0402Z10A00003")("total getToken failed");
 	return std::make_pair(false, "");
 }
 

+ 1 - 0
Module/mod_chromium/CMethodInterface.h

@@ -8,6 +8,7 @@ namespace Chromium {
 	class CMedthodInterface {
 	public:
 		bool m_forceNewReturn;
+		std::string mEntityName;
 		std::string mMethodName;
 		std::string mMethodType;
 		CTransStruct mRequestInterpreter;

+ 24 - 10
Module/mod_chromium/CModTools.cpp

@@ -408,8 +408,18 @@ namespace Chromium {
 		if (stopAll)
 			killAllChromium();
 		else
-			LogWarn(Severity_Low, Error_NotSupport, LOG_WARN_CHROMIUM_NOT_SUPPORT_TOKILL_SPECIFIED_BROWSER,
-					CSimpleStringA::Format("StopChromiumBrowser: %s", reason._to_string()));
+		{
+			CSimpleStringA strBasePath(true);
+			this->m_pEntity->GetFunction()->GetPath("BaseDir", strBasePath);
+			std::string strPath(strBasePath.GetData());
+			std::string stopBrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "stopBrowser.sh";
+			stopBrowser_path.append(" --vtm_browser_type=").append(reason._to_string());
+			DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("StopChromiumBrowser cmdline:%s", stopBrowser_path.c_str());
+			do {
+				boost::process::child child_process(stopBrowser_path);
+				child_process.wait();
+			} while (false);
+		}
 #endif //_MSC_VER
 	}
 
@@ -599,8 +609,8 @@ namespace Chromium {
 		CSimpleStringA strBasePath(true);
 		this->m_pEntity->GetFunction()->GetPath("BaseDir", strBasePath);
 		std::string strPath(strBasePath.GetData());
-		const std::string execute_newbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
-		const std::string execute_oldbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
+		const std::string execute_newbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
+		const std::string execute_oldbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
 		CSimpleStringA strParams(true);
 		if (width > 0 && height > 0)
 			strParams.Append(" --window-size=").Append(std::to_string(width).c_str()).Append(",").Append(std::to_string(height).c_str());
@@ -669,6 +679,7 @@ namespace Chromium {
 			CSimpleStringA usrDataPath(tempPath);
 			usrDataPath.Append(SPLIT_SLASH_STR).Append("UOSBrowserConfig_").Append(name.c_str());
 			strParams.Append(" --user-data-dir=").Append(usrDataPath);
+			strParams.Append(" --vtm_browser_type=").Append(name.c_str());
 
 			CSimpleStringA debug_log_path = usrDataPath + "/chrome_debug.log";
 			if (ConfigManager::getInstance().m_withDebugMode)
@@ -706,6 +717,7 @@ namespace Chromium {
 			CSimpleStringA usrDataPath(tempPath);
 			usrDataPath.Append(SPLIT_SLASH_STR).Append("BrowserConfig_").Append(name.c_str());
 			strParams.Append(" --user-data-dir=").Append(usrDataPath);
+			strParams.Append(" --vtm_browser_type=").Append(name.c_str());
 			CSimpleStringA debug_log_path = usrDataPath + "/chrome_debug.log";
 			if (ConfigManager::getInstance().m_withDebugMode)
 				strParams.Append(" --enable-logging --vmodule=*/webrtc/*=2,*/media/*=2 --log-file=").Append(debug_log_path);
@@ -787,11 +799,11 @@ namespace Chromium {
         CSimpleStringA strBasePath(true);
         this->m_pEntity->GetFunction()->GetPath("BaseDir", strBasePath);
         std::string strPath(strBasePath.GetData());
-        const std::string execute_newbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
-        const std::string execute_oldbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
+        const std::string execute_newbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
+        const std::string execute_oldbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
 
 		std::string name = "";
-		if (pageType == 0 || pageType == 2 || pageType == 3) {
+		if (pageType == 0) {
 			name = (+ERR_PAGE_REASON::main)._to_string();
 		}
 		else if (pageType == 1) {
@@ -891,6 +903,7 @@ namespace Chromium {
                 usrDataPath.Append(CSimpleStringA(SPLIT_SLASH_STR)).Append("UOSBrowserConfigMain_").Append((+ERR_PAGE_REASON::OutsideRequest)._to_string());
             }
             strParams.Append(" --user-data-dir=").Append(usrDataPath);
+			strParams.Append(" --vtm_browser_type=").Append(name.c_str());
 
 			CSimpleStringA debug_log_path = usrDataPath + "/chrome_debug.log";
 			if (ConfigManager::getInstance().m_withDebugMode)
@@ -967,6 +980,7 @@ namespace Chromium {
                 cachePath.Append(CSimpleStringA(SPLIT_SLASH_STR)).Append("BrowserConfigMain_").Append((+ERR_PAGE_REASON::OutsideRequest)._to_string());
             }
             strParams.Append(" --user-data-dir=").Append(usrDataPath);
+			strParams.Append(" --vtm_browser_type=").Append(name.c_str());
 			CSimpleStringA debug_log_path = usrDataPath + "/chrome_debug.log";
 			if (ConfigManager::getInstance().m_withDebugMode)
 				strParams.Append(" --enable-logging --vmodule=*/webrtc/*=2,*/media/*=2 --log-file=").Append(debug_log_path);
@@ -1138,7 +1152,7 @@ namespace Chromium {
 			if (!checkRet.first)
 			{
 				LogWarn(Severity_High, Error_NetBroken, LOG_WARN_CHROMIUM_INSTALL_URLS_CHECK,
-					CSimpleString::Format("install page check err, can not connect to %s or %s", installUrls[0].c_str(), installUrls[1].c_str()).GetData());
+					CSimpleString::Format("install page check err, can not connect to %s", installUrls[0].c_str()));
 			}
 			std::string dstInstallUrl = checkRet.first ? checkRet.second : installUrls[0];
 			CSimpleString runningVer = "";
@@ -1450,8 +1464,8 @@ namespace Chromium {
         CSimpleStringA strBasePath(true);
         this->m_pEntity->GetFunction()->GetPath("BaseDir", strBasePath);
         std::string strPath(strBasePath.GetData());
-        const std::string execute_newbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
-        const std::string execute_oldbrowser_path = strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
+        const std::string execute_newbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startBrower.sh";
+        const std::string execute_oldbrowser_path = "sudo " + strPath + SPLIT_SLASH_STR + "res" + SPLIT_SLASH_STR + "RunScript" + SPLIT_SLASH_STR + "startUOSBrower.sh";
 
 		int result(0);
 		std::string additional("");

+ 4 - 3
Module/mod_chromium/CStructureInterpreter.cpp

@@ -45,6 +45,8 @@ namespace Chromium {
 		std::string strEntityName = pEntity->Attribute("name");
 		std::vector<CMedthodInterface> messageInterface;
 		this->loadMessageInterface(messageInterface, pEntity);
+		for (auto &it : messageInterface)
+			it.mEntityName = strEntityName;
 		this->mMessageStructureMap.insert(std::pair<std::string, std::vector<CMedthodInterface>>(strEntityName, messageInterface));
 		// get class node
 		tinyxml2::XMLElement* pClass = pEntity->FirstChildElement("class");
@@ -53,6 +55,8 @@ namespace Chromium {
 		std::string strClassName = pClass->Attribute("name");
 		std::map<int, CMedthodInterface> functionInterface;
 		this->loadFunctionInterface(functionInterface, pClass);
+		for (auto &it : functionInterface)
+			it.second.mEntityName = strEntityName;
 		m_inServiceEntity.insert(strEntityName);
 		this->mMethodStructureMap.insert(std::pair<std::string, std::map<int, CMedthodInterface>>(strEntityName.append(strClassName), functionInterface));
 		
@@ -96,9 +100,6 @@ namespace Chromium {
 				functionInterface.insert(std::pair<int, CMedthodInterface>(i, method));
 			}
 			
-			
-
-
 			//		functionInterface.emplace_back(method);
 			element = element->NextSiblingElement();
 		}

+ 2 - 0
Module/mod_chromium/CWSCodec.cpp

@@ -682,6 +682,8 @@ namespace Chromium {
 				free(str);
 				return s;
 			}
+			if(iter->second->size() > 0)
+				msgInfo.entityName = iter->second->at(0).mEntityName;
 			DeserializeEvent(msg, iter->second, rpos, ret, isSafe);
 		}
 

+ 12 - 8
Module/mod_chromium/CWebsocketServer.cpp

@@ -1297,17 +1297,20 @@ namespace Chromium {
 		}
 		else
 		{
+			/*
 			if (ConfigManager::getInstance().m_withLinkLog)
 				DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("WebSocket Search message_from_socket : json = %s", js.c_str());
 			else if (messageType != MessageType::RequestAck && messageType != MessageType::Event)//do not upload message which messageType equals to RequestAck or Event
 				DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("WebSocket Search message_from_socket : json = %s", js.c_str());
+			*/
 			if (js.empty())
 			{
 				DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("string empty");
 				return;
 			}
-			if (ConfigManager::getInstance().m_connection_hdls.empty() && ConfigManager::getInstance().m_connection_ws_sm2_hdls.empty())
-				DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("message_from_socket : no websocket client connection");
+			if (ConfigManager::getInstance().m_connection_hdls.empty() && ConfigManager::getInstance().m_connection_ws_sm2_hdls.empty() && messageType != MessageType::Event)
+				DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)
+					("message_from_socket : no websocket client connection, messageType:%s", GetMessageTypeString(messageType));
 			else {
 #if(defined _WIN32 || defined _WIN64)
 				js = string_to_utf8(js);
@@ -1331,12 +1334,13 @@ namespace Chromium {
 		// get message from socket and deserialize 
 		// then send back to the web client
 		auto bufferLength = msg.getBufferLength();
+		
 		/*
 		if (bufferLength > MAX_TRANSFER_LEN)
-			DbgEx("WebSocket Search message_from_socket : buffer len = %d, buffer pre50:%s", msg.getBufferLength(), msg.printfHEX(50).c_str());
+			DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("WebSocket Search message_from_socket : buffer len = %d, buffer pre50:%s", msg.getBufferLength(), msg.printfHEX(50).c_str());
 		else
 			receivehexdump(msg.getPayload(), msg.getLength());
-			*/
+		*/
 		//else
 		//	DbgEx("WebSocket Search message_from_socket : buffer len = %d", msg.getBufferLength());
 
@@ -1384,14 +1388,14 @@ namespace Chromium {
 				unaccurate_js = js;
 			}
 			auto signatureID = msg.getSignatureID();
-			auto sessionId = msg.getSessionID();
-			auto sessionEntityName = EntitySessionManager::FromSessionIdToEntityName(sessionId);
+			auto sessionId = cur_from_socket.sessionId;
+			auto sessionEntityName = cur_from_socket.entityName;
 			if (sendArr.size() == 0)
-				LogManager::getInstance().logVtmEvent(-1, -1, sessionId, sessionEntityName.second, 0, "send", sendArr,
+				LogManager::getInstance().logVtmEvent(-1, -1, sessionId, sessionEntityName, 0, "send", sendArr,
 					cur_from_socket.payLoad, cur_from_socket.msgBytes, Error_NotExist, "message_from_socket event sendArr.size() == 0");
 			else if (signatureID != eMsgSig_LogInfo && signatureID != eMsgSig_EntityStatus && signatureID != eMsgSig_PerformanceList)//fiter guiconsole msg
 			{
-				LogManager::getInstance().logVtmEvent(-1, -1, sessionId, sessionEntityName.second, 0, "send", sendArr,
+				LogManager::getInstance().logVtmEvent(-1, -1, sessionId, sessionEntityName, 0, "send", sendArr,
 					cur_from_socket.payLoad, cur_from_socket.msgBytes);
 				/*
 #ifdef DEVOPS_ON_ST 

+ 3 - 33
Module/mod_chromium/mod_chromium.cpp

@@ -872,16 +872,8 @@ namespace Chromium {
 
 				if (ConfigManager::getInstance().m_existRootIni && ConfigManager::getInstance().m_installMode)
 					return;//if root.ini exist and in install mode, do not close startup page
-#if defined(RVC_OS_LINUX)
-				for (auto it : srcPids)
-				{
-					CSimpleStringA cmd = CSimpleStringA::Format("sudo kill -9 %d", it);
-					DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("CloseCommonPage %s", cmd.GetData());
-					system(cmd.GetData());
-				}
-#else
+
 				CModTools::get_mutable_instance().StopChromiumBrowser(ERR_PAGE_REASON::startup);
-#endif
 				startupPageClean();
 				return;
 			}
@@ -993,9 +985,6 @@ namespace Chromium {
 		{
 			if (CModTools::get_mutable_instance().IsConfigWork())//url正常时才关闭
 			{
-#if defined(RVC_OS_LINUX)
-				auto srcPids = getUosBrowserPIDs(CModTools::get_mutable_instance().getUosBrowser());
-#endif // RVC_OS_UOS
 
 				if (ConfigManager::getInstance().m_runAd) openAdPage();
 				openMainPage();
@@ -1004,16 +993,9 @@ namespace Chromium {
 				std::this_thread::sleep_for(std::chrono::seconds(3));
 				LogWarn(Severity_Low, Error_Debug, LOG_SLV_CHROMIUM_URLOPEN, "正在启动业务界面");
 
-#if defined(RVC_OS_LINUX)
-				for (auto it : srcPids)
-				{
-					CSimpleStringA cmd = CSimpleStringA::Format("sudo kill -9 %d", it);
-					DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("CloseCommonPage %s", cmd.GetData());
-					system(cmd.GetData());
-				}
-#else
+
 				CModTools::get_mutable_instance().StopChromiumBrowser(ERR_PAGE_REASON::startup);
-#endif
+
 				startupPageClean();
 
 				//DbgEx("UnregistSysVarEvent TerminalStage %s", Error_Succeed == GetFunction()->UnregistSysVarEvent("TerminalStage") ? "success" : "fail");
@@ -1036,22 +1018,10 @@ namespace Chromium {
 		{
 			if (!ConfigManager::getInstance().m_withBrowser)
 			{
-#if defined(RVC_OS_LINUX)
-				auto srcPids = getUosBrowserPIDs(CModTools::get_mutable_instance().getUosBrowser());
-#endif // RVC_OS_UOS
 
 				if (ConfigManager::getInstance().m_existRootIni && ConfigManager::getInstance().m_installMode)
 					return;//if root.ini exist and in install mode, do not close startup page
-#if defined(RVC_OS_LINUX)
-				for (auto it : srcPids)
-				{
-					CSimpleStringA cmd = CSimpleStringA::Format("sudo kill -9 %d", it);
-					DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("CloseCommonPage %s", cmd.GetData());
-					system(cmd.GetData());
-				}
-#else
 				CModTools::get_mutable_instance().StopChromiumBrowser(ERR_PAGE_REASON::startup);
-#endif
 				startupPageClean();
 				ERR_PAGE_REASON reson = ERR_PAGE_REASON::breakdown;
 				if (0 == CSimpleStringA("S").Compare(terminalStageVal.c_str(), true))

+ 1 - 1
addin/cmake/DependencyConanFiles.cmake

@@ -69,7 +69,7 @@ if(MSVC)
 			#mod_chromium
 			CEFControl/1.0@LR04.02_ThirdParty/testing
 			#mod_chromium
-			cefclient_mutable/2.0.55@LR04.02_ThirdParty/testing
+			cefclient_mutable/2.0.57@LR04.02_ThirdParty/testing
 			#libaudiorender
 			speex/1.2.1@LR04.02_ThirdParty/testing
 			#libvideoframework

+ 34 - 2
addin/res/RunScript/startBrower.sh

@@ -6,6 +6,26 @@ echo "start $0 with $name"
 
 exec="/usr/share/browser/browser"  # 使用传入的参数值作为要执行的命令
 echo "run $exec"
+
+#!/bin/bash
+declare -A params  # 关联数组存储参数
+
+for arg in "$@"; do
+    if [[ "$arg" =~ ^--vtm_browser_type=(.*)$ ]]; then
+        vtm_browser_type="${BASH_REMATCH[1]}"
+        echo "浏览器类型: $vtm_browser_type"
+        break  # 找到后退出循环
+    fi
+done
+
+if [[ -z "$vtm_browser_type" ]]; then
+    echo "未指定浏览器类型"
+fi
+
+
+GROUP_NAME=${vtm_browser_type:-default_browser}
+
+
 # get env
 pid=$(pgrep startdde)
 # source env from $pid
@@ -13,10 +33,22 @@ pid=$(pgrep startdde)
 . <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- </proc/$pid/environ)
 name=$(ps -o uname= -p "${pid}")
 echo "run in $name"
+
+FILE="/tmp/$GROUP_NAME.pgid"
+rm -f "$FILE"  # 强制删除文件(忽略不存在的情况)
+touch "$FILE"  # 创建新文件
+chmod 666 "$FILE"  # 可选:设置权限
+
 if [ $# -eq 0 ]
 then
-	runuser -u "$name" -- bash -c "$exec &" &  #无参数执行
+    runuser -u  $name -- bash -c "setsid $exec & sudo echo \$! > /tmp/$GROUP_NAME.pgid"
 else
-	runuser -u "$name" -- bash -c "$exec $* &" &  # 将参数传递给命令
+    runuser -u  $name -- bash -c "setsid $exec $* & sudo echo \$! > /tmp/$GROUP_NAME.pgid"
 fi
+
+PGID=$(cat /tmp/$GROUP_NAME.pgid)
+
+echo "UOS Browser 已启动:"
+echo "PGID: $PGID"
+
 exit $?

+ 32 - 2
addin/res/RunScript/startUOSBrower.sh

@@ -4,6 +4,21 @@ set -x
 name=$(whoami)
 echo "start $0 with $name"
 
+for arg in "$@"; do
+    if [[ "$arg" =~ ^--vtm_browser_type=(.*)$ ]]; then
+        vtm_browser_type="${BASH_REMATCH[1]}"
+        echo "浏览器类型: $vtm_browser_type"
+        break  # 找到后退出循环
+    fi
+done
+
+if [[ -z "$vtm_browser_type" ]]; then
+    echo "未指定浏览器类型"
+fi
+
+GROUP_NAME=${vtm_browser_type:-default_browser}
+
+
 exec="/usr/share/uosbrowser/uosbrowser"  # 使用传入的参数值作为要执行的命令
 echo "run $exec"
 # get env
@@ -13,10 +28,25 @@ pid=$(pgrep startdde)
 . <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- </proc/$pid/environ)
 name=$(ps -o uname= -p "${pid}")
 echo "run in $name"
+
+FILE="/tmp/$GROUP_NAME.pgid"
+rm -f "$FILE"  # 强制删除文件(忽略不存在的情况)
+touch "$FILE"  # 创建新文件
+chmod 666 "$FILE"  # 可选:设置权限
+
 if [ $# -eq 0 ]
 then
-	runuser -u "$name" -- bash -c "$exec &" &  #无参数执行
+    runuser -u  $name -- bash -c "setsid $exec & sudo echo \$! > /tmp/$GROUP_NAME.pgid"
 else
-	runuser -u "$name" -- bash -c "$exec $* &" &  # 将参数传递给命令
+    runuser -u  $name -- bash -c "setsid $exec $* & sudo echo \$! > /tmp/$GROUP_NAME.pgid"
 fi
+
+PGID=$(cat /tmp/$GROUP_NAME.pgid)
+
+
+echo "UOS Browser 已启动:"
+echo "PGID: $PGID"
+
+
+
 exit $?

+ 41 - 0
addin/res/RunScript/stopBrowser.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# 检查是否提供了进程组名称参数
+if [ $# -eq 0 ]; then
+    echo "错误:请提供进程组名称作为参数,例如:./stop_browser.sh my-group"
+    exit 1
+fi
+
+
+for arg in "$@"; do
+    if [[ "$arg" =~ ^--vtm_browser_type=(.*)$ ]]; then
+        vtm_browser_type="${BASH_REMATCH[1]}"
+        echo "浏览器类型: $vtm_browser_type"
+        break  # 找到后退出循环
+    fi
+done
+
+if [[ -z "$vtm_browser_type" ]]; then
+    echo "未指定浏览器类型"
+fi
+
+GROUP_NAME=$vtm_browser_type
+
+
+# 通过 PGID 关闭(推荐)
+if [ -f /tmp/$GROUP_NAME.pgid ]; then
+    PGID=$(cat /tmp/$GROUP_NAME.pgid)
+    kill -15 -$PGID 2>/dev/null  # 终止整个进程组
+    sleep 1
+    if ps -o pgid= | grep -q $PGID; then
+        kill -9 -$PGID
+    fi
+    rm -rf /tmp/$GROUP_NAME.pgid
+fi
+
+# 清理 cgroup(如果存在)
+if command -v cgdelete &> /dev/null; then
+    sudo cgdelete cpu,memory:"$GROUP_NAME" 2>/dev/null
+fi
+
+echo "UOS Browser 已终止"