* Copyright (c) 2024-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 <optional>
#include <vector>
#include "base/log/log.h"
#include "platformview_model_ng.h"
#include "platformview_napi.h"
#include "plugins/interfaces/native/inner_api/plugin_utils_napi.h"
#include "core/components_ng/base/view_stack_processor.h"
extern const char _binary_platformview_js_start[];
extern const char _binary_platformview_abc_start[];
#if !defined(IOS_PLATFORM)
extern const char _binary_platformview_js_end[];
extern const char _binary_platformview_abc_end[];
#else
extern const char* _binary_platformview_js_end;
extern const char* _binary_platformview_abc_end;
#endif
using namespace OHOS::Ace;
std::unique_ptr<NG::PlatformViewModelNG> NG::PlatformViewModelNG::instance_ = nullptr;
std::mutex NG::PlatformViewModelNG::mutex_;
namespace {
std::string GetStringFromNapiValue(napi_env env, napi_value value, const std::string& defaultValue)
{
NAPI_ASSERT_BASE(env, value != nullptr, "value is null", defaultValue);
napi_valuetype valueType = napi_undefined;
NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), defaultValue);
NAPI_ASSERT_BASE(env, valueType != napi_undefined && valueType != napi_null,
"value is undefined or null", defaultValue);
NAPI_ASSERT_BASE(env, valueType == napi_number || valueType == napi_string,
"value must be a number or string", defaultValue);
napi_value coerced = nullptr;
NAPI_CALL_BASE(env, napi_coerce_to_string(env, value, &coerced), defaultValue);
return OHOS::Plugin::PluginUtilsNApi::GetStringFromValueUtf8(env, coerced);
}
bool TryGetStringProperty(napi_env env, napi_value obj, const char* name, std::string& out)
{
bool hasProp = false;
if (napi_has_named_property(env, obj, name, &hasProp) != napi_ok || !hasProp) {
return false;
}
napi_value value = nullptr;
if (napi_get_named_property(env, obj, name, &value) != napi_ok) {
return false;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, value, &valueType) != napi_ok || valueType != napi_string) {
return false;
}
out = OHOS::Plugin::PluginUtilsNApi::GetStringFromValueUtf8(env, value);
return true;
}
bool TryGetInt32Property(napi_env env, napi_value obj, const char* name, int32_t& out)
{
bool hasProp = false;
if (napi_has_named_property(env, obj, name, &hasProp) != napi_ok || !hasProp) {
return false;
}
napi_value value = nullptr;
if (napi_get_named_property(env, obj, name, &value) != napi_ok) {
return false;
}
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, value, &valueType) != napi_ok || valueType != napi_number) {
return false;
}
return napi_get_value_int32(env, value, &out) == napi_ok;
}
}
NG::PlatformViewModelNG* NG::PlatformViewModelNG::GetInstance()
{
if (!instance_) {
std::lock_guard<std::mutex> lock(mutex_);
if (!instance_) {
instance_.reset(new NG::PlatformViewModelNG());
}
}
return instance_.get();
}
namespace OHOS::Plugin::PlatformView {
namespace {
constexpr int32_t DEFAULT_PLATFORM_VIEW_TYPE = 0;
constexpr const char DEFAULT_CENTER_PERCENT[] = "50%";
}
napi_value JsCreate(napi_env env, napi_callback_info info)
{
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NAPI_ASSERT(env, argc >= PluginUtilsNApi::ARG_NUM_1, "Wrong number of arguments");
std::string id;
std::optional<std::string> data = std::nullopt;
int32_t type = DEFAULT_PLATFORM_VIEW_TYPE;
napi_value firstArg = argv[PluginUtilsNApi::ARG_NUM_0];
napi_valuetype firstType = napi_undefined;
NAPI_CALL(env, napi_typeof(env, firstArg, &firstType));
bool hasIdProperty = false;
if (firstType == napi_object) {
NAPI_CALL(env, napi_has_named_property(env, firstArg, "id", &hasIdProperty));
}
if (firstType == napi_object && hasIdProperty) {
NAPI_ASSERT(env, TryGetStringProperty(env, firstArg, "id", id), "Property 'id' must be a string");
std::string dataValue;
if (TryGetStringProperty(env, firstArg, "data", dataValue)) {
data = dataValue;
}
int32_t t = DEFAULT_PLATFORM_VIEW_TYPE;
if (TryGetInt32Property(env, firstArg, "type", t)) {
type = t;
}
} else {
NAPI_ASSERT(env, firstType == napi_string, "First argument must be a string or object with string 'id'");
id = PluginUtilsNApi::GetStringFromValueUtf8(env, firstArg);
if (argc >= PluginUtilsNApi::ARG_NUM_2) {
auto napiData = argv[PluginUtilsNApi::ARG_NUM_1];
napi_valuetype paramType;
napi_typeof(env, napiData, ¶mType);
if (paramType == napi_string) {
data = PluginUtilsNApi::GetStringFromValueUtf8(env, napiData);
}
}
}
NG::PlatformViewModelNG::GetInstance()->Create(id, type, data);
return PluginUtilsNApi::CreateNull(env);
}
napi_value JsScale(napi_env env, napi_callback_info info)
{
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NAPI_ASSERT(env, argc >= PluginUtilsNApi::ARG_NUM_1, "Wrong number of arguments");
napi_value x = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "x", env);
napi_value y = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "y", env);
napi_value z = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "z", env);
napi_value centerX = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "centerX", env);
napi_value centerY = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "centerY", env);
auto isUndefined = [env](napi_value value) {
return PluginUtilsNApi::IsMatchType(value, napi_undefined, env) == TYPE_CHECK_SUCCESS;
};
float fx = isUndefined(x) ? 1.0f : static_cast<float>(PluginUtilsNApi::GetDouble(env, x));
float fy = isUndefined(y) ? 1.0f : static_cast<float>(PluginUtilsNApi::GetDouble(env, y));
float fz = isUndefined(z) ? 1.0f : static_cast<float>(PluginUtilsNApi::GetDouble(env, z));
std::string centerXStr = GetStringFromNapiValue(env, centerX, "0");
std::string centerYStr = GetStringFromNapiValue(env, centerY, "0");
NG::PlatformViewModelNG::GetInstance()->SetScale(fx, fy, fz, centerXStr, centerYStr);
return PluginUtilsNApi::CreateNull(env);
}
napi_value JsRotate(napi_env env, napi_callback_info info)
{
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NAPI_ASSERT(env, argc >= PluginUtilsNApi::ARG_NUM_1, "Wrong number of arguments");
napi_value x = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "x", env);
napi_value y = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "y", env);
napi_value z = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "z", env);
napi_value angle = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "angle", env);
napi_value centerX = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "centerX", env);
napi_value centerY = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "centerY", env);
napi_value centerZ = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "centerZ", env);
napi_value perspective = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "perspective", env);
float fx = static_cast<float>(PluginUtilsNApi::GetDouble(env, x));
float fy = static_cast<float>(PluginUtilsNApi::GetDouble(env, y));
float fz = static_cast<float>(PluginUtilsNApi::GetDouble(env, z));
std::string fangleStr = GetStringFromNapiValue(env, angle, "");
std::string centerXStr = GetStringFromNapiValue(env, centerX, DEFAULT_CENTER_PERCENT);
std::string centerYStr = GetStringFromNapiValue(env, centerY, DEFAULT_CENTER_PERCENT);
std::string centerZStr = GetStringFromNapiValue(env, centerZ, "0");
std::string perspectiveStr = GetStringFromNapiValue(env, perspective, "");
NG::PlatformViewModelNG::GetInstance()->SetRotation(fx, fy, fz, fangleStr, centerXStr,
centerYStr, centerZStr, perspectiveStr);
return PluginUtilsNApi::CreateNull(env);
}
napi_value JsTranslate(napi_env env, napi_callback_info info)
{
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NAPI_ASSERT(env, argc >= PluginUtilsNApi::ARG_NUM_1, "Wrong number of arguments");
napi_value x = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "x", env);
napi_value y = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "y", env);
napi_value z = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "z", env);
std::string fx = GetStringFromNapiValue(env, x, "0");
std::string fy = GetStringFromNapiValue(env, y, "0");
std::string fz = GetStringFromNapiValue(env, z, "0");
NG::PlatformViewModelNG::GetInstance()->SetTranslate(fx, fy, fz);
return PluginUtilsNApi::CreateNull(env);
}
napi_value JsTransform(napi_env env, napi_callback_info info)
{
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NAPI_ASSERT(env, argc == PluginUtilsNApi::ARG_NUM_1, "Wrong number of arguments");
napi_value matrix4x4 = PluginUtilsNApi::GetNamedProperty(argv[PluginUtilsNApi::ARG_NUM_0], "matrix4x4", env);
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, matrix4x4, &valueType) != napi_ok || valueType != napi_object) {
return PluginUtilsNApi::CreateNull(env);
}
bool isArray = false;
if (napi_is_array(env, matrix4x4, &isArray) != napi_ok || !isArray) {
return PluginUtilsNApi::CreateNull(env);
}
uint32_t length = 0;
NAPI_CALL(env, napi_get_array_length(env, matrix4x4, &length));
constexpr uint32_t MATRIX4_LEN = 16;
if (length != MATRIX4_LEN) {
return PluginUtilsNApi::CreateNull(env);
}
std::vector<float> matrix;
matrix.reserve(MATRIX4_LEN);
for (uint32_t i = 0; i < MATRIX4_LEN; i++) {
napi_value item = nullptr;
NAPI_CALL(env, napi_get_element(env, matrix4x4, i, &item));
napi_valuetype valueType;
if (napi_typeof(env, item, &valueType) != napi_ok || valueType != napi_number) {
return PluginUtilsNApi::CreateNull(env);
}
matrix.push_back(static_cast<float>(PluginUtilsNApi::GetDouble(env, item)));
}
NG::PlatformViewModelNG::GetInstance()->SetTransformMatrix(matrix);
return PluginUtilsNApi::CreateNull(env);
}
napi_value ExportPlatformView(napi_env env, napi_value exports)
{
static napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("create", JsCreate),
DECLARE_NAPI_FUNCTION("scale", JsScale),
DECLARE_NAPI_FUNCTION("rotate", JsRotate),
DECLARE_NAPI_FUNCTION("translate", JsTranslate),
DECLARE_NAPI_FUNCTION("transform", JsTransform),
DECLARE_NAPI_FUNCTION("transform3D", JsTransform),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
}
extern "C" __attribute__((visibility("default"))) void NAPI_platformview_GetJSCode(const char** buf, int* bufLen)
{
if (buf != nullptr) {
*buf = _binary_platformview_js_start;
}
if (bufLen != nullptr) {
*bufLen = _binary_platformview_js_end - _binary_platformview_js_start;
}
}
extern "C" __attribute__((visibility("default"))) void NAPI_platformview_GetABCCode(const char** buf, int* buflen)
{
if (buf != nullptr) {
*buf = _binary_platformview_abc_start;
}
if (buflen != nullptr) {
*buflen = _binary_platformview_abc_end - _binary_platformview_abc_start;
}
}
static napi_module_with_js platfromViewModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = OHOS::Plugin::PlatformView::ExportPlatformView,
.nm_modname = "platformview",
.nm_priv = ((void*)0),
.nm_get_abc_code = NAPI_platformview_GetABCCode,
.nm_get_js_code = NAPI_platformview_GetJSCode,
};
extern "C" __attribute__((constructor)) void RegisterModulePlatformView()
{
napi_module_with_js_register(&platfromViewModule);
}