• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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