* Copyright (c) 2023 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 "js_component_snapshot.h"
#include "interfaces/napi/kits/utils/napi_utils.h"
#ifdef PIXEL_MAP_SUPPORTED
#include "pixel_map.h"
#include "pixel_map_napi.h"
#endif
#include "core/common/ace_engine.h"
#include "core/common/container_scope.h"
#include "frameworks/bridge/common/utils/engine_helper.h"
namespace OHOS::Ace::Napi {
namespace {
struct SnapshotAsyncCtx {
napi_env env = nullptr;
napi_deferred deferred = nullptr;
napi_ref callbackRef = nullptr;
std::shared_ptr<Media::PixelMap> pixmap = nullptr;
int32_t errCode = -1;
int32_t instanceId = -1;
};
constexpr int32_t GETWITHRANGE_ISSTARTRECT_NUMBER = 2;
constexpr int32_t GETWITHRANGE_ARGV_VALUE = 4;
constexpr int32_t GETWITHRANGE_OPTIONS_NUMBER = 3;
void OnComplete(SnapshotAsyncCtx* asyncCtx, std::function<void()> finishCallback)
{
auto container = AceEngine::Get().GetContainer(asyncCtx->instanceId);
if (!container) {
LOGW("container is null. %{public}d", asyncCtx->instanceId);
if (asyncCtx) {
delete asyncCtx;
}
return;
}
auto taskExecutor = container->GetTaskExecutor();
if (!taskExecutor) {
LOGW("taskExecutor is null.");
if (asyncCtx) {
delete asyncCtx;
}
return;
}
taskExecutor->PostTask(
[asyncCtx, finishCallback]() {
std::unique_ptr<SnapshotAsyncCtx> ctx(asyncCtx);
napi_handle_scope scope = nullptr;
napi_open_handle_scope(ctx->env, &scope);
napi_value result[2] = { nullptr };
napi_get_undefined(ctx->env, &result[0]);
napi_get_undefined(ctx->env, &result[1]);
if (ctx->errCode == ERROR_CODE_NO_ERROR) {
#ifdef PIXEL_MAP_SUPPORTED
result[1] = Media::PixelMapNapi::CreatePixelMap(ctx->env, ctx->pixmap);
#endif
}
napi_create_int32(ctx->env, ctx->errCode, &result[0]);
if (ctx->deferred) {
if (ctx->errCode == ERROR_CODE_NO_ERROR) {
napi_resolve_deferred(ctx->env, ctx->deferred, result[1]);
} else {
napi_reject_deferred(ctx->env, ctx->deferred, result[0]);
}
} else {
napi_value ret = nullptr;
napi_value napiCallback = nullptr;
napi_get_reference_value(ctx->env, ctx->callbackRef, &napiCallback);
napi_call_function(ctx->env, nullptr, napiCallback, 2, result, &ret);
napi_delete_reference(ctx->env, ctx->callbackRef);
}
napi_close_handle_scope(ctx->env, scope);
if (finishCallback) {
finishCallback();
}
},
TaskExecutor::TaskType::JS, "ArkUIComponentSnapshotComplete");
}
}
JsComponentSnapshot::JsComponentSnapshot(napi_env env, napi_callback_info info) : env_(env), argc_(ARGC_MAX)
{
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc_, argv_, &thisVar, &data);
}
bool JsComponentSnapshot::CheckArgs(napi_valuetype firstArgType)
{
size_t minArgc = 1;
std::string errorMsg;
if (argc_ < minArgc) {
errorMsg = "The number of parameters must be greater than or equal to 1.";
LOGE("%{public}s", errorMsg.c_str());
NapiThrow(env_, errorMsg, ERROR_CODE_PARAM_INVALID);
return false;
}
if (argc_ > ARGC_MAX) {
errorMsg = "The largest number of parameters is 2.";
LOGE("%{public}s", errorMsg.c_str());
NapiThrow(env_, errorMsg, ERROR_CODE_PARAM_INVALID);
return false;
}
napi_valuetype type = napi_undefined;
napi_typeof(env_, argv_[0], &type);
if (type != firstArgType) {
errorMsg = "parameter id is not of type string";
LOGE("%{public}s", errorMsg.c_str());
NapiThrow(env_, errorMsg, ERROR_CODE_PARAM_INVALID);
return false;
}
return true;
}
auto JsComponentSnapshot::CreateCallback(napi_value* result)
{
auto* asyncCtx = new SnapshotAsyncCtx;
napi_valuetype type = napi_undefined;
if (argc_ >= 2) {
napi_typeof(env_, argv_[1], &type);
if (type == napi_function) {
napi_create_reference(env_, argv_[1], 1, &asyncCtx->callbackRef);
}
}
if (!asyncCtx->callbackRef) {
napi_create_promise(env_, &asyncCtx->deferred, result);
} else {
napi_get_undefined(env_, result);
}
asyncCtx->env = env_;
asyncCtx->instanceId = Container::CurrentIdSafely();
return [asyncCtx](std::shared_ptr<Media::PixelMap> pixmap, int32_t errCode, std::function<void()> finishCallback) {
asyncCtx->pixmap = std::move(pixmap);
asyncCtx->errCode = errCode;
OnComplete(asyncCtx, finishCallback);
};
}
napi_value JsComponentSnapshot::GetArgv(int32_t idx)
{
if (idx >= ARGC_MAX) {
return nullptr;
}
return argv_[idx];
}
void JsComponentSnapshot::ParseParamForBuilder(NG::SnapshotParam& param)
{
if (argc_ >= 2) {
napi_valuetype type = napi_undefined;
napi_typeof(env_, argv_[1], &type);
if (type != napi_function) {
ParseParam(1, param);
}
}
if (argc_ >= 3) {
ParseParam(2, param);
}
if (argc_ >= 4) {
ParseParam(3, param);
}
if (argc_ == 5) {
ParseParam(4, param);
}
}
void JsComponentSnapshot::ParseParamForGet(NG::SnapshotOptions& options)
{
if (argc_ >= 2) {
napi_valuetype type = napi_undefined;
napi_typeof(env_, argv_[1], &type);
if (type != napi_function) {
ParseOptions(1, options);
}
}
if (argc_ == 3) {
ParseOptions(2, options);
}
if (argc_ == GETWITHRANGE_ARGV_VALUE) {
ParseOptions(GETWITHRANGE_OPTIONS_NUMBER, options);
}
}
void JsComponentSnapshot::ParseParam(int32_t idx, NG::SnapshotParam& param)
{
if (static_cast<int32_t>(argc_) <= idx) {
return;
}
napi_valuetype type = napi_undefined;
napi_typeof(env_, argv_[idx], &type);
if (type == napi_number) {
int32_t delayTime = 0;
napi_get_value_int32(env_, argv_[idx], &delayTime);
if (delayTime >= 0) {
param.delay = delayTime;
}
}
if (type == napi_boolean) {
bool checkImageStatus = 0;
napi_get_value_bool(env_, argv_[idx], &checkImageStatus);
param.checkImageStatus = checkImageStatus;
}
if (type == napi_object) {
NG::SnapshotOptions options;
ParseOptions(idx, options);
param.options = options;
}
}
void JsComponentSnapshot::ParseOptions(int32_t idx, NG::SnapshotOptions& options)
{
if (static_cast<int32_t>(argc_) <= idx) {
return;
}
napi_valuetype type = napi_undefined;
napi_typeof(env_, argv_[idx], &type);
if (type != napi_object) {
return;
}
bool result = false;
napi_has_named_property(env_, argv_[idx], "scale", &result);
if (result) {
napi_value scaleNapi = nullptr;
napi_get_named_property(env_, argv_[idx], "scale", &scaleNapi);
double scale = 0.0;
napi_get_value_double(env_, scaleNapi, &scale);
if (GreatNotEqual(scale, 0.0)) {
options.scale = static_cast<float>(scale);
}
}
result = false;
napi_has_named_property(env_, argv_[idx], "waitUntilRenderFinished", &result);
if (result) {
napi_value waitUntilRenderFinishedNapi = nullptr;
napi_get_named_property(env_, argv_[idx], "waitUntilRenderFinished", &waitUntilRenderFinishedNapi);
bool waitUntilRenderFinished = false;
napi_get_value_bool(env_, waitUntilRenderFinishedNapi, &waitUntilRenderFinished);
options.waitUntilRenderFinished = waitUntilRenderFinished;
}
result = false;
napi_has_named_property(env_, argv_[idx], "colorMode", &result);
if (result) {
napi_value colorModeObject = nullptr;
napi_get_named_property(env_, argv_[idx], "colorMode", &colorModeObject);
ParseColorMode(&colorModeObject, options);
}
result = false;
napi_has_named_property(env_, argv_[idx], "dynamicRangeMode", &result);
if (result) {
napi_value dynamicRangeModeObject = nullptr;
napi_get_named_property(env_, argv_[idx], "dynamicRangeMode", &dynamicRangeModeObject);
ParseDynamicRangeMode(&dynamicRangeModeObject, options);
}
result = false;
napi_has_named_property(env_, argv_[idx], "region", &result);
if (!result) {
options.regionMode = NG::SnapshotRegionMode::NO_REGION;
return;
}
napi_value regionObject = nullptr;
napi_get_named_property(env_, argv_[idx], "region", ®ionObject);
if (!regionObject) {
options.regionMode = NG::SnapshotRegionMode::NO_REGION;
return;
}
options.regionMode = NG::SnapshotRegionMode::COMMON;
if (ParseLocalizedRegion(®ionObject, options)) {
options.regionMode = NG::SnapshotRegionMode::LOCALIZED;
} else {
ParseRegion(®ionObject, options);
}
}
bool JsComponentSnapshot::ParseRegion(napi_value* regionObject, NG::SnapshotOptions& options)
{
bool getReigonResult = false;
options.snapshotRegion = NG::LocalizedSnapshotRegion {};
napi_has_named_property(env_, *regionObject, "left", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"left\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value leftPxNapi;
napi_get_named_property(env_, *regionObject, "left", &leftPxNapi);
napi_get_value_double(env_, leftPxNapi, &options.snapshotRegion.start);
napi_has_named_property(env_, *regionObject, "right", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"right\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value rightPxNapi;
napi_get_named_property(env_, *regionObject, "right", &rightPxNapi);
napi_get_value_double(env_, rightPxNapi, &options.snapshotRegion.end);
napi_has_named_property(env_, *regionObject, "top", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"top\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value topPxNapi;
napi_get_named_property(env_, *regionObject, "top", &topPxNapi);
napi_get_value_double(env_, topPxNapi, &options.snapshotRegion.top);
napi_has_named_property(env_, *regionObject, "bottom", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"bottom\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value bottomPxNapi;
napi_get_named_property(env_, *regionObject, "bottom", &bottomPxNapi);
napi_get_value_double(env_, bottomPxNapi, &options.snapshotRegion.bottom);
return true;
}
bool JsComponentSnapshot::ParseLocalizedRegion(napi_value* regionObject, NG::SnapshotOptions& options)
{
options.snapshotRegion = NG::LocalizedSnapshotRegion {};
bool getReigonResult = false;
napi_has_named_property(env_, *regionObject, "start", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"start\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value startPxNapi;
napi_get_named_property(env_, *regionObject, "start", &startPxNapi);
napi_get_value_double(env_, startPxNapi, &options.snapshotRegion.start);
napi_has_named_property(env_, *regionObject, "end", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"end\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value endPxNapi;
napi_get_named_property(env_, *regionObject, "end", &endPxNapi);
napi_get_value_double(env_, endPxNapi, &options.snapshotRegion.end);
napi_has_named_property(env_, *regionObject, "top", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"top\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value topPxNapi;
napi_get_named_property(env_, *regionObject, "top", &topPxNapi);
napi_get_value_double(env_, topPxNapi, &options.snapshotRegion.top);
napi_has_named_property(env_, *regionObject, "bottom", &getReigonResult);
if (!getReigonResult) {
LOGE("The \"bottom\" attribute cannot be obtained from the parameter.");
return false;
}
napi_value bottomPxNapi;
napi_get_named_property(env_, *regionObject, "bottom", &bottomPxNapi);
napi_get_value_double(env_, bottomPxNapi, &options.snapshotRegion.bottom);
return true;
}
void JsComponentSnapshot::ParseColorMode(napi_value* colorModeObject, NG::SnapshotOptions& options)
{
bool getColorModeResult = false;
napi_has_named_property(env_, *colorModeObject, "colorSpace", &getColorModeResult);
if (getColorModeResult) {
napi_value colorSpaceNapi = nullptr;
auto colorSpaceMode = NG::DEFAULT_COLORSPACE_VALUE_SRGB;
napi_get_named_property(env_, *colorModeObject, "colorSpace", &colorSpaceNapi);
if (colorSpaceNapi) {
napi_get_value_uint32(env_, colorSpaceNapi, &colorSpaceMode);
}
options.colorSpaceModeOptions.colorSpaceMode = static_cast<NG::ColorSpaceMode>(colorSpaceMode);
}
napi_has_named_property(env_, *colorModeObject, "isAuto", &getColorModeResult);
if (getColorModeResult) {
napi_value isAutoNapi = nullptr;
bool isAuto = false;
napi_get_named_property(env_, *colorModeObject, "isAuto", &isAutoNapi);
if (isAutoNapi) {
napi_get_value_bool(env_, isAutoNapi, &isAuto);
}
options.colorSpaceModeOptions.isAuto = isAuto;
}
}
void JsComponentSnapshot::ParseDynamicRangeMode(napi_value* dynamicRangeModeObject, NG::SnapshotOptions& options)
{
bool getDynamicRangeModeResult = false;
napi_has_named_property(env_, *dynamicRangeModeObject, "dynamicRangeMode", &getDynamicRangeModeResult);
if (getDynamicRangeModeResult) {
napi_value dynamicRangeNapi = nullptr;
napi_get_named_property(env_, *dynamicRangeModeObject, "dynamicRangeMode", &dynamicRangeNapi);
auto dynamicRangeMode = NG::DEFAULT_DYNAMICRANGE_VALUE_STANDARD;
if (dynamicRangeNapi) {
napi_get_value_uint32(env_, dynamicRangeNapi, &dynamicRangeMode);
}
options.dynamicRangeModeOptions.dynamicRangeMode = static_cast<NG::DynamicRange>(dynamicRangeMode);
}
napi_has_named_property(env_, *dynamicRangeModeObject, "isAuto", &getDynamicRangeModeResult);
if (getDynamicRangeModeResult) {
napi_value isAutoNapi = nullptr;
bool isAuto = false;
napi_get_named_property(env_, *dynamicRangeModeObject, "isAuto", &isAutoNapi);
if (isAutoNapi) {
napi_get_value_bool(env_, isAutoNapi, &isAuto);
}
options.dynamicRangeModeOptions.isAuto = isAuto;
}
}
static void HandleSyncSnapshotResult(
napi_env env, const std::pair<int32_t, std::shared_ptr<Media::PixelMap>>& pair, napi_value& result)
{
switch (pair.first) {
case ERROR_CODE_NO_ERROR:
#ifdef PIXEL_MAP_SUPPORTED
result = Media::PixelMapNapi::CreatePixelMap(env, pair.second);
#endif
break;
case ERROR_CODE_INTERNAL_ERROR:
napi_get_null(env, &result);
NapiThrow(env, "Internal error!", ERROR_CODE_INTERNAL_ERROR);
break;
case ERROR_CODE_COMPONENT_SNAPSHOT_TIMEOUT:
napi_get_null(env, &result);
NapiThrow(env, "ComponentSnapshot timeout!", ERROR_CODE_COMPONENT_SNAPSHOT_TIMEOUT);
break;
case ERROR_CODE_PARAM_INVALID:
napi_get_null(env, &result);
NapiThrow(env, "Snapshot region is invalid or out of range!", ERROR_CODE_PARAM_INVALID);
break;
case ERROR_CODE_COMPONENT_SNAPSHOT_MODE_NOT_SUPPORTED:
napi_get_null(env, &result);
NapiThrow(env, "Unsupported color space or dynamic range mode in snapshot options!",
ERROR_CODE_COMPONENT_SNAPSHOT_MODE_NOT_SUPPORTED);
break;
case ERROR_CODE_COMPONENT_SNAPSHOT_AUTO_NOT_SUPPORTED:
napi_get_null(env, &result);
NapiThrow(env, "isAuto(true) is not supported for offscreen node snapshots!",
ERROR_CODE_COMPONENT_SNAPSHOT_AUTO_NOT_SUPPORTED);
break;
}
}
static napi_value JSSnapshotGet(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_value result = nullptr;
if (!helper.CheckArgs(napi_valuetype::napi_string)) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of string type.");
napi_close_escapable_handle_scope(env, scope);
return result;
}
std::string componentId;
napi_valuetype valueType = napi_null;
GetNapiString(env, helper.GetArgv(0), componentId, valueType);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT,
"Can't get delegate of ace_engine. param: " SEC_PLD(%{public}s),
SEC_PARAM(componentId.c_str()));
auto callback = helper.CreateCallback(&result);
callback(nullptr, ERROR_CODE_INTERNAL_ERROR, nullptr);
napi_close_escapable_handle_scope(env, scope);
return result;
}
NG::SnapshotOptions options;
helper.ParseParamForGet(options);
delegate->GetSnapshot(componentId, helper.CreateCallback(&result), options);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value JSSnapshotFromBuilder(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
if (!helper.CheckArgs(napi_valuetype::napi_function)) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of function type.");
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
napi_value result = nullptr;
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Can't get delegate of ace_engine. ");
auto callback = helper.CreateCallback(&result);
callback(nullptr, ERROR_CODE_INTERNAL_ERROR, nullptr);
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
auto builder = [build = helper.GetArgv(0), env] { napi_call_function(env, nullptr, build, 0, nullptr, nullptr); };
NG::SnapshotParam param;
helper.ParseParamForBuilder(param);
if (param.options.colorSpaceModeOptions.isAuto || param.options.dynamicRangeModeOptions.isAuto) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT,
"isAuto(true) is not supported for offscreen node snapshots.");
auto callback = helper.CreateCallback(&result);
callback(nullptr, ERROR_CODE_COMPONENT_SNAPSHOT_AUTO_NOT_SUPPORTED, nullptr);
napi_close_escapable_handle_scope(env, scope);
return result;
}
delegate->CreateSnapshot(builder, helper.CreateCallback(&result), true, param);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value JSSnapshotGetSync(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_value result = nullptr;
if (!helper.CheckArgs(napi_valuetype::napi_string)) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of string type.");
napi_close_escapable_handle_scope(env, scope);
return result;
}
std::string componentId;
napi_valuetype valueType = napi_null;
GetNapiString(env, helper.GetArgv(0), componentId, valueType);
auto reason = ContainerScope::CurrentIdWithReason().second;
auto instanceId = Container::CurrentIdSafely();
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Can't get delegate of ace_engine. ");
std::string message = AceEngine::GetEnhancedContextBNotFoundMessage(
reason, instanceId);
NapiThrow(env, "Delegate is null. " + message, ERROR_CODE_INTERNAL_ERROR);
napi_close_escapable_handle_scope(env, scope);
return result;
}
NG::SnapshotOptions options;
helper.ParseParamForGet(options);
auto pair = delegate->GetSyncSnapshot(componentId, options);
HandleSyncSnapshotResult(env, pair, result);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value JSSnapshotGetWithUniqueId(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_value result = nullptr;
napi_valuetype type = napi_undefined;
napi_typeof(env, helper.GetArgv(0), &type);
if (type != napi_valuetype::napi_number) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of number type.");
NapiThrow(env, "parameter uniqueId is not of type number", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return result;
}
int32_t uniqueId;
napi_get_value_int32(env, helper.GetArgv(0), &uniqueId);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT,
"Can't get delegate of ace_engine. param: %{public}d",
uniqueId);
auto callback = helper.CreateCallback(&result);
callback(nullptr, ERROR_CODE_INTERNAL_ERROR, nullptr);
napi_close_escapable_handle_scope(env, scope);
return result;
}
NG::SnapshotOptions options;
helper.ParseParamForGet(options);
delegate->GetSnapshotByUniqueId(uniqueId, helper.CreateCallback(&result), options);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value JSSnapshotGetSyncWithUniqueId(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_value result = nullptr;
napi_valuetype type = napi_undefined;
napi_typeof(env, helper.GetArgv(0), &type);
if (type != napi_valuetype::napi_number) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of number type.");
NapiThrow(env, "parameter uniqueId is not of type number", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return result;
}
int32_t uniqueId;
napi_get_value_int32(env, helper.GetArgv(0), &uniqueId);
auto reason = ContainerScope::CurrentIdWithReason().second;
auto instanceId = Container::CurrentIdSafely();
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT,
"Can't get delegate of ace_engine. param: %{public}d", uniqueId);
std::string message = AceEngine::GetEnhancedContextBNotFoundMessage(
reason, instanceId);
NapiThrow(env, "Delegate is null. " + message, ERROR_CODE_INTERNAL_ERROR);
napi_close_escapable_handle_scope(env, scope);
return result;
}
NG::SnapshotOptions options;
helper.ParseParamForGet(options);
auto pair = delegate->GetSyncSnapshotByUniqueId(uniqueId, options);
HandleSyncSnapshotResult(env, pair, result);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value JSSnapshotFromComponent(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
if (!helper.CheckArgs(napi_valuetype::napi_object)) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing the first argument failed, not of object type.");
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
napi_value result = nullptr;
auto reason = ContainerScope::CurrentIdWithReason().second;
auto instanceId = Container::CurrentIdSafely();
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Can't get delegate of ace_engine. ");
std::string message = AceEngine::GetEnhancedContextBNotFoundMessage(
reason, instanceId);
NapiThrow(env, "Delegate is null. " + message, ERROR_CODE_INTERNAL_ERROR);
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
napi_value frameNodePtr = nullptr;
auto componentResult = napi_get_named_property(env, helper.GetArgv(0), "nodePtr_", &frameNodePtr);
if (componentResult != napi_ok) {
NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
void* nativePtr = nullptr;
componentResult = napi_get_value_external(env, frameNodePtr, &nativePtr);
if (componentResult != napi_ok) {
NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
if (!nativePtr) {
NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return nullptr;
}
WeakPtr<NG::UINode> nodeWk;
auto* uiNodePtr = reinterpret_cast<OHOS::Ace::NG::UINode*>(nativePtr);
nodeWk = AceType::WeakClaim(uiNodePtr);
NG::SnapshotParam param;
helper.ParseParamForBuilder(param);
if (param.options.colorSpaceModeOptions.isAuto || param.options.dynamicRangeModeOptions.isAuto) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT,
"isAuto(true) is not supported for offscreen node snapshots.");
auto callback = helper.CreateCallback(&result);
callback(nullptr, ERROR_CODE_COMPONENT_SNAPSHOT_AUTO_NOT_SUPPORTED, nullptr);
napi_close_escapable_handle_scope(env, scope);
return result;
}
delegate->CreateSnapshotFromComponent(nodeWk.Upgrade(), helper.CreateCallback(&result), false, param);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
bool JudgeRangeType(napi_env env, napi_callback_info info, int32_t argNum)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_valuetype type = napi_undefined;
napi_value argv = helper.GetArgv(argNum);
if (argv == nullptr) {
napi_close_escapable_handle_scope(env, scope);
return false;
}
napi_typeof(env, argv, &type);
if (type != napi_valuetype::napi_number && type != napi_valuetype::napi_string) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing argument failed, not of number or string type.");
NapiThrow(env, "parameter uniqueId is not of type number or string", ERROR_CODE_PARAM_INVALID);
napi_close_escapable_handle_scope(env, scope);
return false;
}
napi_close_escapable_handle_scope(env, scope);
return true;
}
bool JudgeRectValue(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_valuetype type = napi_undefined;
napi_value argv = helper.GetArgv(GETWITHRANGE_ISSTARTRECT_NUMBER);
if (argv == nullptr) {
napi_close_escapable_handle_scope(env, scope);
return true;
}
napi_typeof(env, argv, &type);
bool isRect = true;
if (type == napi_valuetype::napi_boolean) {
napi_get_value_bool(env, argv, &isRect);
}
napi_close_escapable_handle_scope(env, scope);
return isRect;
}
NG::NodeIdentity GetNodeIdentity(napi_env env, napi_callback_info info, int32_t index)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
NG::NodeIdentity nodeIdentity;
napi_valuetype type = napi_undefined;
napi_value argv = helper.GetArgv(index);
if (argv == nullptr) {
napi_close_escapable_handle_scope(env, scope);
return nodeIdentity;
}
napi_typeof(env, argv, &type);
if (type == napi_valuetype::napi_number) {
napi_get_value_int32(env, argv, &nodeIdentity.second);
} else {
napi_valuetype valueType = napi_null;
GetNapiString(env, argv, nodeIdentity.first, valueType);
}
napi_close_escapable_handle_scope(env, scope);
return nodeIdentity;
}
static napi_value JSSnapshotGetSizeLimitation(napi_env env, napi_callback_info info)
{
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
auto currentIdAndReason = ContainerScope::CurrentIdWithReason();
std::string message = AceEngine::GetEnhancedContextBNotFoundMessage(
currentIdAndReason.second, Container::CurrentIdSafely());
NapiThrow(env, "Delegate is null. " + message, ERROR_CODE_INTERNAL_ERROR);
return nullptr;
}
auto limitation = delegate->GetSizeLimitation();
napi_value result = nullptr;
napi_status status = napi_create_object(env, &result);
if (status != napi_ok) {
NapiThrow(env, "Failed to create result object", ERROR_CODE_INTERNAL_ERROR);
return nullptr;
}
napi_value maxWidthVal = nullptr;
napi_value maxHeightVal = nullptr;
if (napi_create_int32(env, limitation.maxWidth, &maxWidthVal) != napi_ok ||
napi_create_int32(env, limitation.maxHeight, &maxHeightVal) != napi_ok) {
NapiThrow(env, "Failed to create int values", ERROR_CODE_INTERNAL_ERROR);
return nullptr;
}
napi_set_named_property(env, result, "maxWidth", maxWidthVal);
napi_set_named_property(env, result, "maxHeight", maxHeightVal);
return result;
}
static napi_value JSSnapshotGetWithRange(napi_env env, napi_callback_info info)
{
napi_escapable_handle_scope scope = nullptr;
napi_open_escapable_handle_scope(env, &scope);
JsComponentSnapshot helper(env, info);
napi_value result = nullptr;
if (!JudgeRangeType(env, info, 0) || !JudgeRangeType(env, info, 1)) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Parsing argument failed, not of number or string type.");
napi_close_escapable_handle_scope(env, scope);
return result;
}
auto startID = GetNodeIdentity(env, info, 0);
auto endID = GetNodeIdentity(env, info, 1);
bool isStartRect = JudgeRectValue(env, info);
auto reason = ContainerScope::CurrentIdWithReason().second;
auto instanceId = Container::CurrentIdSafely();
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
TAG_LOGW(AceLogTag::ACE_COMPONENT_SNAPSHOT, "Can't get delegate of ace_engine. ");
std::string message = AceEngine::GetEnhancedContextBNotFoundMessage(
reason, instanceId);
NapiThrow(env, "Delegate is null. " + message, ERROR_CODE_INTERNAL_ERROR);
napi_close_escapable_handle_scope(env, scope);
return result;
}
NG::SnapshotOptions options;
helper.ParseParamForGet(options);
delegate->GetSnapshotWithRange(startID, endID, isStartRect, helper.CreateCallback(&result), options);
napi_escape_handle(env, scope, result, &result);
napi_close_escapable_handle_scope(env, scope);
return result;
}
static napi_value ComponentSnapshotExport(napi_env env, napi_value exports)
{
napi_property_descriptor snapshotDesc[] = {
DECLARE_NAPI_FUNCTION("get", JSSnapshotGet),
DECLARE_NAPI_FUNCTION("createFromBuilder", JSSnapshotFromBuilder),
DECLARE_NAPI_FUNCTION("getSync", JSSnapshotGetSync),
DECLARE_NAPI_FUNCTION("getWithUniqueId", JSSnapshotGetWithUniqueId),
DECLARE_NAPI_FUNCTION("getSyncWithUniqueId", JSSnapshotGetSyncWithUniqueId),
DECLARE_NAPI_FUNCTION("createFromComponent", JSSnapshotFromComponent),
DECLARE_NAPI_FUNCTION("getWithRange", JSSnapshotGetWithRange),
DECLARE_NAPI_FUNCTION("getSizeLimitation", JSSnapshotGetSizeLimitation),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(snapshotDesc) / sizeof(snapshotDesc[0]), snapshotDesc));
return exports;
}
static napi_module snapshotModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = ComponentSnapshotExport,
.nm_modname = "arkui.componentSnapshot",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void ComponentSnapshotRegister()
{
napi_module_register(&snapshotModule);
}
}