* Copyright (c) 2025 Huawei Device Co., Ltd.
* 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.
*/
#include "ani_proxy_controller.h"
#include "ani.h"
#include "webview_controller.h"
#include "proxy_config.h"
#include "proxy_rule.h"
#include "nweb_helper.h"
#include "ani_business_error.h"
#include "ani_parse_utils.h"
#include "nweb_log.h"
#include "web_errors.h"
namespace OHOS {
namespace NWeb {
using namespace NWebError;
using NWebError::NO_ERROR;
namespace {
const char* WEB_PROXY_CONTROLLER_CLASS_NAME = "@ohos.web.webview.webview.ProxyController";
const int ZERO = 0;
}
static void JsConstructor(ani_env* env, ani_object object)
{
WVLOG_D("ProxyConfig Constructor");
if (env == nullptr) {
WVLOG_E("env is nullptr");
return;
}
}
ProxyChangedCallbackImpl::~ProxyChangedCallbackImpl()
{
if (env_ && callback_) {
if (env_->GlobalReference_Delete(callback_) != ANI_OK) {
WVLOG_E("delete global reference failed");
}
}
}
void ProxyChangedCallbackImpl::OnChanged()
{
ani_ref jsCallback = nullptr;
ani_ref result;
if (env_ == nullptr) {
WVLOG_E("env is nullptr");
return;
}
if (env_->GlobalReference_Create(callback_, &jsCallback) != ANI_OK) {
WVLOG_E("create global reference object failed");
return;
}
auto status = env_->FunctionalObject_Call(static_cast<ani_fn_object>(jsCallback), ZERO, nullptr, &result);
if (status != ANI_OK) {
WVLOG_E("onChanged functionalObject_Call status: %{public}d", status);
return;
}
env_->GlobalReference_Delete(jsCallback);
}
void InnerApplyProxyOverride(ProxyConfig *proxyConfig, ani_env *env, ani_ref jsCallback)
{
std::vector<std::string> proxyUrls;
std::vector<std::string> proxySchemeFilters;
std::vector<std::string> bypassRules;
bool reverseBypass;
if (!proxyConfig) {
WVLOG_E("proxyConfig is nullptr");
return;
}
for (auto proxyRule : proxyConfig->GetProxyRules()) {
proxyUrls.push_back(proxyRule.GetUrl());
std::string proxySchemeFilter = "*";
switch (proxyRule.GetSchemeFilter()) {
case static_cast<int32_t>(ProxySchemeFilter::MATCH_ALL_SCHEMES):
break;
case static_cast<int32_t>(ProxySchemeFilter::MATCH_HTTP):
proxySchemeFilter = "http";
break;
case static_cast<int32_t>(ProxySchemeFilter::MATCH_HTTPS):
proxySchemeFilter = "https";
break;
}
proxySchemeFilters.push_back(proxySchemeFilter);
}
reverseBypass = proxyConfig->IsReverseBypassEnabled();
WVLOG_I("ProxyConfig reverse bypass %{public}d", reverseBypass);
for (auto bypassRule :proxyConfig->GetBypassRules()) {
WVLOG_I("ProxyConfig add bypass rule %{public}s", bypassRule.c_str());
bypassRules.push_back(bypassRule);
}
auto resultCallback = std::make_shared<ProxyChangedCallbackImpl>(env, jsCallback);
NWebHelper::Instance().SetProxyOverride(proxyUrls, proxySchemeFilters, bypassRules,
reverseBypass, resultCallback);
}
static void JsApplyProxyOverride(ani_env* env, ani_object object, ani_object proxyConfigObject, ani_fn_object callback)
{
WVLOG_D("ProxyConfig JsApplyProxyOverride Start.");
if (env == nullptr) {
WVLOG_E("ProxyConfig env is nullptr");
return;
}
ani_ref jsCallback = nullptr;
env->GlobalReference_Create(callback, &jsCallback);
if (!jsCallback) {
NWebError::AniBusinessError::ThrowErrorByErrCode(env, NWebError::PARAM_CHECK_ERROR);
return;
}
ProxyConfig* proxyConfig = nullptr;
ani_long thisVar;
ani_status status = env->Object_GetFieldByName_Long(proxyConfigObject, "nativePtr", &thisVar);
if (status != ANI_OK) {
WVLOG_E("AniUtils_Unwrap Object_GetFieldByName_Long status: %{public}d", status);
return;
}
proxyConfig = reinterpret_cast<ProxyConfig*>(thisVar);
if (!proxyConfig) {
WVLOG_E("ProxyConfig JsApplyProxyOverride proxyConfig is null");
return;
}
InnerApplyProxyOverride(proxyConfig, env, jsCallback);
return;
}
static void JsRemoveProxyOverride(ani_env *env, ani_object object, ani_fn_object callback)
{
WVLOG_D("ProxyConfig JsRemoveProxyOverride.");
if (env == nullptr) {
WVLOG_E("ProxyConfig env is nullptr");
return;
}
ani_ref jsCallback = nullptr;
env->GlobalReference_Create(callback, &jsCallback);
if (!jsCallback) {
NWebError::AniBusinessError::ThrowErrorByErrCode(env, NWebError::PARAM_CHECK_ERROR);
return;
}
auto resultCallback = std::make_shared<ProxyChangedCallbackImpl>(env, jsCallback);
NWebHelper::Instance().RemoveProxyOverride(resultCallback);
return;
}
ani_status StsProxyControllerInit(ani_env *env)
{
if (env == nullptr) {
WVLOG_E("env is nullptr");
return ANI_ERROR;
}
ani_class proxyControllerCls = nullptr;
ani_status status = env->FindClass(WEB_PROXY_CONTROLLER_CLASS_NAME, &proxyControllerCls);
if (status != ANI_OK || !proxyControllerCls) {
WVLOG_E("find %{public}s class failed, status: %{public}d", WEB_PROXY_CONTROLLER_CLASS_NAME, status);
return ANI_ERROR;
}
std::array instanceMethods = {
ani_native_function { "<ctor>", nullptr, reinterpret_cast<void*>(JsConstructor) },
};
status = env->Class_BindNativeMethods(proxyControllerCls, instanceMethods.data(), instanceMethods.size());
if (status != ANI_OK) {
WVLOG_E("Class_BindNativeMethods failed status: %{public}d", status);
return status;
}
std::array staticMethods = {
ani_native_function { "applyProxyOverride", nullptr, reinterpret_cast<void *>(JsApplyProxyOverride) },
ani_native_function { "removeProxyOverride", nullptr, reinterpret_cast<void *>(JsRemoveProxyOverride) },
};
status = env->Class_BindStaticNativeMethods(proxyControllerCls, staticMethods.data(), staticMethods.size());
if (status != ANI_OK) {
WVLOG_E("Class_BindStaticNativeMethods failed status: %{public}d", status);
}
return status;
}
}
}