1 /*
2  * Copyright (c) 2021 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 "bridge/declarative_frontend/jsview/js_view_context.h"
17 
18 #include <algorithm>
19 #include <functional>
20 #include <memory>
21 #include <optional>
22 #include <sstream>
23 
24 #include "base/log/ace_trace.h"
25 #include "base/utils/system_properties.h"
26 #include "base/utils/utils.h"
27 #include "base/log/jank_frame_report.h"
28 #include "bridge/common/utils/engine_helper.h"
29 #include "bridge/common/utils/utils.h"
30 #include "bridge/declarative_frontend/engine/functions/js_function.h"
31 #include "bridge/declarative_frontend/engine/js_converter.h"
32 #include "bridge/declarative_frontend/jsview/js_tabs_feature.h"
33 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
34 #include "bridge/declarative_frontend/jsview/models/view_context_model_impl.h"
35 #include "core/animation/animation_pub.h"
36 #include "core/common/ace_engine.h"
37 #include "core/components/common/properties/animation_option.h"
38 #include "core/components_ng/base/view_stack_model.h"
39 #include "core/components_ng/base/view_stack_processor.h"
40 #include "core/components_ng/pattern/view_context/view_context_model_ng.h"
41 
42 #ifdef USE_ARK_ENGINE
43 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
44 #endif
45 
46 namespace OHOS::Ace {
47 
48 std::unique_ptr<ViewContextModel> ViewContextModel::instance_ = nullptr;
49 std::mutex ViewContextModel::mutex_;
50 
GetInstance()51 ViewContextModel* ViewContextModel::GetInstance()
52 {
53     if (!instance_) {
54         std::lock_guard<std::mutex> lock(mutex_);
55         if (!instance_) {
56 #ifdef NG_BUILD
57             instance_.reset(new NG::ViewContextModelNG());
58 #else
59             if (Container::IsCurrentUseNewPipeline()) {
60                 instance_.reset(new NG::ViewContextModelNG());
61             } else {
62                 instance_.reset(new Framework::ViewContextModelImpl());
63             }
64 #endif
65         }
66     }
67     return instance_.get();
68 }
69 
70 } // namespace OHOS::Ace
71 
72 namespace OHOS::Ace::Framework {
73 namespace {
74 
75 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
76 constexpr int64_t MICROSEC_TO_MILLISEC = 1000;
77 constexpr int32_t INVALID_ID = -1;
78 constexpr int32_t INDEX_ONE = 1;
79 constexpr int32_t INDEX_TWO = 2;
80 constexpr int32_t LENGTH_ONE = 1;
81 constexpr int32_t LENGTH_TWO = 2;
82 constexpr int32_t LENGTH_THREE = 3;
83 constexpr int32_t MAX_FLUSH_COUNT = 2;
84 int32_t g_animationCount = 0;
85 
86 std::unordered_map<int32_t, std::string> BIND_SHEET_ERROR_MAP = {
87     { ERROR_CODE_BIND_SHEET_CONTENT_ERROR, "The bindSheetContent is incorrect." },
88     { ERROR_CODE_BIND_SHEET_CONTENT_ALREADY_EXIST, "The bindSheetContent already exists." },
89     { ERROR_CODE_BIND_SHEET_CONTENT_NOT_FOUND, "The bindSheetContent cannot be found." },
90     { ERROR_CODE_TARGET_ID_NOT_EXIST, "The targetId does not exist." },
91     { ERROR_CODE_TARGET_NOT_ON_MAIN_TREE, "The node of targetId is not in the component tree." },
92     { ERROR_CODE_TARGET_NOT_PAGE_CHILD,
93         "The node of targetId is not a child of the page node or NavDestination node." },
94     { ERROR_CODE_INTERNAL_ERROR, "Internal error." },
95     { ERROR_CODE_PARAM_INVALID, "Parameter error. Possible causes: 1. Mandatory parameters are left unspecified;"
96         "2. Incorrect parameter types; 3. Parameter verification failed." }
97 };
98 
PrintAnimationInfo(const AnimationOption & option,AnimationInterface interface,const std::optional<int32_t> & cnt)99 void PrintAnimationInfo(const AnimationOption& option, AnimationInterface interface, const std::optional<int32_t>& cnt)
100 {
101     auto animationInterfaceName = GetAnimationInterfaceName(interface);
102     CHECK_NULL_VOID(animationInterfaceName);
103     if (option.GetIteration() == ANIMATION_REPEAT_INFINITE) {
104         if (interface == AnimationInterface::KEYFRAME_ANIMATE_TO) {
105             TAG_LOGI(AceLogTag::ACE_ANIMATION,
106                 "keyframeAnimateTo iteration is infinite, remember to stop it. total duration:%{public}d",
107                 option.GetDuration());
108         } else {
109             TAG_LOGI(AceLogTag::ACE_ANIMATION,
110                 "%{public}s iteration is infinite, remember to stop it. duration:%{public}d, curve:%{public}s",
111                 animationInterfaceName, option.GetDuration(), option.GetCurve()->ToString().c_str());
112         }
113         return;
114     }
115     if (cnt) {
116         TAG_LOGI(AceLogTag::ACE_ANIMATION, "%{public}s starts, [%{public}s], finish cnt:%{public}d",
117             animationInterfaceName, option.ToString().c_str(), cnt.value());
118     }
119 }
120 
121 // check whether this container needs to perform animateTo
CheckContainer(const RefPtr<Container> & container)122 bool CheckContainer(const RefPtr<Container>& container)
123 {
124     auto context = container->GetPipelineContext();
125     if (!context) {
126         return false;
127     }
128     if (!container->GetSettings().usingSharedRuntime) {
129         return false;
130     }
131     if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
132         return false;
133     }
134     auto executor = container->GetTaskExecutor();
135     CHECK_NULL_RETURN(executor, false);
136     return executor->WillRunOnCurrentThread(TaskExecutor::TaskType::UI);
137 }
138 
GetAnyContextIsLayouting(const RefPtr<PipelineBase> & currentPipeline)139 bool GetAnyContextIsLayouting(const RefPtr<PipelineBase>& currentPipeline)
140 {
141     if (currentPipeline->IsLayouting()) {
142         return true;
143     }
144     bool isLayouting = false;
145     AceEngine::Get().NotifyContainers([&isLayouting](const RefPtr<Container>& container) {
146         if (isLayouting) {
147             // One container is already in layouting
148             return;
149         }
150         if (!CheckContainer(container)) {
151             return;
152         }
153         auto context = container->GetPipelineContext();
154         isLayouting |= context->IsLayouting();
155     });
156     return isLayouting;
157 }
158 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,int32_t triggerId)159 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option,
160     JSRef<JSFunc> jsAnimateToFunc, int32_t triggerId)
161 {
162     pipelineContext->StartImplicitAnimation(option, option.GetCurve(), option.GetOnFinishEvent());
163     auto previousOption = pipelineContext->GetSyncAnimationOption();
164     pipelineContext->SetSyncAnimationOption(option);
165     // Execute the function.
166     jsAnimateToFunc->Call(jsAnimateToFunc);
167     pipelineContext->FlushOnceVsyncTask();
168     AceEngine::Get().NotifyContainersOrderly([triggerId](const RefPtr<Container>& container) {
169         if (!CheckContainer(container)) {
170             return;
171         }
172         auto context = container->GetPipelineContext();
173         ContainerScope scope(container->GetInstanceId());
174         context->FlushBuild();
175         if (context->GetInstanceId() == triggerId) {
176             return;
177         }
178         context->PrepareCloseImplicitAnimation();
179     });
180     pipelineContext->CloseImplicitAnimation();
181     pipelineContext->SetSyncAnimationOption(previousOption);
182 }
183 
FlushDirtyNodesWhenExist(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,const std::optional<int32_t> & count,AnimationInterface interface)184 void FlushDirtyNodesWhenExist(const RefPtr<PipelineBase>& pipelineContext,
185     const AnimationOption& option, const std::optional<int32_t>& count, AnimationInterface interface)
186 {
187     auto animationInterfaceName = GetAnimationInterfaceName(interface);
188     CHECK_NULL_VOID(animationInterfaceName);
189     int32_t flushCount = 0;
190     bool isDirtyNodesEmpty = pipelineContext->IsDirtyNodesEmpty();
191     bool isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
192     while (!isDirtyNodesEmpty || (!isDirtyLayoutNodesEmpty && !pipelineContext->IsLayouting())) {
193         if (flushCount >= MAX_FLUSH_COUNT || option.GetIteration() != ANIMATION_REPEAT_INFINITE) {
194             TAG_LOGW(AceLogTag::ACE_ANIMATION, "%{public}s, option:%{public}s, finish cnt:%{public}d,"
195                 "dirtyNodes is empty:%{public}d, dirtyLayoutNodes is empty:%{public}d",
196                 animationInterfaceName, option.ToString().c_str(), count.value_or(-1),
197                 isDirtyNodesEmpty, isDirtyLayoutNodesEmpty);
198             break;
199         }
200         if (!isDirtyNodesEmpty) {
201             pipelineContext->FlushBuild();
202             isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
203         }
204         if (!isDirtyLayoutNodesEmpty && !pipelineContext->IsLayouting()) {
205             pipelineContext->FlushUITasks(true);
206         }
207         isDirtyNodesEmpty = pipelineContext->IsDirtyNodesEmpty();
208         isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
209         flushCount++;
210     }
211 }
212 
StartAnimationForStageMode(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,const std::optional<int32_t> & count,bool immediately)213 void StartAnimationForStageMode(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option,
214     JSRef<JSFunc> jsAnimateToFunc, const std::optional<int32_t>& count, bool immediately)
215 {
216     auto triggerId = pipelineContext->GetInstanceId();
217     ACE_SCOPED_TRACE("%s, instanceId:%d, finish cnt:%d", option.ToString().c_str(), triggerId, count.value_or(-1));
218     PrintAnimationInfo(
219         option, immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO, count);
220     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
221         TAG_LOGW(AceLogTag::ACE_ANIMATION,
222             "when call animateTo, node stack is not empty, not suitable for animateTo."
223             "param is [option:%{public}s]", option.ToString().c_str());
224     }
225     NG::ScopedViewStackProcessor scopedProcessor;
226     AceEngine::Get().NotifyContainersOrderly([triggerId](const RefPtr<Container>& container) {
227         if (!CheckContainer(container)) {
228             return;
229         }
230         auto context = container->GetPipelineContext();
231         ContainerScope scope(container->GetInstanceId());
232         context->FlushBuild();
233         if (context->GetInstanceId() == triggerId) {
234             return;
235         }
236         context->PrepareOpenImplicitAnimation();
237     });
238     pipelineContext->PrepareOpenImplicitAnimation();
239     FlushDirtyNodesWhenExist(pipelineContext, option, count,
240         immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO);
241     if (!pipelineContext->CatchInteractiveAnimations([pipelineContext, option, jsAnimateToFunc, triggerId]() {
242         AnimateToForStageMode(pipelineContext, option, jsAnimateToFunc, triggerId);
243     })) {
244         AnimateToForStageMode(pipelineContext, option, jsAnimateToFunc, triggerId);
245     }
246     pipelineContext->FlushAfterLayoutCallbackInImplicitAnimationTask();
247     if (immediately) {
248         pipelineContext->FlushModifier();
249         pipelineContext->FlushMessages();
250         JankFrameReport::GetInstance().RecordAnimateEnd();
251     } else {
252         pipelineContext->RequestFrame();
253     }
254 }
255 
StartAnimateToForFaMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,const std::optional<int32_t> & count,bool immediately)256 void StartAnimateToForFaMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
257     JSRef<JSFunc> jsAnimateToFunc, const std::optional<int32_t>& count, bool immediately)
258 {
259     ACE_SCOPED_TRACE("%s, instanceId:%d, finish cnt:%d", option.ToString().c_str(), pipelineContext->GetInstanceId(),
260         count.value_or(-1));
261     PrintAnimationInfo(
262         option, immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO, count);
263     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
264         TAG_LOGW(AceLogTag::ACE_ANIMATION,
265             "when call animateTo, node stack is not empty, not suitable for animateTo. param is [duration:%{public}d, "
266             "curve:%{public}s, iteration:%{public}d]",
267             option.GetDuration(), option.GetCurve()->ToString().c_str(), option.GetIteration());
268     }
269     NG::ScopedViewStackProcessor scopedProcessor;
270     pipelineContext->FlushBuild();
271     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), option.GetOnFinishEvent());
272     auto previousOption = pipelineContext->GetSyncAnimationOption();
273     pipelineContext->SetSyncAnimationOption(option);
274     jsAnimateToFunc->Call(jsAnimateToFunc);
275     pipelineContext->FlushBuild();
276     pipelineContext->CloseImplicitAnimation();
277     pipelineContext->SetSyncAnimationOption(previousOption);
278     if (immediately) {
279         pipelineContext->FlushModifier();
280         pipelineContext->FlushMessages();
281         JankFrameReport::GetInstance().RecordAnimateEnd();
282     } else {
283         pipelineContext->RequestFrame();
284     }
285 }
286 
GetFormAnimationTimeInterval(const RefPtr<PipelineBase> & pipelineContext)287 int64_t GetFormAnimationTimeInterval(const RefPtr<PipelineBase>& pipelineContext)
288 {
289     CHECK_NULL_RETURN(pipelineContext, 0);
290     return (GetMicroTickCount() - pipelineContext->GetFormAnimationStartTime()) / MICROSEC_TO_MILLISEC;
291 }
292 
CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option)293 bool CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option)
294 {
295     CHECK_NULL_RETURN(pipelineContext, false);
296     return pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRender() &&
297         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
298 }
299 
ParseCallBackFunction(const JSRef<JSObject> & curveObj)300 std::function<float(float)> ParseCallBackFunction(const JSRef<JSObject>& curveObj)
301 {
302     std::function<float(float)> customCallBack = nullptr;
303     JSRef<JSVal> onCallBack = curveObj->GetProperty("__curveCustomFunc");
304     if (onCallBack->IsFunction()) {
305         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
306         RefPtr<JsFunction> jsFuncCallBack =
307             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCallBack));
308         customCallBack = [func = std::move(jsFuncCallBack), id = Container::CurrentIdSafely(), node = frameNode](
309                              float time) -> float {
310             ContainerScope scope(id);
311             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
312             CHECK_NULL_RETURN(pipelineContext, 1.0f);
313             pipelineContext->UpdateCurrentActiveNode(node);
314             JSRef<JSVal> params[1];
315             params[0] = JSRef<JSVal>::Make(ToJSValue(time));
316             auto result = func->ExecuteJS(1, params);
317             return result->IsNumber() ? result->ToNumber<float>() : 1.0f;
318         };
319     }
320     return customCallBack;
321 }
322 
323 struct KeyframeParam {
324     int32_t duration = 0;
325     RefPtr<Curve> curve;
326     std::function<void()> animationClosure;
327 };
328 
ParseKeyframeOverallParam(const JSExecutionContext & executionContext,const JSRef<JSObject> & obj)329 AnimationOption ParseKeyframeOverallParam(const JSExecutionContext& executionContext, const JSRef<JSObject>& obj)
330 {
331     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
332     AnimationOption option;
333     if (onFinish->IsFunction()) {
334         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
335         std::function<void()> onFinishEvent = [execCtx = executionContext, func = std::move(jsFunc),
336                             id = Container::CurrentIdSafely()]() mutable {
337             CHECK_NULL_VOID(func);
338             ContainerScope scope(id);
339             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
340             func->Execute();
341             func = nullptr;
342         };
343         option.SetOnFinishEvent(onFinishEvent);
344     }
345     auto delay = obj->GetPropertyValue<int32_t>("delay", 0);
346     auto iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
347     option.SetDelay(delay);
348     option.SetIteration(iterations);
349     return option;
350 }
351 
ParseKeyframes(const JSExecutionContext & executionContext,const JSRef<JSArray> & arr)352 std::vector<KeyframeParam> ParseKeyframes(const JSExecutionContext& executionContext, const JSRef<JSArray>& arr)
353 {
354     std::vector<KeyframeParam> params;
355     for (size_t index = 0; index != arr->Length(); ++index) {
356         if (!arr->GetValueAt(index)->IsObject()) {
357             continue;
358         }
359         auto info = JSRef<JSObject>::Cast(arr->GetValueAt(index));
360         KeyframeParam param;
361 
362         auto jsEventValue = info->GetProperty("event");
363         if (!jsEventValue->IsFunction()) {
364             continue;
365         }
366         param.duration = info->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
367         if (param.duration < 0) {
368             param.duration = 0;
369         }
370         RefPtr<JsFunction> jsFunc =
371             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(jsEventValue));
372         param.animationClosure = [execCtx = executionContext, func = std::move(jsFunc)]() {
373             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
374             func->Execute();
375         };
376         auto curveArgs = info->GetProperty("curve");
377         param.curve = JSViewContext::ParseCurve(curveArgs, true);
378         params.emplace_back(param);
379     }
380     return params;
381 }
382 
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")383 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
384 {
385     napi_value code = nullptr;
386     std::string codeStr = std::to_string(errCode);
387     napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
388     napi_value msg = nullptr;
389     napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
390     napi_value error = nullptr;
391     napi_create_error(env, code, msg, &error);
392     return error;
393 }
394 
ParseSheeetContentNode(const JSCallbackInfo & info)395 RefPtr<NG::FrameNode> ParseSheeetContentNode(const JSCallbackInfo& info)
396 {
397     EcmaVM* vm = info.GetVm();
398     CHECK_NULL_RETURN(vm, nullptr);
399     auto jsTargetNode = info[0];
400     auto* targetNodePtr = jsTargetNode->GetLocalHandle()->ToNativePointer(vm)->Value();
401     CHECK_NULL_RETURN(targetNodePtr, nullptr);
402     NG::FrameNode* sheetContentNode = reinterpret_cast<NG::FrameNode*>(targetNodePtr);
403     CHECK_NULL_RETURN(sheetContentNode, nullptr);
404     return AceType::Claim(sheetContentNode);
405 }
406 
ReturnPromise(const JSCallbackInfo & info,int32_t errCode)407 void ReturnPromise(const JSCallbackInfo& info, int32_t errCode)
408 {
409     auto engine = EngineHelper::GetCurrentEngine();
410     CHECK_NULL_VOID(engine);
411     NativeEngine* nativeEngine = engine->GetNativeEngine();
412     auto env = reinterpret_cast<napi_env>(nativeEngine);
413     napi_deferred deferred = nullptr;
414     napi_value promise = nullptr;
415     napi_create_promise(env, &deferred, &promise);
416 
417     if (errCode != ERROR_CODE_NO_ERROR) {
418         napi_value result = CreateErrorValue(env, errCode, BIND_SHEET_ERROR_MAP[errCode]);
419         napi_reject_deferred(env, deferred, result);
420     } else {
421         napi_value result = nullptr;
422         napi_get_undefined(env, &result);
423         napi_resolve_deferred(env, deferred, result);
424     }
425     CHECK_NULL_VOID(promise);
426     auto jsPromise = JsConverter::ConvertNapiValueToJsVal(promise);
427     if (!jsPromise->IsObject()) {
428         TAG_LOGE(AceLogTag::ACE_SHEET, "Return promise failed.");
429         return;
430     }
431     info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
432 }
433 
StartKeyframeAnimation(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & overallAnimationOption,std::vector<KeyframeParam> & keyframes)434 void StartKeyframeAnimation(const RefPtr<PipelineBase>& pipelineContext,
435     AnimationOption& overallAnimationOption, std::vector<KeyframeParam>& keyframes)
436 {
437     // flush build and flush ui tasks before open animation closure.
438     pipelineContext->FlushBuild();
439     if (!pipelineContext->IsLayouting()) {
440         pipelineContext->FlushUITasks(true);
441     }
442 
443     // flush build when exist dirty nodes, flush ui tasks when exist dirty layout nodes.
444     FlushDirtyNodesWhenExist(pipelineContext, overallAnimationOption, std::nullopt,
445         AnimationInterface::KEYFRAME_ANIMATE_TO);
446 
447     // start KeyframeAnimation.
448     pipelineContext->StartImplicitAnimation(
449         overallAnimationOption, overallAnimationOption.GetCurve(), overallAnimationOption.GetOnFinishEvent());
450     for (auto& keyframe : keyframes) {
451         if (!keyframe.animationClosure) {
452             continue;
453         }
454         AceTraceBeginWithArgs("keyframe duration%d", keyframe.duration);
455         AnimationUtils::AddDurationKeyFrame(keyframe.duration, keyframe.curve, [&keyframe, &pipelineContext]() {
456             keyframe.animationClosure();
457             pipelineContext->FlushBuild();
458             if (!pipelineContext->IsLayouting()) {
459                 pipelineContext->FlushUITasks(true);
460             } else {
461                 TAG_LOGI(AceLogTag::ACE_ANIMATION, "isLayouting, maybe some layout keyframe animation not generated");
462             }
463         });
464         AceTraceEnd();
465     }
466 
467     // close KeyframeAnimation.
468     AnimationUtils::CloseImplicitAnimation();
469 }
470 } // namespace
471 
ParseCurve(const JSRef<JSVal> & curveArgs,bool exceptSpring)472 RefPtr<Curve> JSViewContext::ParseCurve(const JSRef<JSVal>& curveArgs, bool exceptSpring)
473 {
474     RefPtr<Curve> curve;
475     if (curveArgs->IsString()) {
476         auto curveString = curveArgs->ToString();
477         if (exceptSpring) {
478             curve = CreateCurveExceptSpring(curveString);
479         } else {
480             curve = CreateCurve(curveString);
481         }
482     } else if (curveArgs->IsObject()) {
483         JSRef<JSObject> curveObject = JSRef<JSObject>::Cast(curveArgs);
484         JSRef<JSVal> curveString = curveObject->GetProperty("__curveString");
485         if (!curveString->IsString()) {
486             return Curves::EASE_IN_OUT;
487         }
488         auto aniTimFunc = curveString->ToString();
489         std::string customFuncName(DOM_ANIMATION_TIMING_FUNCTION_CUSTOM);
490         if (aniTimFunc == customFuncName) {
491             auto customCurveFunc = ParseCallBackFunction(curveObject);
492             curve = CreateCurve(customCurveFunc);
493         } else if (exceptSpring) {
494             curve = CreateCurveExceptSpring(aniTimFunc);
495         } else {
496             curve = CreateCurve(aniTimFunc);
497         }
498     } else {
499         curve = Curves::EASE_IN_OUT;
500     }
501     return curve;
502 }
503 
CreateAnimation(const JSRef<JSObject> & animationArgs,bool isForm)504 const AnimationOption JSViewContext::CreateAnimation(const JSRef<JSObject>& animationArgs, bool isForm)
505 {
506     AnimationOption option;
507     // If the attribute does not exist, the default value is used.
508     auto duration = animationArgs->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
509     auto delay = animationArgs->GetPropertyValue<int32_t>("delay", 0);
510     auto iterations = animationArgs->GetPropertyValue<int32_t>("iterations", 1);
511     auto tempo = animationArgs->GetPropertyValue<double>("tempo", 1.0);
512     if (SystemProperties::GetRosenBackendEnabled() && NearZero(tempo)) {
513         // set duration to 0 to disable animation.
514         duration = 0;
515     }
516     auto direction = StringToAnimationDirection(animationArgs->GetPropertyValue<std::string>("playMode", "normal"));
517     auto finishCallbackType = static_cast<FinishCallbackType>(
518         animationArgs->GetPropertyValue<int32_t>("finishCallbackType", 0));
519     auto curve = ParseCurve(animationArgs->GetProperty("curve"));
520 
521     // limit animation for ArkTS Form
522     if (isForm) {
523         if (duration > static_cast<int32_t>(DEFAULT_DURATION)) {
524             duration = static_cast<int32_t>(DEFAULT_DURATION);
525         }
526         if (delay != 0) {
527             delay = 0;
528         }
529         if (SystemProperties::IsFormAnimationLimited() && iterations != 1) {
530             iterations = 1;
531         }
532         if (!NearEqual(tempo, 1.0)) {
533             tempo = 1.0;
534         }
535     }
536 
537     int32_t fRRmin = 0;
538     int32_t fRRmax = 0;
539     int32_t fRRExpected = 0;
540     JSRef<JSVal> rateRangeObjectArgs = animationArgs->GetProperty("expectedFrameRateRange");
541     if (rateRangeObjectArgs->IsObject()) {
542         JSRef<JSObject> rateRangeObj = JSRef<JSObject>::Cast(rateRangeObjectArgs);
543         fRRmin = rateRangeObj->GetPropertyValue<int32_t>("min", -1);
544         fRRmax = rateRangeObj->GetPropertyValue<int32_t>("max", -1);
545         fRRExpected = rateRangeObj->GetPropertyValue<int32_t>("expected", -1);
546         TAG_LOGI(AceLogTag::ACE_ANIMATION, "[animation/animateTo] SetExpectedFrameRateRange"
547             "{%{public}d, %{public}d, %{public}d}", fRRmin, fRRmax, fRRExpected);
548     }
549     RefPtr<FrameRateRange> frameRateRange = AceType::MakeRefPtr<FrameRateRange>(fRRmin, fRRmax, fRRExpected);
550 
551     option.SetDuration(duration);
552     option.SetDelay(delay);
553     option.SetIteration(iterations);
554     option.SetTempo(tempo);
555     option.SetAnimationDirection(direction);
556     option.SetCurve(curve);
557     option.SetFinishCallbackType(finishCallbackType);
558     option.SetFrameRateRange(frameRateRange);
559     return option;
560 }
561 
JSAnimation(const JSCallbackInfo & info)562 void JSViewContext::JSAnimation(const JSCallbackInfo& info)
563 {
564     ACE_FUNCTION_TRACE();
565     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
566     if (!scopedDelegate) {
567         // this case usually means there is no foreground container, need to figure out the reason.
568         return;
569     }
570     if (ViewStackModel::GetInstance()->CheckTopNodeFirstBuilding()) {
571         // the node sets attribute value for the first time. No animation is generated.
572         return;
573     }
574     AnimationOption option = AnimationOption();
575     auto container = Container::CurrentSafely();
576     CHECK_NULL_VOID(container);
577     auto pipelineContextBase = container->GetPipelineContext();
578     CHECK_NULL_VOID(pipelineContextBase);
579     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
580         GetFormAnimationTimeInterval(pipelineContextBase) > DEFAULT_DURATION) {
581         TAG_LOGW(
582             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
583         return;
584     }
585     if (info[0]->IsNull() || !info[0]->IsObject()) {
586         ViewContextModel::GetInstance()->closeAnimation(option, true);
587         return;
588     }
589     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
590     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
591     std::function<void()> onFinishEvent;
592     if (onFinish->IsFunction()) {
593         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
594         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
595         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
596                             id = Container::CurrentIdSafely(), node = frameNode]() mutable {
597             CHECK_NULL_VOID(func);
598             ContainerScope scope(id);
599             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
600             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
601             CHECK_NULL_VOID(pipelineContext);
602             pipelineContext->UpdateCurrentActiveNode(node);
603             func->Execute();
604             func = nullptr;
605         };
606     }
607 
608     option = CreateAnimation(obj, pipelineContextBase->IsFormRender());
609     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
610         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase))) {
611         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase));
612         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
613             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase)));
614     }
615 
616     option.SetOnFinishEvent(onFinishEvent);
617     if (SystemProperties::GetRosenBackendEnabled()) {
618         option.SetAllowRunningAsynchronously(true);
619     }
620     PrintAnimationInfo(option, AnimationInterface::ANIMATION, std::nullopt);
621     AceScopedTrace paramTrace("duration:%d, curve:%s, iteration:%d", option.GetDuration(),
622         option.GetCurve()->ToString().c_str(), option.GetIteration());
623     ViewContextModel::GetInstance()->openAnimation(option);
624     JankFrameReport::GetInstance().ReportJSAnimation();
625 }
626 
JSAnimateTo(const JSCallbackInfo & info)627 void JSViewContext::JSAnimateTo(const JSCallbackInfo& info)
628 {
629     ACE_FUNCTION_TRACE();
630     AnimateToInner(info, false);
631 }
632 
JSAnimateToImmediately(const JSCallbackInfo & info)633 void JSViewContext::JSAnimateToImmediately(const JSCallbackInfo& info)
634 {
635     ACE_FUNCTION_TRACE();
636     AnimateToInner(info, true);
637 }
638 
AnimateToInner(const JSCallbackInfo & info,bool immediately)639 void JSViewContext::AnimateToInner(const JSCallbackInfo& info, bool immediately)
640 {
641     ContainerScope scope(Container::CurrentIdSafelyWithCheck());
642     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
643     if (!scopedDelegate) {
644         // this case usually means there is no foreground container, need to figure out the reason.
645         const char* funcName = immediately ? "animateToImmediately" : "animateTo";
646         TAG_LOGW(AceLogTag::ACE_ANIMATION,
647             "can not find current context, %{public}s failed, please use uiContext.%{public}s to specify the context",
648             funcName, funcName);
649         return;
650     }
651     if (info.Length() < 2) {
652         return;
653     }
654     if (!info[0]->IsObject()) {
655         return;
656     }
657     // 2nd argument should be a closure passed to the animateTo function.
658     if (!info[1]->IsFunction()) {
659         return;
660     }
661 
662     auto container = Container::CurrentSafely();
663     CHECK_NULL_VOID(container);
664     auto pipelineContext = container->GetPipelineContext();
665     CHECK_NULL_VOID(pipelineContext);
666     if (pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRender() &&
667         GetFormAnimationTimeInterval(pipelineContext) > DEFAULT_DURATION) {
668         TAG_LOGW(
669             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
670         return;
671     }
672 
673     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
674     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
675     std::function<void()> onFinishEvent;
676     std::optional<int32_t> count;
677     auto traceStreamPtr = std::make_shared<std::stringstream>();
678     if (onFinish->IsFunction()) {
679         count = g_animationCount++;
680         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
681         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
682         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
683                             id = Container::CurrentIdSafely(), traceStreamPtr, node = frameNode, count]() mutable {
684             CHECK_NULL_VOID(func);
685             ContainerScope scope(id);
686             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
687             ACE_SCOPED_TRACE("onFinish[cnt:%d]", count.value());
688             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
689             CHECK_NULL_VOID(pipelineContext);
690             pipelineContext->UpdateCurrentActiveNode(node);
691             TAG_LOGI(AceLogTag::ACE_ANIMATION, "animateTo finish, cnt:%{public}d", count.value());
692             func->Execute();
693             func = nullptr;
694             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
695         };
696     } else {
697         onFinishEvent = [traceStreamPtr]() {
698             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
699         };
700     }
701 
702     AnimationOption option = CreateAnimation(obj, pipelineContext->IsFormRender());
703     option.SetOnFinishEvent(onFinishEvent);
704     *traceStreamPtr << "AnimateTo, Options"
705                     << " duration:" << option.GetDuration()
706                     << ",iteration:" << option.GetIteration()
707                     << ",delay:" << option.GetDelay()
708                     << ",tempo:" << option.GetTempo()
709                     << ",direction:" << (uint32_t) option.GetAnimationDirection()
710                     << ",curve:" << (option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
711     AceAsyncTraceBegin(0, traceStreamPtr->str().c_str(), true);
712     if (CheckIfSetFormAnimationDuration(pipelineContext, option)) {
713         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
714         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
715             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext)));
716     }
717     if (SystemProperties::GetRosenBackendEnabled()) {
718         bool usingSharedRuntime = container->GetSettings().usingSharedRuntime;
719         if (usingSharedRuntime) {
720             if (GetAnyContextIsLayouting(pipelineContext)) {
721                 TAG_LOGW(AceLogTag::ACE_ANIMATION,
722                     "pipeline is layouting, post animateTo, duration:%{public}d, curve:%{public}s",
723                     option.GetDuration(), option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
724                 pipelineContext->GetTaskExecutor()->PostTask(
725                     [id = Container::CurrentIdSafely(), option, func = JSRef<JSFunc>::Cast(info[1]), count,
726                         immediately]() mutable {
727                         ContainerScope scope(id);
728                         auto container = Container::CurrentSafely();
729                         CHECK_NULL_VOID(container);
730                         auto pipelineContext = container->GetPipelineContext();
731                         CHECK_NULL_VOID(pipelineContext);
732                         StartAnimationForStageMode(pipelineContext, option, func, count, immediately);
733                     },
734                     TaskExecutor::TaskType::UI, "ArkUIAnimateToForStageMode", PriorityType::IMMEDIATE);
735                 return;
736             }
737             StartAnimationForStageMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), count, immediately);
738         } else {
739             StartAnimateToForFaMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), count, immediately);
740         }
741     } else {
742         pipelineContext->FlushBuild();
743         pipelineContext->SaveExplicitAnimationOption(option);
744         // Execute the function.
745         JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
746         jsAnimateToFunc->Call(info[1]);
747         pipelineContext->FlushBuild();
748         pipelineContext->CreateExplicitAnimator(onFinishEvent);
749         pipelineContext->ClearExplicitAnimationOption();
750     }
751 }
752 
JSKeyframeAnimateTo(const JSCallbackInfo & info)753 void JSViewContext::JSKeyframeAnimateTo(const JSCallbackInfo& info)
754 {
755     ACE_FUNCTION_TRACE();
756     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
757     if (!scopedDelegate) {
758         // this case usually means there is no foreground container, need to figure out the reason.
759         return;
760     }
761     if (info.Length() < 2) {
762         return;
763     }
764     if (!info[0]->IsObject()) {
765         return;
766     }
767     if (!info[1]->IsArray()) {
768         return;
769     }
770     JSRef<JSArray> keyframeArr = JSRef<JSArray>::Cast(info[1]);
771     if (keyframeArr->Length() == 0) {
772         return;
773     }
774 
775     auto container = Container::CurrentSafely();
776     CHECK_NULL_VOID(container);
777     auto pipelineContext = container->GetPipelineContext();
778     CHECK_NULL_VOID(pipelineContext);
779     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
780     auto overallAnimationOption = ParseKeyframeOverallParam(info.GetExecutionContext(), obj);
781     auto keyframes = ParseKeyframes(info.GetExecutionContext(), keyframeArr);
782     int duration = 0;
783     for (auto& keyframe : keyframes) {
784         duration += keyframe.duration;
785     }
786     overallAnimationOption.SetDuration(duration);
787     // actual curve is in keyframe, this curve will not be effective
788     overallAnimationOption.SetCurve(Curves::EASE_IN_OUT);
789     AceScopedTrace trace("KeyframeAnimateTo iteration:%d, delay:%d",
790                          overallAnimationOption.GetIteration(), overallAnimationOption.GetDelay());
791     PrintAnimationInfo(overallAnimationOption, AnimationInterface::KEYFRAME_ANIMATE_TO, std::nullopt);
792     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
793         TAG_LOGW(AceLogTag::ACE_ANIMATION,
794             "when call keyframeAnimateTo, node stack is not empty, not suitable for keyframeAnimateTo."
795             "param is [duration:%{public}d, delay:%{public}d, iteration:%{public}d]",
796             overallAnimationOption.GetDuration(), overallAnimationOption.GetDelay(),
797             overallAnimationOption.GetIteration());
798     }
799     NG::ScopedViewStackProcessor scopedProcessor;
800     StartKeyframeAnimation(pipelineContext, overallAnimationOption, keyframes);
801     pipelineContext->FlushAfterLayoutCallbackInImplicitAnimationTask();
802 }
803 
SetDynamicDimming(const JSCallbackInfo & info)804 void JSViewContext::SetDynamicDimming(const JSCallbackInfo& info)
805 {
806     EcmaVM* vm = info.GetVm();
807     CHECK_NULL_VOID(vm);
808     auto jsTargetNode = info[0];
809     auto jsDimming = info[1];
810     auto* targetNodePtr = jsTargetNode->GetLocalHandle()->ToNativePointer(vm)->Value();
811     auto* frameNode = reinterpret_cast<NG::FrameNode*>(targetNodePtr);
812     CHECK_NULL_VOID(frameNode);
813     if (!info[1]->IsNumber()) {
814         return;
815     }
816     float dimming = info[1]->ToNumber<float>();
817     RefPtr<Ace::NG::RenderContext> renderContext = frameNode->GetRenderContext();
818     renderContext->UpdateDynamicDimDegree(std::clamp(dimming, 0.0f, 1.0f));
819 }
820 
JSOpenBindSheet(const JSCallbackInfo & info)821 void JSViewContext::JSOpenBindSheet(const JSCallbackInfo& info)
822 {
823     auto paramCnt = info.Length();
824     if (paramCnt < LENGTH_ONE) {
825         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
826         return;
827     }
828 
829     auto sheetContentNode = ParseSheeetContentNode(info);
830     if (sheetContentNode == nullptr) {
831         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
832         return;
833     }
834 
835     // parse SheetStyle and callbacks
836     NG::SheetStyle sheetStyle;
837     sheetStyle.sheetMode = NG::SheetMode::LARGE;
838     sheetStyle.showDragBar = true;
839     sheetStyle.showInPage = false;
840     std::function<void()> onAppearCallback;
841     std::function<void()> onDisappearCallback;
842     std::function<void()> onWillAppearCallback;
843     std::function<void()> onWillDisappearCallback;
844     std::function<void()> shouldDismissFunc;
845     std::function<void(const int32_t)> onWillDismissCallback;
846     std::function<void(const float)> onHeightDidChangeCallback;
847     std::function<void(const float)> onDetentsDidChangeCallback;
848     std::function<void(const float)> onWidthDidChangeCallback;
849     std::function<void(const float)> onTypeDidChangeCallback;
850     std::function<void()> titleBuilderFunction;
851     std::function<void()> sheetSpringBackFunc;
852     if (paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject()) {
853         JSViewAbstract::ParseSheetCallback(info[INDEX_ONE], onAppearCallback, onDisappearCallback, shouldDismissFunc,
854             onWillDismissCallback, onWillAppearCallback, onWillDisappearCallback, onHeightDidChangeCallback,
855             onDetentsDidChangeCallback, onWidthDidChangeCallback, onTypeDidChangeCallback, sheetSpringBackFunc);
856         JSViewAbstract::ParseSheetStyle(info[INDEX_ONE], sheetStyle);
857         JSViewAbstract::ParseSheetTitle(info[INDEX_ONE], sheetStyle, titleBuilderFunction);
858     }
859 
860     int32_t targetId = INVALID_ID;
861     if (paramCnt >= LENGTH_THREE) {
862         if (!info[INDEX_TWO]->IsNumber()) {
863             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
864             return;
865         }
866         targetId = info[INDEX_TWO]->ToNumber<int32_t>();
867         if (targetId < 0) {
868             ReturnPromise(info, ERROR_CODE_TARGET_ID_NOT_EXIST);
869             return;
870         }
871     }
872     sheetStyle.instanceId = Container::CurrentId();
873     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d, targetId: %{public}d",
874         paramCnt, sheetContentNode->GetId(), targetId);
875     auto ret = ViewContextModel::GetInstance()->OpenBindSheet(sheetContentNode,
876         std::move(titleBuilderFunction), sheetStyle, std::move(onAppearCallback), std::move(onDisappearCallback),
877         std::move(shouldDismissFunc), std::move(onWillDismissCallback),  std::move(onWillAppearCallback),
878         std::move(onWillDisappearCallback), std::move(onHeightDidChangeCallback),
879         std::move(onDetentsDidChangeCallback), std::move(onWidthDidChangeCallback),
880         std::move(onTypeDidChangeCallback), std::move(sheetSpringBackFunc), Container::CurrentId(), targetId);
881 
882     ReturnPromise(info, ret);
883     return;
884 }
885 
JSUpdateBindSheet(const JSCallbackInfo & info)886 void JSViewContext::JSUpdateBindSheet(const JSCallbackInfo& info)
887 {
888     auto paramCnt = info.Length();
889     if (paramCnt < LENGTH_TWO) {
890         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
891         return;
892     }
893     auto sheetContentNode = ParseSheeetContentNode(info);
894     if (sheetContentNode == nullptr) {
895         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
896         return;
897     }
898 
899     bool isPartialUpdate = false;
900     if (paramCnt == LENGTH_THREE) {
901         if (!info[INDEX_TWO]->IsBoolean()) {
902             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
903             return;
904         }
905         isPartialUpdate = info[INDEX_TWO]->ToBoolean();
906     }
907 
908     NG::SheetStyle sheetStyle;
909     std::function<void()> titleBuilderFunction;
910     if (paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject()) {
911         JSViewAbstract::ParseSheetStyle(info[INDEX_ONE], sheetStyle, isPartialUpdate);
912         JSViewAbstract::ParseSheetTitle(info[INDEX_ONE], sheetStyle, titleBuilderFunction);
913     } else {
914         sheetStyle.sheetMode = NG::SheetMode::LARGE;
915         sheetStyle.showDragBar = true;
916         sheetStyle.showInPage = false;
917         isPartialUpdate = false;
918     }
919     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d, isPartialUpdate: %{public}d",
920         paramCnt, sheetContentNode->GetId(), isPartialUpdate);
921     auto ret = ViewContextModel::GetInstance()->UpdateBindSheet(
922         sheetContentNode, sheetStyle, isPartialUpdate, Container::CurrentId());
923     ReturnPromise(info, ret);
924     return;
925 }
926 
JSCloseBindSheet(const JSCallbackInfo & info)927 void JSViewContext::JSCloseBindSheet(const JSCallbackInfo& info)
928 {
929     auto paramCnt = info.Length();
930     if (paramCnt < LENGTH_ONE) {
931         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
932         return;
933     }
934 
935     auto sheetContentNode = ParseSheeetContentNode(info);
936     if (sheetContentNode == nullptr) {
937         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
938         return;
939     }
940 
941     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d", paramCnt, sheetContentNode->GetId());
942     auto ret =
943         ViewContextModel::GetInstance()->CloseBindSheet(sheetContentNode, Container::CurrentId());
944     ReturnPromise(info, ret);
945     return;
946 }
IsFollowingSystemFontScale(const JSCallbackInfo & info)947 void JSViewContext::IsFollowingSystemFontScale(const JSCallbackInfo& info)
948 {
949     auto container = Container::CurrentSafely();
950     CHECK_NULL_VOID(container);
951     auto pipelineContext = container->GetPipelineContext();
952     CHECK_NULL_VOID(pipelineContext);
953     auto follow = pipelineContext->IsFollowSystem();
954     auto followRef = JSRef<JSVal>::Make(JSVal(ToJSValue(follow)));
955     info.SetReturnValue(followRef);
956     return;
957 }
958 
GetMaxFontScale(const JSCallbackInfo & info)959 void JSViewContext::GetMaxFontScale(const JSCallbackInfo& info)
960 {
961     auto container = Container::CurrentSafely();
962     CHECK_NULL_VOID(container);
963     auto pipelineContext = container->GetPipelineContext();
964     CHECK_NULL_VOID(pipelineContext);
965     auto maxFontScale = pipelineContext->GetMaxAppFontScale();
966     auto maxFontScaleRef = JSRef<JSVal>::Make(JSVal(ToJSValue(maxFontScale)));
967     info.SetReturnValue(maxFontScaleRef);
968     return;
969 }
970 
JSBind(BindingTarget globalObj)971 void JSViewContext::JSBind(BindingTarget globalObj)
972 {
973     JSClass<JSViewContext>::Declare("Context");
974     JSClass<JSViewContext>::StaticMethod("animation", JSAnimation);
975     JSClass<JSViewContext>::StaticMethod("animateTo", JSAnimateTo);
976     JSClass<JSViewContext>::StaticMethod("animateToImmediately", JSAnimateToImmediately);
977     JSClass<JSViewContext>::StaticMethod("keyframeAnimateTo", JSKeyframeAnimateTo);
978     JSClass<JSViewContext>::StaticMethod("setDynamicDimming", SetDynamicDimming);
979     JSClass<JSViewContext>::StaticMethod("openBindSheet", JSOpenBindSheet);
980     JSClass<JSViewContext>::StaticMethod("updateBindSheet", JSUpdateBindSheet);
981     JSClass<JSViewContext>::StaticMethod("closeBindSheet", JSCloseBindSheet);
982     JSClass<JSViewContext>::StaticMethod("isFollowingSystemFontScale", IsFollowingSystemFontScale);
983     JSClass<JSViewContext>::StaticMethod("getMaxFontScale", GetMaxFontScale);
984     JSClass<JSViewContext>::StaticMethod("bindTabsToScrollable", JSTabsFeature::BindTabsToScrollable);
985     JSClass<JSViewContext>::StaticMethod("unbindTabsFromScrollable", JSTabsFeature::UnbindTabsFromScrollable);
986     JSClass<JSViewContext>::StaticMethod("bindTabsToNestedScrollable", JSTabsFeature::BindTabsToNestedScrollable);
987     JSClass<JSViewContext>::StaticMethod(
988         "unbindTabsFromNestedScrollable", JSTabsFeature::UnbindTabsFromNestedScrollable);
989     JSClass<JSViewContext>::Bind<>(globalObj);
990 }
991 
992 } // namespace OHOS::Ace::Framework
993