1 /*
2 * Copyright (c) 2025-2025 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_window_animation_utils.h"
17 #include "window_manager_hilog.h"
18 namespace {
19 #define NAPI_CALL_NO_THROW(theCall, retVal) \
20 do { \
21 if ((theCall) != napi_ok) { \
22 return retVal; \
23 } \
24 } while (0)
25
26 template <typename T>
CreateJsNumber(napi_env env,T value)27 napi_value CreateJsNumber(napi_env env, T value)
28 {
29 napi_value result = nullptr;
30 if constexpr (std::is_same_v<T, int32_t>) {
31 napi_create_int32(env, value, &result);
32 } else if constexpr (std::is_same_v<T, uint32_t>) {
33 napi_create_uint32(env, value, &result);
34 } else if constexpr (std::is_same_v<T, int64_t>) {
35 napi_create_int64(env, value, &result);
36 } else if constexpr (std::is_same_v<T, double>) {
37 napi_create_double(env, value, &result);
38 }
39 return result;
40 }
41
42 template<class T>
CreateJsValue(napi_env env,const T & value)43 napi_value CreateJsValue(napi_env env, const T& value)
44 {
45 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
46 napi_value result = nullptr;
47 if constexpr (std::is_same_v<ValueType, bool>) {
48 napi_get_boolean(env, value, &result);
49 return result;
50 } else if constexpr (std::is_arithmetic_v<ValueType>) {
51 return CreateJsNumber(env, value);
52 } else if constexpr (std::is_same_v<ValueType, std::string>) {
53 napi_create_string_utf8(env, value.c_str(), value.length(), &result);
54 return result;
55 } else if constexpr (std::is_enum_v<ValueType>) {
56 return CreateJsNumber(env, static_cast<std::make_signed_t<std::underlying_type_t<ValueType>>>(value));
57 } else if constexpr (std::is_same_v<ValueType, const char*>) {
58 (value != nullptr) ? napi_create_string_utf8(env, value, strlen(value), &result) :
59 napi_get_undefined(env, &result);
60 return result;
61 }
62 }
63
64 template <typename T>
ConvertFromJsNumber(napi_env env,napi_value jsValue,T & outValue)65 bool ConvertFromJsNumber(napi_env env, napi_value jsValue, T& outValue)
66 {
67 if constexpr (std::is_same_v<T, int32_t>) {
68 NAPI_CALL_NO_THROW(napi_get_value_int32(env, jsValue, &outValue), false);
69 } else if constexpr (std::is_same_v<T, uint32_t>) {
70 NAPI_CALL_NO_THROW(napi_get_value_uint32(env, jsValue, &outValue), false);
71 } else if constexpr (std::is_same_v<T, int64_t>) {
72 NAPI_CALL_NO_THROW(napi_get_value_int64(env, jsValue, &outValue), false);
73 } else if constexpr (std::is_same_v<T, double>) {
74 NAPI_CALL_NO_THROW(napi_get_value_double(env, jsValue, &outValue), false);
75 }
76 return true;
77 }
78
79 template<class T>
ConvertFromJsValue(napi_env env,napi_value jsValue,T & value)80 bool ConvertFromJsValue(napi_env env, napi_value jsValue, T& value)
81 {
82 if (jsValue == nullptr) {
83 return false;
84 }
85
86 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
87 if constexpr (std::is_same_v<ValueType, bool>) {
88 NAPI_CALL_NO_THROW(napi_get_value_bool(env, jsValue, &value), false);
89 return true;
90 } else if constexpr (std::is_arithmetic_v<ValueType>) {
91 return ConvertFromJsNumber(env, jsValue, value);
92 } else if constexpr (std::is_same_v<ValueType, std::string>) {
93 size_t len = 0;
94 NAPI_CALL_NO_THROW(napi_get_value_string_utf8(env, jsValue, nullptr, 0, &len), false);
95 auto buffer = std::make_unique<char[]>(len + 1);
96 size_t strLength = 0;
97 NAPI_CALL_NO_THROW(napi_get_value_string_utf8(env, jsValue, buffer.get(), len + 1, &strLength), false);
98 value = buffer.get();
99 return true;
100 } else if constexpr (std::is_enum_v<ValueType>) {
101 std::make_signed_t<ValueType> numberValue = 0;
102 if (!ConvertFromJsNumber(env, jsValue, numberValue)) {
103 return false;
104 }
105 value = static_cast<ValueType>(numberValue);
106 return true;
107 }
108 }
109
110 template<class T>
ParseJsValue(napi_value jsObject,napi_env env,const std::string & name,T & data)111 bool ParseJsValue(napi_value jsObject, napi_env env, const std::string& name, T& data)
112 {
113 napi_value value = nullptr;
114 napi_get_named_property(env, jsObject, name.c_str(), &value);
115 napi_valuetype type = napi_undefined;
116 napi_typeof(env, value, &type);
117 if (type != napi_undefined) {
118 if (!ConvertFromJsValue(env, value, data)) {
119 return false;
120 }
121 } else {
122 return false;
123 }
124 return true;
125 }
126 }
127 namespace OHOS {
128 namespace Rosen {
ConvertTransitionAnimationToJsValue(napi_env env,std::shared_ptr<TransitionAnimation> transitionAnimation)129 napi_value ConvertTransitionAnimationToJsValue(napi_env env, std::shared_ptr<TransitionAnimation> transitionAnimation)
130 {
131 napi_value objValue = nullptr;
132 if (!transitionAnimation) {
133 return objValue;
134 }
135 CHECK_NAPI_CREATE_OBJECT_RETURN_IF_NULL(env, objValue);
136 napi_value configJsValue = ConvertWindowAnimationOptionToJsValue(env, transitionAnimation->config);
137 if (!configJsValue) {
138 return nullptr;
139 }
140 napi_set_named_property(env, objValue, "config", configJsValue);
141 napi_set_named_property(env, objValue, "opacity", CreateJsValue(env, transitionAnimation->opacity));
142
143 return objValue;
144 }
145
ConvertStartAnimationOptionsToJsValue(napi_env env,std::shared_ptr<StartAnimationOptions> startAnimationOptions)146 napi_value ConvertStartAnimationOptionsToJsValue(napi_env env,
147 std::shared_ptr<StartAnimationOptions> startAnimationOptions)
148 {
149 napi_value objValue = nullptr;
150 if (!startAnimationOptions) {
151 return objValue;
152 }
153 CHECK_NAPI_CREATE_OBJECT_RETURN_IF_NULL(env, objValue);
154 NAPI_CHECK_RETURN_IF_NULL(napi_set_named_property(env, objValue, "type",
155 CreateJsValue(env, startAnimationOptions->animationType)),
156 "ConvertStartAnimationOptionsToJsValue failed");
157 return objValue;
158 }
159
ConvertStartAnimationSystemOptionsToJsValue(napi_env env,std::shared_ptr<StartAnimationSystemOptions> startAnimationSystemOptions)160 napi_value ConvertStartAnimationSystemOptionsToJsValue(napi_env env,
161 std::shared_ptr<StartAnimationSystemOptions> startAnimationSystemOptions)
162 {
163 napi_value objValue = nullptr;
164 if (!startAnimationSystemOptions) {
165 return objValue;
166 }
167 CHECK_NAPI_CREATE_OBJECT_RETURN_IF_NULL(env, objValue);
168 NAPI_CHECK_RETURN_IF_NULL(napi_set_named_property(env, objValue, "type",
169 CreateJsValue(env, startAnimationSystemOptions->animationType)),
170 "ConvertStartAnimationSystemOptionsToJsValue failed");
171 if (startAnimationSystemOptions->animationConfig != nullptr) {
172 napi_value configJsValue = ConvertWindowAnimationOptionToJsValue(env,
173 *(startAnimationSystemOptions->animationConfig));
174 if (configJsValue != nullptr) {
175 NAPI_CHECK_RETURN_IF_NULL(napi_set_named_property(env, objValue, "animationConfig", configJsValue),
176 "Set animationConfig failed");
177 } else {
178 TLOGE(WmsLogTag::WMS_ANIMATION, "ConvertWindowAnimationOptionToJsValue failed");
179 }
180 }
181 return objValue;
182 }
183
ConvertWindowAnimationOptionToJsValue(napi_env env,const WindowAnimationOption & animationConfig)184 napi_value ConvertWindowAnimationOptionToJsValue(napi_env env,
185 const WindowAnimationOption& animationConfig)
186 {
187 napi_value configJsValue = nullptr;
188 CHECK_NAPI_CREATE_OBJECT_RETURN_IF_NULL(env, configJsValue);
189 napi_set_named_property(env, configJsValue, "curve", CreateJsValue(env, animationConfig.curve));
190 switch (animationConfig.curve) {
191 case WindowAnimationCurve::LINEAR: {
192 napi_set_named_property(env, configJsValue, "duration", CreateJsValue(env, animationConfig.duration));
193 break;
194 }
195 case WindowAnimationCurve::CUBIC_BEZIER: {
196 napi_set_named_property(env, configJsValue, "duration", CreateJsValue(env, animationConfig.duration));
197 [[fallthrough]];
198 }
199 case WindowAnimationCurve::INTERPOLATION_SPRING: {
200 napi_value params = nullptr;
201 napi_create_array(env, ¶ms);
202 for (uint32_t i = 0; i < ANIMATION_PARAM_SIZE; ++i) {
203 napi_value element;
204 napi_create_double(env, static_cast<double>(animationConfig.params[i]), &element);
205 napi_set_element(env, params, i, element);
206 }
207 napi_set_named_property(env, configJsValue, "param", params);
208 break;
209 }
210 default:
211 break;
212 }
213 return configJsValue;
214 }
215
ConvertTransitionAnimationFromJsValue(napi_env env,napi_value jsObject,TransitionAnimation & transitionAnimation,WmErrorCode & result)216 bool ConvertTransitionAnimationFromJsValue(napi_env env, napi_value jsObject, TransitionAnimation& transitionAnimation,
217 WmErrorCode& result)
218 {
219 napi_value jsAnimationConfig = nullptr;
220 napi_get_named_property(env, jsObject, "config", &jsAnimationConfig);
221 if (!ConvertWindowAnimationOptionFromJsValue(env, jsAnimationConfig, transitionAnimation.config, result) ||
222 !CheckWindowAnimationOption(env, transitionAnimation.config, result)) {
223 return false;
224 }
225 double opacity = 1.0f;
226 napi_value jsOpacityValue = nullptr;
227 napi_get_named_property(env, jsObject, "opacity", &jsOpacityValue);
228 napi_valuetype type = napi_undefined;
229 napi_typeof(env, jsOpacityValue, &type);
230 if (type != napi_undefined) {
231 if (!ConvertFromJsValue(env, jsOpacityValue, opacity)) {
232 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
233 return false;
234 } else if (opacity < 0.0 || opacity > 1.0) {
235 result = WmErrorCode::WM_ERROR_ILLEGAL_PARAM;
236 return false;
237 }
238 }
239 transitionAnimation.opacity = static_cast<float>(opacity);
240 return true;
241 }
242
ConvertStartAnimationOptionsFromJsValue(napi_env env,napi_value jsObject,StartAnimationOptions & startAnimationOptions)243 bool ConvertStartAnimationOptionsFromJsValue(napi_env env, napi_value jsObject,
244 StartAnimationOptions& startAnimationOptions)
245 {
246 uint32_t animationType = 0;
247 if (!ParseJsValue(jsObject, env, "type", animationType)) {
248 return false;
249 }
250 if (animationType >= static_cast<uint32_t>(AnimationType::END)) {
251 return false;
252 }
253 startAnimationOptions.animationType = static_cast<AnimationType>(animationType);
254 return true;
255 }
256
ConvertStartAnimationSystemOptionsFromJsValue(napi_env env,napi_value jsObject,StartAnimationSystemOptions & startAnimationSystemOptions)257 bool ConvertStartAnimationSystemOptionsFromJsValue(napi_env env, napi_value jsObject,
258 StartAnimationSystemOptions& startAnimationSystemOptions)
259 {
260 WmErrorCode result = WmErrorCode::WM_OK;
261 uint32_t animationType = 0;
262 if (!ParseJsValue(jsObject, env, "type", animationType)) {
263 return false;
264 }
265 if (animationType >= static_cast<uint32_t>(AnimationType::END)) {
266 return false;
267 }
268 startAnimationSystemOptions.animationType = static_cast<AnimationType>(animationType);
269 bool hasAnimationConfig = false;
270 napi_has_named_property(env, jsObject, "animationConfig", &hasAnimationConfig);
271 if (!hasAnimationConfig) {
272 return true;
273 }
274 napi_value jsAnimationConfig = nullptr;
275 napi_get_named_property(env, jsObject, "animationConfig", &jsAnimationConfig);
276 startAnimationSystemOptions.animationConfig = std::make_shared<WindowAnimationOption>();
277 if (!ConvertWindowAnimationOptionFromJsValue(env, jsAnimationConfig, *(startAnimationSystemOptions.animationConfig),
278 result) || !CheckWindowAnimationOption(env, *(startAnimationSystemOptions.animationConfig), result)) {
279 startAnimationSystemOptions.animationConfig = nullptr;
280 }
281 return true;
282 }
283
ConvertWindowCreateParamsFromJsValue(napi_env env,napi_value jsObject,WindowCreateParams & windowCreateParams)284 bool ConvertWindowCreateParamsFromJsValue(napi_env env, napi_value jsObject,
285 WindowCreateParams& windowCreateParams)
286 {
287 bool hasAnimationParams = false;
288 napi_has_named_property(env, jsObject, "animationParams", &hasAnimationParams);
289 if (hasAnimationParams) {
290 napi_value jsAnimationParams = nullptr;
291 napi_get_named_property(env, jsObject, "animationParams", &jsAnimationParams);
292 windowCreateParams.animationParams = std::make_shared<StartAnimationOptions>();
293 if (!ConvertStartAnimationOptionsFromJsValue(env, jsAnimationParams, *(windowCreateParams.animationParams))) {
294 windowCreateParams.animationParams = nullptr;
295 }
296 }
297 bool hasAnimationSystemParams = false;
298 napi_has_named_property(env, jsObject, "systemAnimationParams", &hasAnimationSystemParams);
299 if (hasAnimationSystemParams) {
300 napi_value jsAnimationSystemParams = nullptr;
301 napi_get_named_property(env, jsObject, "systemAnimationParams", &jsAnimationSystemParams);
302 windowCreateParams.animationSystemParams = std::make_shared<StartAnimationSystemOptions>();
303 if (!ConvertStartAnimationSystemOptionsFromJsValue(env, jsAnimationSystemParams,
304 *(windowCreateParams.animationSystemParams))) {
305 windowCreateParams.animationSystemParams = nullptr;
306 }
307 }
308 return true;
309 }
310
CheckWindowAnimationOption(napi_env env,WindowAnimationOption & animationConfig,WmErrorCode & result)311 bool CheckWindowAnimationOption(napi_env env, WindowAnimationOption& animationConfig, WmErrorCode& result)
312 {
313 switch (animationConfig.curve) {
314 case WindowAnimationCurve::LINEAR: {
315 if (animationConfig.duration > ANIMATION_MAX_DURATION) {
316 TLOGE(WmsLogTag::WMS_ANIMATION, "Duration is invalid: %{public}u", animationConfig.duration);
317 result = WmErrorCode::WM_ERROR_ILLEGAL_PARAM;
318 return false;
319 }
320 break;
321 }
322 case WindowAnimationCurve::INTERPOLATION_SPRING: {
323 for (uint32_t i = 1; i < ANIMATION_PARAM_SIZE; ++i) {
324 if (animationConfig.params[i] <= 0.0) {
325 TLOGI(WmsLogTag::WMS_ANIMATION, "Interpolation spring param %{public}u is invalid: %{public}f",
326 i, animationConfig.params[i]);
327 animationConfig.params[i] = 1.0;
328 }
329 }
330 break;
331 }
332 case WindowAnimationCurve::CUBIC_BEZIER: {
333 if (animationConfig.duration > ANIMATION_MAX_DURATION) {
334 TLOGE(WmsLogTag::WMS_ANIMATION, "Duration is invalid: %{public}u", animationConfig.duration);
335 result = WmErrorCode::WM_ERROR_ILLEGAL_PARAM;
336 return false;
337 }
338 break;
339 }
340 default:
341 result = WmErrorCode::WM_ERROR_ILLEGAL_PARAM;
342 return false;
343 }
344 return true;
345 }
346
ConvertWindowAnimationOptionFromJsValue(napi_env env,napi_value jsAnimationConfig,WindowAnimationOption & animationConfig,WmErrorCode & result)347 bool ConvertWindowAnimationOptionFromJsValue(napi_env env, napi_value jsAnimationConfig,
348 WindowAnimationOption& animationConfig, WmErrorCode& result)
349 {
350 if (jsAnimationConfig == nullptr) {
351 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
352 return false;
353 }
354 uint32_t curve = 0;
355 if (!ParseJsValue(jsAnimationConfig, env, "curve", curve)) {
356 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
357 return false;
358 }
359 animationConfig.curve = static_cast<WindowAnimationCurve>(curve);
360 uint32_t duration = 0;
361 std::array<double, ANIMATION_PARAM_SIZE> params;
362 switch (curve) {
363 case static_cast<uint32_t>(WindowAnimationCurve::LINEAR): {
364 if (!ParseJsValue(jsAnimationConfig, env, "duration", duration)) {
365 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
366 return false;
367 }
368 animationConfig.duration = duration;
369 break;
370 }
371 case static_cast<uint32_t>(WindowAnimationCurve::CUBIC_BEZIER): {
372 if (!ParseJsValue(jsAnimationConfig, env, "duration", duration)) {
373 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
374 return false;
375 }
376 animationConfig.duration = duration;
377 [[fallthrough]];
378 }
379 case static_cast<uint32_t>(WindowAnimationCurve::INTERPOLATION_SPRING): {
380 napi_value paramsValue = nullptr;
381 napi_get_named_property(env, jsAnimationConfig, "param", ¶msValue);
382 for (uint32_t i = 0; i < ANIMATION_PARAM_SIZE; ++i) {
383 napi_value element;
384 napi_get_element(env, paramsValue, i, &element);
385 if (!ConvertFromJsValue(env, element, params[i])) {
386 result = WmErrorCode::WM_ERROR_INVALID_PARAM;
387 return false;
388 }
389 animationConfig.params[i] = static_cast<float>(params[i]);
390 }
391 break;
392 }
393 default:
394 result = WmErrorCode::WM_ERROR_ILLEGAL_PARAM;
395 return false;
396 }
397 return true;
398 }
399 } // namespace Rosen
400 } // namespace OHOS
401