1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_component_snapshot.h"
17
18 #include "interfaces/napi/kits/utils/napi_utils.h"
19 #include "js_native_api.h"
20 #include "js_native_api_types.h"
21 #include "napi/native_common.h"
22 #include "node_api_types.h"
23 #ifdef PIXEL_MAP_SUPPORTED
24 #include "pixel_map.h"
25 #include "pixel_map_napi.h"
26 #endif
27
28 #include "node_api.h"
29
30 #include "bridge/common/utils/utils.h"
31 #include "core/common/ace_engine.h"
32 #include "frameworks/bridge/common/utils/engine_helper.h"
33
34 namespace OHOS::Ace::Napi {
35 namespace {
36 struct SnapshotAsyncCtx {
37 napi_env env = nullptr;
38 napi_deferred deferred = nullptr;
39 napi_ref callbackRef = nullptr;
40 std::shared_ptr<Media::PixelMap> pixmap = nullptr;
41 int32_t errCode = -1;
42 int32_t instanceId = -1;
43 };
44
OnComplete(SnapshotAsyncCtx * asyncCtx)45 void OnComplete(SnapshotAsyncCtx* asyncCtx)
46 {
47 auto container = AceEngine::Get().GetContainer(asyncCtx->instanceId);
48 if (!container) {
49 LOGW("container is null. %{public}d", asyncCtx->instanceId);
50 return;
51 }
52
53 auto taskExecutor = container->GetTaskExecutor();
54 if (!taskExecutor) {
55 LOGW("taskExecutor is null.");
56 return;
57 }
58 taskExecutor->PostTask(
59 [asyncCtx]() {
60 std::unique_ptr<SnapshotAsyncCtx> ctx(asyncCtx);
61 napi_handle_scope scope = nullptr;
62 napi_open_handle_scope(ctx->env, &scope);
63
64 // callback result format: [Error, PixelMap]
65 napi_value result[JsComponentSnapshot::ARGC_MAX] = { nullptr };
66 napi_get_undefined(ctx->env, &result[0]);
67 napi_get_undefined(ctx->env, &result[1]);
68
69 if (ctx->errCode == Framework::ERROR_CODE_NO_ERROR) {
70 #ifdef PIXEL_MAP_SUPPORTED
71 // convert pixelMap to napi value
72 result[1] = Media::PixelMapNapi::CreatePixelMap(ctx->env, ctx->pixmap);
73 #endif
74 }
75 napi_create_int32(ctx->env, ctx->errCode, &result[0]);
76
77 if (ctx->deferred) {
78 // promise
79 if (ctx->errCode == Framework::ERROR_CODE_NO_ERROR) {
80 napi_resolve_deferred(ctx->env, ctx->deferred, result[1]);
81 } else {
82 napi_reject_deferred(ctx->env, ctx->deferred, result[0]);
83 }
84 } else {
85 // callback
86 napi_value ret = nullptr;
87 napi_value napiCallback = nullptr;
88 napi_get_reference_value(ctx->env, ctx->callbackRef, &napiCallback);
89 napi_call_function(ctx->env, nullptr, napiCallback, JsComponentSnapshot::ARGC_MAX, result, &ret);
90 napi_delete_reference(ctx->env, ctx->callbackRef);
91 }
92
93 napi_close_handle_scope(ctx->env, scope);
94 },
95 TaskExecutor::TaskType::JS);
96 }
97 } // namespace
98
JsComponentSnapshot(napi_env env,napi_callback_info info)99 JsComponentSnapshot::JsComponentSnapshot(napi_env env, napi_callback_info info) : env_(env), argc_(ARGC_MAX)
100 {
101 napi_value thisVar = nullptr;
102 void* data = nullptr;
103
104 // get arguments from JS
105 napi_get_cb_info(env, info, &argc_, argv_, &thisVar, &data);
106 }
107
CheckArgs(napi_valuetype firstArgType)108 bool JsComponentSnapshot::CheckArgs(napi_valuetype firstArgType)
109
110 {
111 size_t minArgc = 1;
112 std::string errorMsg;
113 if (argc_ < minArgc) {
114 errorMsg = "The number of parameters must be greater than or equal to 1.";
115 LOGE("%{public}s", errorMsg.c_str());
116 NapiThrow(env_, errorMsg, Framework::ERROR_CODE_PARAM_INVALID);
117
118 return false;
119 }
120 if (argc_ > ARGC_MAX) {
121 errorMsg = "The largest number of parameters is 2.";
122 LOGE("%{public}s", errorMsg.c_str());
123 NapiThrow(env_, errorMsg, Framework::ERROR_CODE_PARAM_INVALID);
124 }
125 napi_valuetype type = napi_undefined;
126 napi_typeof(env_, argv_[0], &type);
127 if (type != firstArgType) {
128 errorMsg = "parameter id is not of type string";
129 LOGE("%{public}s", errorMsg.c_str());
130 NapiThrow(env_, errorMsg, Framework::ERROR_CODE_PARAM_INVALID);
131 return false;
132 }
133 if (argc_ == ARGC_MAX) {
134 napi_typeof(env_, argv_[1], &type);
135 if (type != napi_function) {
136 errorMsg = "parameter callback is not of type function";
137 LOGE("%{public}s", errorMsg.c_str());
138 NapiThrow(env_, errorMsg, Framework::ERROR_CODE_PARAM_INVALID);
139 return false;
140 }
141 }
142 return true;
143 }
144
CreateCallback(napi_value * result)145 std::function<void(std::shared_ptr<Media::PixelMap>, int32_t)> JsComponentSnapshot::CreateCallback(napi_value* result)
146 {
147 auto* asyncCtx = new SnapshotAsyncCtx;
148 // parse JsCallback
149 if (argc_ == ARGC_MAX) {
150 napi_create_reference(env_, argv_[1], 1, &asyncCtx->callbackRef);
151 }
152 // init promise
153 if (!asyncCtx->callbackRef) {
154 napi_create_promise(env_, &asyncCtx->deferred, result);
155 } else {
156 napi_get_undefined(env_, result);
157 }
158
159 asyncCtx->env = env_;
160 asyncCtx->instanceId = Container::CurrentId();
161
162 return [asyncCtx](std::shared_ptr<Media::PixelMap> pixmap, int32_t errCode) {
163 asyncCtx->pixmap = std::move(pixmap);
164 asyncCtx->errCode = errCode;
165 OnComplete(asyncCtx);
166 };
167 }
168
GetArgv(int32_t idx)169 napi_value JsComponentSnapshot::GetArgv(int32_t idx)
170 {
171 if (idx >= ARGC_MAX) {
172 return nullptr;
173 }
174 return argv_[idx];
175 }
176
JSSnapshotGet(napi_env env,napi_callback_info info)177 static napi_value JSSnapshotGet(napi_env env, napi_callback_info info)
178 {
179 napi_escapable_handle_scope scope = nullptr;
180 napi_open_escapable_handle_scope(env, &scope);
181
182 JsComponentSnapshot helper(env, info);
183
184 napi_value result = nullptr;
185
186 if (!helper.CheckArgs(napi_valuetype::napi_string)) {
187 napi_close_escapable_handle_scope(env, scope);
188 return result;
189 }
190
191 // parse id
192 std::string componentId;
193 napi_valuetype valueType = napi_null;
194 GetNapiString(env, helper.GetArgv(0), componentId, valueType);
195
196 auto delegate = EngineHelper::GetCurrentDelegate();
197 if (!delegate) {
198 NapiThrow(env, "ace engine delegate is null", Framework::ERROR_CODE_INTERNAL_ERROR);
199 napi_close_escapable_handle_scope(env, scope);
200 return result;
201 }
202
203 delegate->GetSnapshot(componentId, helper.CreateCallback(&result));
204
205 napi_escape_handle(env, scope, result, &result);
206 napi_close_escapable_handle_scope(env, scope);
207 return result;
208 }
209
JSSnapshotFromBuilder(napi_env env,napi_callback_info info)210 static napi_value JSSnapshotFromBuilder(napi_env env, napi_callback_info info)
211 {
212 napi_escapable_handle_scope scope = nullptr;
213 napi_open_escapable_handle_scope(env, &scope);
214
215 JsComponentSnapshot helper(env, info);
216 if (!helper.CheckArgs(napi_valuetype::napi_function)) {
217 napi_close_escapable_handle_scope(env, scope);
218 return nullptr;
219 }
220
221 auto delegate = EngineHelper::GetCurrentDelegate();
222 if (!delegate) {
223 NapiThrow(env, "ace engine delegate is null", Framework::ERROR_CODE_INTERNAL_ERROR);
224 napi_close_escapable_handle_scope(env, scope);
225 return nullptr;
226 }
227
228 // create builder closure
229 auto builder = [build = helper.GetArgv(0), env] { napi_call_function(env, nullptr, build, 0, nullptr, nullptr); };
230 napi_value result = nullptr;
231 delegate->CreateSnapshot(builder, helper.CreateCallback(&result));
232
233 napi_escape_handle(env, scope, result, &result);
234 napi_close_escapable_handle_scope(env, scope);
235 return result;
236 }
237
ComponentSnapshotExport(napi_env env,napi_value exports)238 static napi_value ComponentSnapshotExport(napi_env env, napi_value exports)
239 {
240 napi_property_descriptor snapshotDesc[] = {
241 DECLARE_NAPI_FUNCTION("get", JSSnapshotGet),
242 DECLARE_NAPI_FUNCTION("createFromBuilder", JSSnapshotFromBuilder),
243 };
244 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(snapshotDesc) / sizeof(snapshotDesc[0]), snapshotDesc));
245
246 return exports;
247 }
248
249 static napi_module snapshotModule = {
250 .nm_version = 1,
251 .nm_flags = 0,
252 .nm_filename = nullptr,
253 .nm_register_func = ComponentSnapshotExport,
254 .nm_modname = "arkui.componentSnapshot",
255 .nm_priv = ((void*)0),
256 .reserved = { 0 },
257 };
258
ComponentSnapshotRegister()259 extern "C" __attribute__((constructor)) void ComponentSnapshotRegister()
260 {
261 napi_module_register(&snapshotModule);
262 }
263 } // namespace OHOS::Ace::Napi
264