• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/log/jank_frame_report.h"
26 #include "base/utils/system_properties.h"
27 #include "base/utils/utils.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_view_abstract.h"
33 #include "bridge/declarative_frontend/jsview/js_tabs_feature.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/common/recorder/event_recorder.h"
38 #include "core/components/common/properties/animation_option.h"
39 #include "core/components_ng/base/view_stack_model.h"
40 #include "core/components_ng/base/view_stack_processor.h"
41 #include "core/components_ng/pattern/view_context/view_context_model_ng.h"
42 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
43 
44 #ifdef USE_ARK_ENGINE
45 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
46 #endif
47 
48 namespace OHOS::Ace {
49 
50 std::unique_ptr<ViewContextModel> ViewContextModel::instance_ = nullptr;
51 std::mutex ViewContextModel::mutex_;
52 
GetInstance()53 ViewContextModel* ViewContextModel::GetInstance()
54 {
55     if (!instance_) {
56         std::lock_guard<std::mutex> lock(mutex_);
57         if (!instance_) {
58 #ifdef NG_BUILD
59             instance_.reset(new NG::ViewContextModelNG());
60 #else
61             if (Container::IsCurrentUseNewPipeline()) {
62                 instance_.reset(new NG::ViewContextModelNG());
63             } else {
64                 instance_.reset(new Framework::ViewContextModelImpl());
65             }
66 #endif
67         }
68     }
69     return instance_.get();
70 }
71 
72 } // namespace OHOS::Ace
73 
74 namespace OHOS::Ace::Framework {
75 namespace {
76 
77 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
78 constexpr int64_t MICROSEC_TO_MILLISEC = 1000;
79 constexpr int32_t INVALID_ID = -1;
80 constexpr int32_t INDEX_ONE = 1;
81 constexpr int32_t INDEX_TWO = 2;
82 constexpr int32_t LENGTH_ONE = 1;
83 constexpr int32_t LENGTH_TWO = 2;
84 constexpr int32_t LENGTH_THREE = 3;
85 constexpr int32_t MAX_FLUSH_COUNT = 2;
86 
87 std::unordered_map<int32_t, std::string> UICONTEXT_ERROR_MAP = {
88     { ERROR_CODE_BIND_SHEET_CONTENT_ERROR, "The bindSheetContent is incorrect." },
89     { ERROR_CODE_BIND_SHEET_CONTENT_ALREADY_EXIST, "The bindSheetContent already exists." },
90     { ERROR_CODE_BIND_SHEET_CONTENT_NOT_FOUND, "The bindSheetContent cannot be found." },
91     { ERROR_CODE_TARGET_ID_NOT_EXIST, "The targetId does not exist." },
92     { ERROR_CODE_TARGET_NOT_ON_MAIN_TREE, "The node of targetId is not in the component tree." },
93     { ERROR_CODE_TARGET_NOT_PAGE_CHILD,
94         "The node of targetId is not a child of the page node or NavDestination node." },
95     { ERROR_CODE_INTERNAL_ERROR, "Internal error." },
96     { ERROR_CODE_PARAM_INVALID, "Parameter error. Possible causes: 1. Mandatory parameters are left unspecified;"
97         "2. Incorrect parameter types; 3. Parameter verification failed." },
98     { ERROR_CODE_DIALOG_CONTENT_ERROR, "The ComponentContent is incorrect. " },
99     { ERROR_CODE_DIALOG_CONTENT_ALREADY_EXIST, "The ComponentContent already exists. " },
100     { ERROR_CODE_DIALOG_CONTENT_NOT_FOUND, "The ComponentContent cannot be found. " },
101     { ERROR_CODE_TARGET_INFO_NOT_EXIST, "The targetId does not exist. " },
102     { ERROR_CODE_TARGET_NOT_ON_COMPONENT_TREE, "The node of targetId is not in the component tree. " }
103 };
104 
PrintAnimationInfo(const AnimationOption & option,AnimationInterface interface,const std::optional<int32_t> & cnt)105 void PrintAnimationInfo(const AnimationOption& option, AnimationInterface interface, const std::optional<int32_t>& cnt)
106 {
107     auto animationInterfaceName = GetAnimationInterfaceName(interface);
108     CHECK_NULL_VOID(animationInterfaceName);
109     if (option.GetIteration() == ANIMATION_REPEAT_INFINITE) {
110         if (interface == AnimationInterface::KEYFRAME_ANIMATE_TO) {
111             TAG_LOGI(AceLogTag::ACE_ANIMATION,
112                 "keyframeAnimateTo iteration is infinite, remember to stop it. total duration:%{public}d",
113                 option.GetDuration());
114         } else {
115             TAG_LOGI(AceLogTag::ACE_ANIMATION,
116                 "%{public}s iteration is infinite, remember to stop it. duration:%{public}d, curve:%{public}s",
117                 animationInterfaceName, option.GetDuration(), option.GetCurve()->ToString().c_str());
118         }
119         return;
120     }
121     if (cnt) {
122         TAG_LOGI(AceLogTag::ACE_ANIMATION, "%{public}s starts, [%{public}s], finish cnt:%{public}d",
123             animationInterfaceName, option.ToString().c_str(), cnt.value());
124     }
125 }
126 
127 // check whether this container needs to perform animateTo
CheckContainer(const RefPtr<Container> & container)128 bool CheckContainer(const RefPtr<Container>& container)
129 {
130     auto context = container->GetPipelineContext();
131     if (!context) {
132         return false;
133     }
134     if (!container->GetSettings().usingSharedRuntime) {
135         return false;
136     }
137     if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
138         return false;
139     }
140     auto executor = container->GetTaskExecutor();
141     CHECK_NULL_RETURN(executor, false);
142     return executor->WillRunOnCurrentThread(TaskExecutor::TaskType::UI);
143 }
144 
GetAnyContextIsLayouting(const RefPtr<PipelineBase> & currentPipeline)145 bool GetAnyContextIsLayouting(const RefPtr<PipelineBase>& currentPipeline)
146 {
147     if (currentPipeline->IsLayouting()) {
148         return true;
149     }
150     bool isLayouting = false;
151     AceEngine::Get().NotifyContainers([&isLayouting](const RefPtr<Container>& container) {
152         if (isLayouting) {
153             // One container is already in layouting
154             return;
155         }
156         if (!CheckContainer(container)) {
157             return;
158         }
159         auto context = container->GetPipelineContext();
160         isLayouting |= context->IsLayouting();
161     });
162     return isLayouting;
163 }
164 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,int32_t triggerId,const std::optional<int32_t> & count)165 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option,
166     JSRef<JSFunc> jsAnimateToFunc, int32_t triggerId, const std::optional<int32_t>& count)
167 {
168     pipelineContext->StartImplicitAnimation(option, option.GetCurve(), option.GetOnFinishEvent(), count);
169     auto previousOption = pipelineContext->GetSyncAnimationOption();
170     pipelineContext->SetSyncAnimationOption(option);
171     // Execute the function.
172     jsAnimateToFunc->Call(jsAnimateToFunc);
173     pipelineContext->FlushOnceVsyncTask();
174     AceEngine::Get().NotifyContainersOrderly([triggerId](const RefPtr<Container>& container) {
175         if (!CheckContainer(container)) {
176             return;
177         }
178         auto context = container->GetPipelineContext();
179         ContainerScope scope(container->GetInstanceId());
180         context->FlushBuild();
181         if (context->GetInstanceId() == triggerId) {
182             return;
183         }
184         context->PrepareCloseImplicitAnimation();
185     });
186     pipelineContext->CloseImplicitAnimation();
187     pipelineContext->SetSyncAnimationOption(previousOption);
188 }
189 
FlushDirtyNodesWhenExist(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,const std::optional<int32_t> & count,AnimationInterface interface)190 void FlushDirtyNodesWhenExist(const RefPtr<PipelineBase>& pipelineContext,
191     const AnimationOption& option, const std::optional<int32_t>& count, AnimationInterface interface)
192 {
193     auto animationInterfaceName = GetAnimationInterfaceName(interface);
194     CHECK_NULL_VOID(animationInterfaceName);
195     int32_t flushCount = 0;
196     bool isDirtyNodesEmpty = pipelineContext->IsDirtyNodesEmpty();
197     bool isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
198     while (!isDirtyNodesEmpty || (!isDirtyLayoutNodesEmpty && !pipelineContext->IsLayouting())) {
199         if (flushCount >= MAX_FLUSH_COUNT || option.GetIteration() != ANIMATION_REPEAT_INFINITE) {
200             TAG_LOGW(AceLogTag::ACE_ANIMATION, "%{public}s, option:%{public}s, finish cnt:%{public}d,"
201                 "dirtyNodes is empty:%{public}d, dirtyLayoutNodes is empty:%{public}d",
202                 animationInterfaceName, option.ToString().c_str(), count.value_or(-1),
203                 isDirtyNodesEmpty, isDirtyLayoutNodesEmpty);
204             break;
205         }
206         if (!isDirtyNodesEmpty) {
207             pipelineContext->FlushBuild();
208             isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
209         }
210         if (!isDirtyLayoutNodesEmpty && !pipelineContext->IsLayouting()) {
211             pipelineContext->FlushUITasks(true);
212         }
213         isDirtyNodesEmpty = pipelineContext->IsDirtyNodesEmpty();
214         isDirtyLayoutNodesEmpty = pipelineContext->IsDirtyLayoutNodesEmpty();
215         flushCount++;
216     }
217 }
218 
StartAnimationForStageMode(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,const std::optional<int32_t> & count,bool immediately)219 void StartAnimationForStageMode(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option,
220     JSRef<JSFunc> jsAnimateToFunc, const std::optional<int32_t>& count, bool immediately)
221 {
222     auto triggerId = pipelineContext->GetInstanceId();
223     ACE_SCOPED_TRACE("%s, instanceId:%d, finish cnt:%d", option.ToString().c_str(), triggerId, count.value_or(-1));
224     PrintAnimationInfo(
225         option, immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO, count);
226     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
227         TAG_LOGW(AceLogTag::ACE_ANIMATION,
228             "when call animateTo, node stack is not empty, not suitable for animateTo."
229             "param is [option:%{public}s]", option.ToString().c_str());
230     }
231     NG::ScopedViewStackProcessor scopedProcessor;
232     AceEngine::Get().NotifyContainersOrderly([triggerId](const RefPtr<Container>& container) {
233         if (!CheckContainer(container)) {
234             return;
235         }
236         auto context = container->GetPipelineContext();
237         ContainerScope scope(container->GetInstanceId());
238         context->FlushBuild();
239         if (context->GetInstanceId() == triggerId) {
240             return;
241         }
242         context->PrepareOpenImplicitAnimation();
243     });
244     pipelineContext->PrepareOpenImplicitAnimation();
245     FlushDirtyNodesWhenExist(pipelineContext, option, count,
246         immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO);
247     if (!pipelineContext->CatchInteractiveAnimations([pipelineContext, option, jsAnimateToFunc, triggerId, count]() {
248         AnimateToForStageMode(pipelineContext, option, jsAnimateToFunc, triggerId, count);
249     })) {
250         AnimateToForStageMode(pipelineContext, option, jsAnimateToFunc, triggerId, count);
251     }
252     pipelineContext->FlushAfterLayoutCallbackInImplicitAnimationTask();
253     if (immediately) {
254         pipelineContext->FlushModifier();
255         pipelineContext->FlushMessages();
256         JankFrameReport::GetInstance().RecordAnimateEnd();
257     } else {
258         pipelineContext->RequestFrame();
259     }
260 }
261 
StartAnimateToForFaMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,const std::optional<int32_t> & count,bool immediately)262 void StartAnimateToForFaMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
263     JSRef<JSFunc> jsAnimateToFunc, const std::optional<int32_t>& count, bool immediately)
264 {
265     ACE_SCOPED_TRACE("%s, instanceId:%d, finish cnt:%d", option.ToString().c_str(), pipelineContext->GetInstanceId(),
266         count.value_or(-1));
267     PrintAnimationInfo(
268         option, immediately ? AnimationInterface::ANIMATE_TO_IMMEDIATELY : AnimationInterface::ANIMATE_TO, count);
269     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
270         TAG_LOGW(AceLogTag::ACE_ANIMATION,
271             "when call animateTo, node stack is not empty, not suitable for animateTo. param is [duration:%{public}d, "
272             "curve:%{public}s, iteration:%{public}d]",
273             option.GetDuration(), option.GetCurve()->ToString().c_str(), option.GetIteration());
274     }
275     NG::ScopedViewStackProcessor scopedProcessor;
276     pipelineContext->FlushBuild();
277     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), option.GetOnFinishEvent());
278     auto previousOption = pipelineContext->GetSyncAnimationOption();
279     pipelineContext->SetSyncAnimationOption(option);
280     jsAnimateToFunc->Call(jsAnimateToFunc);
281     pipelineContext->FlushBuild();
282     pipelineContext->CloseImplicitAnimation();
283     pipelineContext->SetSyncAnimationOption(previousOption);
284     if (immediately) {
285         pipelineContext->FlushModifier();
286         pipelineContext->FlushMessages();
287         JankFrameReport::GetInstance().RecordAnimateEnd();
288     } else {
289         pipelineContext->RequestFrame();
290     }
291 }
292 
GetFormAnimationTimeInterval(const RefPtr<PipelineBase> & pipelineContext)293 int64_t GetFormAnimationTimeInterval(const RefPtr<PipelineBase>& pipelineContext)
294 {
295     CHECK_NULL_RETURN(pipelineContext, 0);
296     return (GetMicroTickCount() - pipelineContext->GetFormAnimationStartTime()) / MICROSEC_TO_MILLISEC;
297 }
298 
CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option)299 bool CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option)
300 {
301     CHECK_NULL_RETURN(pipelineContext, false);
302     return pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRenderExceptDynamicComponent() &&
303         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
304 }
305 
ParseCallBackFunction(const JSRef<JSObject> & curveObj)306 std::function<float(float)> ParseCallBackFunction(const JSRef<JSObject>& curveObj)
307 {
308     std::function<float(float)> customCallBack = nullptr;
309     JSRef<JSVal> onCallBack = curveObj->GetProperty("__curveCustomFunc");
310     if (onCallBack->IsFunction()) {
311         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
312         RefPtr<JsFunction> jsFuncCallBack =
313             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCallBack));
314         customCallBack = [func = std::move(jsFuncCallBack), id = Container::CurrentIdSafely(), node = frameNode](
315                              float time) -> float {
316             ContainerScope scope(id);
317             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
318             CHECK_NULL_RETURN(pipelineContext, 1.0f);
319             pipelineContext->UpdateCurrentActiveNode(node);
320             JSRef<JSVal> params[1];
321             params[0] = JSRef<JSVal>::Make(ToJSValue(time));
322             auto result = func->ExecuteJS(1, params);
323             return result->IsNumber() ? result->ToNumber<float>() : 1.0f;
324         };
325     }
326     return customCallBack;
327 }
328 
329 struct KeyframeParam {
330     int32_t duration = 0;
331     RefPtr<Curve> curve;
332     std::function<void()> animationClosure;
333 };
334 
ParseKeyframeOverallParam(const JSExecutionContext & executionContext,const JSRef<JSObject> & obj,std::optional<int32_t> & count)335 AnimationOption ParseKeyframeOverallParam(const JSExecutionContext& executionContext,
336     const JSRef<JSObject>& obj, std::optional<int32_t>& count)
337 {
338     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
339     AnimationOption option;
340     if (onFinish->IsFunction()) {
341         count = GetAnimationFinshCount();
342         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
343         std::function<void()> onFinishEvent = [execCtx = executionContext, func = std::move(jsFunc),
344                             id = Container::CurrentIdSafely()]() mutable {
345             CHECK_NULL_VOID(func);
346             ContainerScope scope(id);
347             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
348             func->Execute();
349             func = nullptr;
350         };
351         option.SetOnFinishEvent(onFinishEvent);
352     }
353     auto delay = obj->GetPropertyValue<int32_t>("delay", 0);
354     auto iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
355     JSRef<JSVal> rateRangeObjectArgs = obj->GetProperty("expectedFrameRateRange");
356     if (rateRangeObjectArgs->IsObject()) {
357         JSRef<JSObject> rateRangeObj = JSRef<JSObject>::Cast(rateRangeObjectArgs);
358         int32_t fRRmin = rateRangeObj->GetPropertyValue<int32_t>("min", -1);
359         int32_t fRRmax = rateRangeObj->GetPropertyValue<int32_t>("max", -1);
360         int32_t fRRExpected = rateRangeObj->GetPropertyValue<int32_t>("expected", -1);
361         TAG_LOGD(AceLogTag::ACE_ANIMATION, "[keyframe] SetExpectedFrameRateRange"
362             "{%{public}d, %{public}d, %{public}d}", fRRmin, fRRmax, fRRExpected);
363         RefPtr<FrameRateRange> frameRateRange = AceType::MakeRefPtr<FrameRateRange>(fRRmin, fRRmax, fRRExpected);
364         option.SetFrameRateRange(frameRateRange);
365     }
366     option.SetDelay(delay);
367     option.SetIteration(iterations);
368     return option;
369 }
370 
ParseKeyframes(const JSExecutionContext & executionContext,const JSRef<JSArray> & arr)371 std::vector<KeyframeParam> ParseKeyframes(const JSExecutionContext& executionContext, const JSRef<JSArray>& arr)
372 {
373     std::vector<KeyframeParam> params;
374     for (size_t index = 0; index != arr->Length(); ++index) {
375         if (!arr->GetValueAt(index)->IsObject()) {
376             continue;
377         }
378         auto info = JSRef<JSObject>::Cast(arr->GetValueAt(index));
379         KeyframeParam param;
380 
381         auto jsEventValue = info->GetProperty("event");
382         if (!jsEventValue->IsFunction()) {
383             continue;
384         }
385         param.duration = info->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
386         if (param.duration < 0) {
387             param.duration = 0;
388         }
389         RefPtr<JsFunction> jsFunc =
390             AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(jsEventValue));
391         param.animationClosure = [execCtx = executionContext, func = std::move(jsFunc)]() {
392             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
393             func->Execute();
394         };
395         auto curveArgs = info->GetProperty("curve");
396         param.curve = JSViewContext::ParseCurve(curveArgs, true);
397         params.emplace_back(param);
398     }
399     return params;
400 }
401 
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")402 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
403 {
404     napi_value code = nullptr;
405     std::string codeStr = std::to_string(errCode);
406     napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
407     napi_value msg = nullptr;
408     napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
409     napi_value error = nullptr;
410     napi_create_error(env, code, msg, &error);
411     return error;
412 }
413 
ParseContentNode(const JSCallbackInfo & info)414 RefPtr<NG::FrameNode> ParseContentNode(const JSCallbackInfo& info)
415 {
416     EcmaVM* vm = info.GetVm();
417     CHECK_NULL_RETURN(vm, nullptr);
418     auto jsTargetNode = info[0];
419     auto* targetNodePtr = jsTargetNode->GetLocalHandle()->ToNativePointer(vm)->Value();
420     CHECK_NULL_RETURN(targetNodePtr, nullptr);
421     NG::FrameNode* sheetContentNode = reinterpret_cast<NG::FrameNode*>(targetNodePtr);
422     CHECK_NULL_RETURN(sheetContentNode, nullptr);
423     return AceType::Claim(sheetContentNode);
424 }
425 
ReturnPromise(const JSCallbackInfo & info,int32_t errCode)426 void ReturnPromise(const JSCallbackInfo& info, int32_t errCode)
427 {
428     auto engine = EngineHelper::GetCurrentEngine();
429     CHECK_NULL_VOID(engine);
430     NativeEngine* nativeEngine = engine->GetNativeEngine();
431     auto env = reinterpret_cast<napi_env>(nativeEngine);
432     napi_deferred deferred = nullptr;
433     napi_value promise = nullptr;
434     napi_create_promise(env, &deferred, &promise);
435 
436     if (errCode != ERROR_CODE_NO_ERROR) {
437         napi_value result = CreateErrorValue(env, errCode, UICONTEXT_ERROR_MAP[errCode]);
438         napi_reject_deferred(env, deferred, result);
439     } else {
440         napi_value result = nullptr;
441         napi_get_undefined(env, &result);
442         napi_resolve_deferred(env, deferred, result);
443     }
444     CHECK_NULL_VOID(promise);
445     auto jsPromise = JsConverter::ConvertNapiValueToJsVal(promise);
446     if (!jsPromise->IsObject()) {
447         TAG_LOGE(AceLogTag::ACE_SHEET, "Return promise failed.");
448         return;
449     }
450     info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
451 }
452 
StartKeyframeAnimation(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & overallAnimationOption,std::vector<KeyframeParam> & keyframes,const std::optional<int32_t> & count)453 void StartKeyframeAnimation(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& overallAnimationOption,
454     std::vector<KeyframeParam>& keyframes, const std::optional<int32_t>& count)
455 {
456     // flush build and flush ui tasks before open animation closure.
457     pipelineContext->FlushBuild();
458     if (!pipelineContext->IsLayouting()) {
459         pipelineContext->FlushUITasks(true);
460     }
461 
462     // flush build when exist dirty nodes, flush ui tasks when exist dirty layout nodes.
463     FlushDirtyNodesWhenExist(pipelineContext,
464         overallAnimationOption, count, AnimationInterface::KEYFRAME_ANIMATE_TO);
465 
466     // start KeyframeAnimation.
467     pipelineContext->StartImplicitAnimation(
468         overallAnimationOption, overallAnimationOption.GetCurve(), overallAnimationOption.GetOnFinishEvent(), count);
469     for (auto& keyframe : keyframes) {
470         if (!keyframe.animationClosure) {
471             continue;
472         }
473         AceTraceBeginWithArgs("keyframe duration%d", keyframe.duration);
474         AnimationUtils::AddDurationKeyFrame(keyframe.duration, keyframe.curve, [&keyframe, &pipelineContext]() {
475             keyframe.animationClosure();
476             pipelineContext->FlushBuild();
477             if (!pipelineContext->IsLayouting()) {
478                 pipelineContext->FlushUITasks(true);
479             } else {
480                 TAG_LOGI(AceLogTag::ACE_ANIMATION, "isLayouting, maybe some layout keyframe animation not generated");
481             }
482         });
483         AceTraceEnd();
484     }
485 
486     // close KeyframeAnimation.
487     AnimationUtils::CloseImplicitAnimation();
488 }
489 } // namespace
490 
ParseCurve(const JSRef<JSVal> & curveArgs,bool exceptSpring)491 RefPtr<Curve> JSViewContext::ParseCurve(const JSRef<JSVal>& curveArgs, bool exceptSpring)
492 {
493     RefPtr<Curve> curve;
494     if (curveArgs->IsString()) {
495         auto curveString = curveArgs->ToString();
496         if (exceptSpring) {
497             curve = CreateCurveExceptSpring(curveString);
498         } else {
499             curve = CreateCurve(curveString);
500         }
501     } else if (curveArgs->IsObject()) {
502         JSRef<JSObject> curveObject = JSRef<JSObject>::Cast(curveArgs);
503         JSRef<JSVal> curveString = curveObject->GetProperty("__curveString");
504         if (!curveString->IsString()) {
505             return Curves::EASE_IN_OUT;
506         }
507         auto aniTimFunc = curveString->ToString();
508         std::string customFuncName(DOM_ANIMATION_TIMING_FUNCTION_CUSTOM);
509         if (aniTimFunc == customFuncName) {
510             auto customCurveFunc = ParseCallBackFunction(curveObject);
511             curve = CreateCurve(customCurveFunc);
512         } else if (exceptSpring) {
513             curve = CreateCurveExceptSpring(aniTimFunc);
514         } else {
515             curve = CreateCurve(aniTimFunc);
516         }
517     } else {
518         curve = Curves::EASE_IN_OUT;
519     }
520     return curve;
521 }
522 
CreateAnimation(const JSRef<JSObject> & animationArgs,bool isForm)523 const AnimationOption JSViewContext::CreateAnimation(const JSRef<JSObject>& animationArgs, bool isForm)
524 {
525     AnimationOption option;
526     // If the attribute does not exist, the default value is used.
527     auto duration = animationArgs->GetPropertyValue<int32_t>("duration", DEFAULT_DURATION);
528     auto delay = animationArgs->GetPropertyValue<int32_t>("delay", 0);
529     auto iterations = animationArgs->GetPropertyValue<int32_t>("iterations", 1);
530     auto tempo = animationArgs->GetPropertyValue<double>("tempo", 1.0);
531     if (SystemProperties::GetRosenBackendEnabled() && NearZero(tempo)) {
532         // set duration to 0 to disable animation.
533         duration = 0;
534     }
535     auto direction = StringToAnimationDirection(animationArgs->GetPropertyValue<std::string>("playMode", "normal"));
536     auto finishCallbackType = static_cast<FinishCallbackType>(
537         animationArgs->GetPropertyValue<int32_t>("finishCallbackType", 0));
538     auto curve = ParseCurve(animationArgs->GetProperty("curve"));
539 
540     // limit animation for ArkTS Form
541     if (isForm) {
542         if (duration > static_cast<int32_t>(DEFAULT_DURATION)) {
543             duration = static_cast<int32_t>(DEFAULT_DURATION);
544         }
545         if (delay != 0) {
546             delay = 0;
547         }
548         if (SystemProperties::IsFormAnimationLimited() && iterations != 1) {
549             iterations = 1;
550         }
551         if (!NearEqual(tempo, 1.0)) {
552             tempo = 1.0;
553         }
554     }
555 
556     int32_t fRRmin = 0;
557     int32_t fRRmax = 0;
558     int32_t fRRExpected = 0;
559     JSRef<JSVal> rateRangeObjectArgs = animationArgs->GetProperty("expectedFrameRateRange");
560     if (rateRangeObjectArgs->IsObject()) {
561         JSRef<JSObject> rateRangeObj = JSRef<JSObject>::Cast(rateRangeObjectArgs);
562         fRRmin = rateRangeObj->GetPropertyValue<int32_t>("min", -1);
563         fRRmax = rateRangeObj->GetPropertyValue<int32_t>("max", -1);
564         fRRExpected = rateRangeObj->GetPropertyValue<int32_t>("expected", -1);
565         TAG_LOGD(AceLogTag::ACE_ANIMATION, "[animation/animateTo] SetExpectedFrameRateRange"
566             "{%{public}d, %{public}d, %{public}d}", fRRmin, fRRmax, fRRExpected);
567     }
568     RefPtr<FrameRateRange> frameRateRange = AceType::MakeRefPtr<FrameRateRange>(fRRmin, fRRmax, fRRExpected);
569 
570     option.SetDuration(duration);
571     option.SetDelay(delay);
572     option.SetIteration(iterations);
573     option.SetTempo(tempo);
574     option.SetAnimationDirection(direction);
575     option.SetCurve(curve);
576     option.SetFinishCallbackType(finishCallbackType);
577     option.SetFrameRateRange(frameRateRange);
578     return option;
579 }
580 
JSAnimation(const JSCallbackInfo & info)581 void JSViewContext::JSAnimation(const JSCallbackInfo& info)
582 {
583     ACE_FUNCTION_TRACE();
584     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
585     if (!scopedDelegate) {
586         // this case usually means there is no foreground container, need to figure out the reason.
587         return;
588     }
589     if (ViewStackModel::GetInstance()->CheckTopNodeFirstBuilding()) {
590         // the node sets attribute value for the first time. No animation is generated.
591         return;
592     }
593     AnimationOption option = AnimationOption();
594     auto container = Container::CurrentSafely();
595     CHECK_NULL_VOID(container);
596     auto pipelineContextBase = container->GetPipelineContext();
597     CHECK_NULL_VOID(pipelineContextBase);
598     if (pipelineContextBase->IsFormAnimationFinishCallback() &&
599         pipelineContextBase->IsFormRenderExceptDynamicComponent() &&
600         GetFormAnimationTimeInterval(pipelineContextBase) > DEFAULT_DURATION) {
601         TAG_LOGW(
602             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
603         return;
604     }
605     if (info[0]->IsNull() || !info[0]->IsObject()) {
606         ViewContextModel::GetInstance()->closeAnimation(option, true);
607         return;
608     }
609     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
610     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
611     std::function<void()> onFinishEvent;
612     if (onFinish->IsFunction()) {
613         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
614         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
615         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
616                             id = Container::CurrentIdSafely(), node = frameNode]() mutable {
617             CHECK_NULL_VOID(func);
618             ContainerScope scope(id);
619             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
620             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
621             CHECK_NULL_VOID(pipelineContext);
622             pipelineContext->UpdateCurrentActiveNode(node);
623             func->Execute();
624             func = nullptr;
625         };
626     }
627 
628     option = CreateAnimation(obj, pipelineContextBase->IsFormRenderExceptDynamicComponent());
629     if (pipelineContextBase->IsFormAnimationFinishCallback() &&
630         pipelineContextBase->IsFormRenderExceptDynamicComponent() &&
631         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase))) {
632         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase));
633         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
634             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase)));
635     }
636 
637     option.SetOnFinishEvent(onFinishEvent);
638     if (SystemProperties::GetRosenBackendEnabled()) {
639         option.SetAllowRunningAsynchronously(true);
640     }
641     PrintAnimationInfo(option, AnimationInterface::ANIMATION, std::nullopt);
642     AceScopedTrace paramTrace("duration:%d, curve:%s, iteration:%d", option.GetDuration(),
643         option.GetCurve()->ToString().c_str(), option.GetIteration());
644     ViewContextModel::GetInstance()->openAnimation(option);
645     JankFrameReport::GetInstance().ReportJSAnimation();
646 }
647 
JSAnimateTo(const JSCallbackInfo & info)648 void JSViewContext::JSAnimateTo(const JSCallbackInfo& info)
649 {
650     ACE_FUNCTION_TRACE();
651     AnimateToInner(info, false);
652 }
653 
JSAnimateToImmediately(const JSCallbackInfo & info)654 void JSViewContext::JSAnimateToImmediately(const JSCallbackInfo& info)
655 {
656     ACE_FUNCTION_TRACE();
657     AnimateToInner(info, true);
658 }
659 
RecordAnimationFinished(int32_t count)660 void RecordAnimationFinished(int32_t count)
661 {
662     if (Recorder::EventRecorder::Get().IsRecordEnable(Recorder::EventCategory::CATEGORY_ANIMATION)) {
663         Recorder::EventParamsBuilder builder;
664         builder.SetEventCategory(Recorder::EventCategory::CATEGORY_ANIMATION)
665             .SetEventType(Recorder::EventType::ANIMATION_FINISHED)
666             .SetExtra(Recorder::KEY_COUNT, std::to_string(count));
667         Recorder::EventRecorder::Get().OnEvent(std::move(builder));
668     }
669 }
670 
AnimateToInner(const JSCallbackInfo & info,bool immediately)671 void JSViewContext::AnimateToInner(const JSCallbackInfo& info, bool immediately)
672 {
673     auto currentId = Container::CurrentIdSafelyWithCheck();
674     bool needCheck = false;
675     if (!Container::CheckRunOnThreadByThreadId(currentId, false)) {
676         // fix DynamicComponent get wrong container when calling the animateTo function.
677         auto localContainerId = ContainerScope::CurrentLocalId();
678         TAG_LOGI(AceLogTag::ACE_ANIMATION,
679             "AnimateToInner not run on running thread, currentId: %{public}d, localId: %{public}d",
680             currentId, localContainerId);
681         if (localContainerId > 0 && Container::CheckRunOnThreadByThreadId(localContainerId, false)) {
682             currentId = localContainerId;
683         } else {
684             needCheck = true;
685         }
686     }
687     ContainerScope scope(currentId);
688     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
689     if (!scopedDelegate) {
690         // this case usually means there is no foreground container, need to figure out the reason.
691         const char* funcName = immediately ? "animateToImmediately" : "animateTo";
692         TAG_LOGW(AceLogTag::ACE_ANIMATION,
693             "can not find currnet context ,%{pubic}s faild, please use uiContext.%{public}s to specify the context",
694             funcName, funcName);
695         return;
696     }
697     if (info.Length() < 2) {
698         return;
699     }
700     if (!info[0]->IsObject()) {
701         return;
702     }
703     // 2nd argument should be a closure passed to the animateTo function.
704     if (!info[1]->IsFunction()) {
705         return;
706     }
707 
708     auto container = Container::CurrentSafely();
709     CHECK_NULL_VOID(container);
710     if (needCheck && !Container::CheckRunOnThreadByThreadId(container->GetInstanceId(), false)) {
711         const char* funcName = immediately ? "animateToImmediately" : "animateTo";
712         TAG_LOGW(AceLogTag::ACE_ANIMATION,
713             "the context found cannot run on current thread, %{public}s failed, "
714             "please use uiContext.%{public}s to specify the context",
715             funcName, funcName);
716         return;
717     }
718     auto pipelineContext = container->GetPipelineContext();
719     CHECK_NULL_VOID(pipelineContext);
720     if (pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRenderExceptDynamicComponent() &&
721         GetFormAnimationTimeInterval(pipelineContext) > DEFAULT_DURATION) {
722         TAG_LOGW(
723             AceLogTag::ACE_FORM, "[Form animation] Form finish callback triggered animation cannot exceed 1000ms.");
724         return;
725     }
726 
727     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
728     AnimationOption option = CreateAnimation(obj, pipelineContext->IsFormRenderExceptDynamicComponent());
729     auto iterations = option.GetIteration();
730     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
731     std::function<void()> onFinishEvent;
732     std::optional<int32_t> count;
733     auto traceStreamPtr = std::make_shared<std::stringstream>();
734     if (onFinish->IsFunction()) {
735         count = GetAnimationFinshCount();
736         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
737         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
738         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
739                             id = Container::CurrentIdSafely(), traceStreamPtr, node = frameNode, count,
740                             iterations]() mutable {
741             RecordAnimationFinished(iterations);
742             CHECK_NULL_VOID(func);
743             ContainerScope scope(id);
744             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
745             ACE_SCOPED_TRACE("onFinish[cnt:%d]", count.value());
746             auto pipelineContext = PipelineContext::GetCurrentContextSafely();
747             CHECK_NULL_VOID(pipelineContext);
748             pipelineContext->UpdateCurrentActiveNode(node);
749             TAG_LOGI(AceLogTag::ACE_ANIMATION, "animateTo finish, cnt:%{public}d", count.value());
750             func->Execute();
751             func = nullptr;
752             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
753         };
754     } else {
755         onFinishEvent = [traceStreamPtr, iterations]() {
756             RecordAnimationFinished(iterations);
757             AceAsyncTraceEnd(0, traceStreamPtr->str().c_str(), true);
758         };
759     }
760 
761     option.SetOnFinishEvent(onFinishEvent);
762     *traceStreamPtr << "AnimateTo, Options"
763                     << " duration:" << option.GetDuration()
764                     << ",iteration:" << option.GetIteration()
765                     << ",delay:" << option.GetDelay()
766                     << ",tempo:" << option.GetTempo()
767                     << ",direction:" << (uint32_t) option.GetAnimationDirection()
768                     << ",curve:" << (option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
769     AceAsyncTraceBegin(0, traceStreamPtr->str().c_str(), true);
770     if (CheckIfSetFormAnimationDuration(pipelineContext, option)) {
771         option.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
772         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
773             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext)));
774     }
775     if (SystemProperties::GetRosenBackendEnabled()) {
776         bool usingSharedRuntime = container->GetSettings().usingSharedRuntime;
777         if (usingSharedRuntime) {
778             if (GetAnyContextIsLayouting(pipelineContext)) {
779                 TAG_LOGW(AceLogTag::ACE_ANIMATION,
780                     "pipeline is layouting, post animateTo, duration:%{public}d, curve:%{public}s",
781                     option.GetDuration(), option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
782                 pipelineContext->GetTaskExecutor()->PostTask(
783                     [id = Container::CurrentIdSafely(), option, func = JSRef<JSFunc>::Cast(info[1]), count,
784                         immediately]() mutable {
785                         ContainerScope scope(id);
786                         auto container = Container::CurrentSafely();
787                         CHECK_NULL_VOID(container);
788                         auto pipelineContext = container->GetPipelineContext();
789                         CHECK_NULL_VOID(pipelineContext);
790                         StartAnimationForStageMode(pipelineContext, option, func, count, immediately);
791                     },
792                     TaskExecutor::TaskType::UI, "ArkUIAnimateToForStageMode", PriorityType::IMMEDIATE);
793                 return;
794             }
795             StartAnimationForStageMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), count, immediately);
796         } else {
797             StartAnimateToForFaMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), count, immediately);
798         }
799     } else {
800         pipelineContext->FlushBuild();
801         pipelineContext->SaveExplicitAnimationOption(option);
802         // Execute the function.
803         JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
804         jsAnimateToFunc->Call(info[1]);
805         pipelineContext->FlushBuild();
806         pipelineContext->CreateExplicitAnimator(onFinishEvent);
807         pipelineContext->ClearExplicitAnimationOption();
808     }
809 }
810 
JSKeyframeAnimateTo(const JSCallbackInfo & info)811 void JSViewContext::JSKeyframeAnimateTo(const JSCallbackInfo& info)
812 {
813     ACE_FUNCTION_TRACE();
814     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
815     if (!scopedDelegate) {
816         // this case usually means there is no foreground container, need to figure out the reason.
817         return;
818     }
819     if (info.Length() < 2) {
820         return;
821     }
822     if (!info[0]->IsObject()) {
823         return;
824     }
825     if (!info[1]->IsArray()) {
826         return;
827     }
828     JSRef<JSArray> keyframeArr = JSRef<JSArray>::Cast(info[1]);
829     if (keyframeArr->Length() == 0) {
830         return;
831     }
832 
833     auto container = Container::CurrentSafely();
834     CHECK_NULL_VOID(container);
835     if (!Container::CheckRunOnThreadByThreadId(container->GetInstanceId(), false)) {
836         TAG_LOGW(AceLogTag::ACE_ANIMATION, "the context found cannot run current thread, KeyframeAnimateTo "
837                                            "failed, please use correct ui context.");
838         return;
839     }
840     auto pipelineContext = container->GetPipelineContext();
841     CHECK_NULL_VOID(pipelineContext);
842     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
843     std::optional<int32_t> count;
844     auto overallAnimationOption = ParseKeyframeOverallParam(info.GetExecutionContext(), obj, count);
845     auto keyframes = ParseKeyframes(info.GetExecutionContext(), keyframeArr);
846     int duration = 0;
847     for (auto& keyframe : keyframes) {
848         duration += keyframe.duration;
849     }
850     overallAnimationOption.SetDuration(duration);
851     // actual curve is in keyframe, this curve will not be effective
852     overallAnimationOption.SetCurve(Curves::EASE_IN_OUT);
853     AceScopedTrace trace("KeyframeAnimateTo iteration:%d, delay:%d",
854                          overallAnimationOption.GetIteration(), overallAnimationOption.GetDelay());
855     PrintAnimationInfo(overallAnimationOption, AnimationInterface::KEYFRAME_ANIMATE_TO, count);
856     if (!ViewStackModel::GetInstance()->IsEmptyStack()) {
857         TAG_LOGW(AceLogTag::ACE_ANIMATION,
858             "when call keyframeAnimateTo, node stack is not empty, not suitable for keyframeAnimateTo."
859             "param is [duration:%{public}d, delay:%{public}d, iteration:%{public}d]",
860             overallAnimationOption.GetDuration(), overallAnimationOption.GetDelay(),
861             overallAnimationOption.GetIteration());
862     }
863     NG::ScopedViewStackProcessor scopedProcessor;
864     StartKeyframeAnimation(pipelineContext, overallAnimationOption, keyframes, count);
865     pipelineContext->FlushAfterLayoutCallbackInImplicitAnimationTask();
866 }
867 
SetDynamicDimming(const JSCallbackInfo & info)868 void JSViewContext::SetDynamicDimming(const JSCallbackInfo& info)
869 {
870     EcmaVM* vm = info.GetVm();
871     CHECK_NULL_VOID(vm);
872     auto jsTargetNode = info[0];
873     auto jsDimming = info[1];
874     auto* targetNodePtr = jsTargetNode->GetLocalHandle()->ToNativePointer(vm)->Value();
875     auto* frameNode = reinterpret_cast<NG::FrameNode*>(targetNodePtr);
876     CHECK_NULL_VOID(frameNode);
877     if (!info[1]->IsNumber()) {
878         return;
879     }
880     float dimming = info[1]->ToNumber<float>();
881     RefPtr<Ace::NG::RenderContext> renderContext = frameNode->GetRenderContext();
882     renderContext->UpdateDynamicDimDegree(std::clamp(dimming, 0.0f, 1.0f));
883 }
884 
JSOpenBindSheet(const JSCallbackInfo & info)885 void JSViewContext::JSOpenBindSheet(const JSCallbackInfo& info)
886 {
887     auto paramCnt = info.Length();
888     if (paramCnt < LENGTH_ONE) {
889         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
890         return;
891     }
892 
893     auto sheetContentNode = ParseContentNode(info);
894     if (sheetContentNode == nullptr) {
895         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
896         return;
897     }
898 
899     // parse SheetStyle and callbacks
900     NG::SheetStyle sheetStyle;
901     sheetStyle.sheetHeight.sheetMode = NG::SheetMode::LARGE;
902     sheetStyle.showDragBar = true;
903     sheetStyle.showInPage = false;
904     std::function<void()> onAppearCallback;
905     std::function<void()> onDisappearCallback;
906     std::function<void()> onWillAppearCallback;
907     std::function<void()> onWillDisappearCallback;
908     std::function<void()> shouldDismissFunc;
909     std::function<void(const int32_t)> onWillDismissCallback;
910     std::function<void(const float)> onHeightDidChangeCallback;
911     std::function<void(const float)> onDetentsDidChangeCallback;
912     std::function<void(const float)> onWidthDidChangeCallback;
913     std::function<void(const float)> onTypeDidChangeCallback;
914     std::function<void()> titleBuilderFunction;
915     std::function<void()> sheetSpringBackFunc;
916     if (paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject()) {
917         JSViewAbstract::ParseSheetCallback(info[INDEX_ONE], onAppearCallback, onDisappearCallback, shouldDismissFunc,
918             onWillDismissCallback, onWillAppearCallback, onWillDisappearCallback, onHeightDidChangeCallback,
919             onDetentsDidChangeCallback, onWidthDidChangeCallback, onTypeDidChangeCallback, sheetSpringBackFunc);
920         JSViewAbstract::ParseSheetStyle(info[INDEX_ONE], sheetStyle);
921         JSViewAbstract::ParseSheetTitle(info[INDEX_ONE], sheetStyle, titleBuilderFunction);
922     }
923 
924     int32_t targetId = INVALID_ID;
925     if (paramCnt >= LENGTH_THREE) {
926         if (!info[INDEX_TWO]->IsNumber()) {
927             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
928             return;
929         }
930         targetId = info[INDEX_TWO]->ToNumber<int32_t>();
931         if (targetId < 0) {
932             ReturnPromise(info, ERROR_CODE_TARGET_ID_NOT_EXIST);
933             return;
934         }
935     }
936     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d, targetId: %{public}d",
937         paramCnt, sheetContentNode->GetId(), targetId);
938     auto ret = ViewContextModel::GetInstance()->OpenBindSheet(sheetContentNode,
939         std::move(titleBuilderFunction), sheetStyle, std::move(onAppearCallback), std::move(onDisappearCallback),
940         std::move(shouldDismissFunc), std::move(onWillDismissCallback),  std::move(onWillAppearCallback),
941         std::move(onWillDisappearCallback), std::move(onHeightDidChangeCallback),
942         std::move(onDetentsDidChangeCallback), std::move(onWidthDidChangeCallback),
943         std::move(onTypeDidChangeCallback), std::move(sheetSpringBackFunc), Container::CurrentId(), targetId);
944 
945     ReturnPromise(info, ret);
946     return;
947 }
948 
JSUpdateBindSheet(const JSCallbackInfo & info)949 void JSViewContext::JSUpdateBindSheet(const JSCallbackInfo& info)
950 {
951     auto paramCnt = info.Length();
952     if (paramCnt < LENGTH_TWO) {
953         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
954         return;
955     }
956     auto sheetContentNode = ParseContentNode(info);
957     if (sheetContentNode == nullptr) {
958         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
959         return;
960     }
961 
962     bool isPartialUpdate = false;
963     if (paramCnt == LENGTH_THREE) {
964         if (!info[INDEX_TWO]->IsBoolean()) {
965             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
966             return;
967         }
968         isPartialUpdate = info[INDEX_TWO]->ToBoolean();
969     }
970 
971     NG::SheetStyle sheetStyle;
972     std::function<void()> titleBuilderFunction;
973     if (paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject()) {
974         JSViewAbstract::ParseSheetStyle(info[INDEX_ONE], sheetStyle, isPartialUpdate);
975         JSViewAbstract::ParseSheetTitle(info[INDEX_ONE], sheetStyle, titleBuilderFunction);
976     } else {
977         sheetStyle.sheetHeight.sheetMode = NG::SheetMode::LARGE;
978         sheetStyle.showDragBar = true;
979         sheetStyle.showInPage = false;
980         isPartialUpdate = false;
981     }
982     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d, isPartialUpdate: %{public}d",
983         paramCnt, sheetContentNode->GetId(), isPartialUpdate);
984     auto ret = ViewContextModel::GetInstance()->UpdateBindSheet(
985         sheetContentNode, sheetStyle, isPartialUpdate, Container::CurrentId());
986     ReturnPromise(info, ret);
987     return;
988 }
989 
JSCloseBindSheet(const JSCallbackInfo & info)990 void JSViewContext::JSCloseBindSheet(const JSCallbackInfo& info)
991 {
992     auto paramCnt = info.Length();
993     if (paramCnt < LENGTH_ONE) {
994         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
995         return;
996     }
997 
998     auto sheetContentNode = ParseContentNode(info);
999     if (sheetContentNode == nullptr) {
1000         ReturnPromise(info, ERROR_CODE_BIND_SHEET_CONTENT_ERROR);
1001         return;
1002     }
1003 
1004     TAG_LOGI(AceLogTag::ACE_SHEET, "paramCnt: %{public}d, contentId: %{public}d", paramCnt, sheetContentNode->GetId());
1005     auto ret =
1006         ViewContextModel::GetInstance()->CloseBindSheet(sheetContentNode, Container::CurrentId());
1007     ReturnPromise(info, ret);
1008     return;
1009 }
1010 
ParseTargetInfo(const JSRef<JSObject> & obj,int32_t & targetId)1011 int32_t ParseTargetInfo(const JSRef<JSObject>& obj, int32_t& targetId)
1012 {
1013     CHECK_EQUAL_RETURN(obj->IsEmpty(), true, ERROR_CODE_PARAM_INVALID);
1014     auto targetInfoID = obj->GetProperty("id");
1015     if (targetInfoID->IsNumber()) {
1016         targetId = targetInfoID->ToNumber<int32_t>();
1017     } else if (targetInfoID->IsString()) {
1018         std::string targetIdString = targetInfoID->ToString();
1019         auto targetInfoComponentId = obj->GetProperty("componentId");
1020         if (targetInfoComponentId->IsNumber()) {
1021             auto componentId = targetInfoComponentId->ToNumber<int32_t>();
1022             auto targetComponentIdNode =
1023                 ElementRegister::GetInstance()->GetSpecificItemById<NG::FrameNode>(componentId);
1024             CHECK_NULL_RETURN(targetComponentIdNode, ERROR_CODE_TARGET_INFO_NOT_EXIST);
1025             if (targetComponentIdNode->GetInspectorId().value_or("") == targetIdString) {
1026                 targetId = targetComponentIdNode->GetId();
1027                 return ERROR_CODE_NO_ERROR;
1028             }
1029             auto targetNode = NG::FrameNode::FindChildByName(targetComponentIdNode, targetIdString);
1030             CHECK_NULL_RETURN(targetNode, ERROR_CODE_TARGET_INFO_NOT_EXIST);
1031             targetId = targetNode->GetId();
1032         } else {
1033             auto targetNode = ElementRegister::GetInstance()->GetAttachedFrameNodeById(targetIdString);
1034             CHECK_NULL_RETURN(targetNode, ERROR_CODE_TARGET_INFO_NOT_EXIST);
1035             targetId = targetNode->GetId();
1036         }
1037     }
1038     if (targetId < 0) {
1039         return ERROR_CODE_PARAM_INVALID;
1040     }
1041     return ERROR_CODE_NO_ERROR;
1042 }
1043 
JSOpenPopup(const JSCallbackInfo & info)1044 void JSViewContext::JSOpenPopup(const JSCallbackInfo& info)
1045 {
1046     auto paramCnt = info.Length();
1047     if (paramCnt < LENGTH_TWO) {
1048         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1049         return;
1050     }
1051     auto popupContentNode = ParseContentNode(info);
1052     if (popupContentNode == nullptr) {
1053         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1054         return;
1055     }
1056     auto popupParam = AceType::MakeRefPtr<PopupParam>();
1057     CHECK_NULL_VOID(popupParam);
1058     popupParam->SetIsShow(true);
1059     popupParam->SetUseCustomComponent(true);
1060     if (info[INDEX_ONE]->IsObject()) {
1061         auto popupObj = JSRef<JSObject>::Cast(info[INDEX_ONE]);
1062         int32_t targetId = INVALID_ID;
1063         auto result = ParseTargetInfo(popupObj, targetId);
1064         if (result == ERROR_CODE_NO_ERROR) {
1065             popupParam->SetTargetId(std::to_string(targetId));
1066         } else {
1067             ReturnPromise(info, result);
1068             return;
1069         }
1070     } else {
1071         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1072         return;
1073     }
1074     if (paramCnt == LENGTH_THREE && info[INDEX_TWO]->IsObject()) {
1075         auto popupObj = JSRef<JSObject>::Cast(info[INDEX_TWO]);
1076         JSViewAbstract::ParseContentPopupCommonParam(info, popupObj, popupParam);
1077     }
1078     auto ret = JSViewAbstract::OpenPopup(popupParam, popupContentNode);
1079     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1080         ret = ERROR_CODE_NO_ERROR;
1081     }
1082     ReturnPromise(info, ret);
1083     return;
1084 }
1085 
UpdateParsePopupParam(const JSCallbackInfo & info,RefPtr<PopupParam> & popupParam,const RefPtr<NG::UINode> & customNode,bool isPartialUpdate)1086 bool UpdateParsePopupParam(const JSCallbackInfo& info, RefPtr<PopupParam>& popupParam,
1087     const RefPtr<NG::UINode>& customNode, bool isPartialUpdate)
1088 {
1089     if ((!popupParam) || (!customNode)) {
1090         return false;
1091     }
1092     auto paramCnt = info.Length();
1093     if (!(paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject())) {
1094         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1095         return false;
1096     }
1097     auto param = AceType::MakeRefPtr<PopupParam>();
1098     CHECK_NULL_RETURN(param, false);
1099     auto result = JSViewAbstract::GetPopupParam(param, customNode);
1100     if (result == ERROR_CODE_NO_ERROR) {
1101         if (isPartialUpdate) {
1102             popupParam = param;
1103         } else {
1104             popupParam->SetTargetId(param->GetTargetId());
1105         }
1106     } else {
1107         if (result == ERROR_CODE_INTERNAL_ERROR) {
1108             result = ERROR_CODE_NO_ERROR;
1109         }
1110         ReturnPromise(info, result);
1111         return false;
1112     }
1113     auto isShowInSubWindow = param->IsShowInSubWindow();
1114     auto focusable = param->GetFocusable();
1115     popupParam->SetIsShow(true);
1116     popupParam->SetUseCustomComponent(true);
1117     popupParam->SetIsPartialUpdate(isPartialUpdate);
1118     auto popupObj = JSRef<JSObject>::Cast(info[INDEX_ONE]);
1119     JSViewAbstract::ParseContentPopupCommonParam(info, popupObj, popupParam);
1120     popupParam->SetShowInSubWindow(isShowInSubWindow);
1121     popupParam->SetFocusable(focusable);
1122     return true;
1123 }
1124 
JSUpdatePopup(const JSCallbackInfo & info)1125 void JSViewContext::JSUpdatePopup(const JSCallbackInfo& info)
1126 {
1127     auto paramCnt = info.Length();
1128     if (paramCnt < LENGTH_TWO) {
1129         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1130         return;
1131     }
1132     auto popupContentNode = ParseContentNode(info);
1133     if (popupContentNode == nullptr) {
1134         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1135         return;
1136     }
1137     auto popupParam = AceType::MakeRefPtr<PopupParam>();
1138     CHECK_NULL_VOID(popupParam);
1139     bool isPartialUpdate = false;
1140     if (paramCnt == LENGTH_THREE) {
1141         if (!info[INDEX_TWO]->IsBoolean()) {
1142             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1143             return;
1144         }
1145         isPartialUpdate = info[INDEX_TWO]->ToBoolean();
1146     }
1147     auto result = UpdateParsePopupParam(info, popupParam, popupContentNode, isPartialUpdate);
1148     if (!result) {
1149         return;
1150     }
1151     auto ret = JSViewAbstract::UpdatePopup(popupParam, popupContentNode);
1152     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1153         ret = ERROR_CODE_NO_ERROR;
1154     }
1155     ReturnPromise(info, ret);
1156     return;
1157 }
1158 
JSClosePopup(const JSCallbackInfo & info)1159 void JSViewContext::JSClosePopup(const JSCallbackInfo& info)
1160 {
1161     auto paramCnt = info.Length();
1162     if (paramCnt < LENGTH_ONE) {
1163         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1164         return;
1165     }
1166     auto popupContentNode = ParseContentNode(info);
1167     if (popupContentNode == nullptr) {
1168         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1169         return;
1170     }
1171     auto ret = JSViewAbstract::ClosePopup(popupContentNode);
1172     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1173         ret = ERROR_CODE_NO_ERROR;
1174     }
1175     ReturnPromise(info, ret);
1176     return;
1177 }
1178 
GetMenuParam(NG::MenuParam & menuParam,const RefPtr<NG::UINode> & node)1179 int32_t GetMenuParam(NG::MenuParam& menuParam, const RefPtr<NG::UINode>& node)
1180 {
1181     if (!node) {
1182         TAG_LOGE(AceLogTag::ACE_DIALOG, "Content of menu is null.");
1183         return ERROR_CODE_DIALOG_CONTENT_ERROR;
1184     }
1185     auto context = node->GetContextWithCheck();
1186     CHECK_NULL_RETURN(context, ERROR_CODE_INTERNAL_ERROR);
1187     auto overlayManager = context->GetOverlayManager();
1188     if (!overlayManager) {
1189         return ERROR_CODE_INTERNAL_ERROR;
1190     }
1191     auto menuNode = overlayManager->GetMenuNodeWithExistContent(node);
1192     if (!menuNode) {
1193         TAG_LOGE(AceLogTag::ACE_DIALOG, "GetMenuParam failed because cannot find menuNode.");
1194         return ERROR_CODE_DIALOG_CONTENT_NOT_FOUND;
1195     }
1196     auto wrapperPattern = AceType::DynamicCast<NG::MenuWrapperPattern>(menuNode->GetPattern());
1197     CHECK_NULL_RETURN(wrapperPattern, ERROR_CODE_INTERNAL_ERROR);
1198     auto menuProperties = wrapperPattern->GetMenuParam();
1199     menuParam = menuProperties;
1200     return ERROR_CODE_NO_ERROR;
1201 }
1202 
JSOpenMenu(const JSCallbackInfo & info)1203 void JSViewContext::JSOpenMenu(const JSCallbackInfo& info)
1204 {
1205     auto paramCnt = info.Length();
1206     if (paramCnt < LENGTH_TWO) {
1207         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1208         return;
1209     }
1210     auto menuContentNode = ParseContentNode(info);
1211     if (menuContentNode == nullptr) {
1212         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1213         return;
1214     }
1215     int32_t targetId = INVALID_ID;
1216     if (info[INDEX_ONE]->IsObject()) {
1217         auto menuObj = JSRef<JSObject>::Cast(info[INDEX_ONE]);
1218         auto result = ParseTargetInfo(menuObj, targetId);
1219         if (result != ERROR_CODE_NO_ERROR) {
1220             ReturnPromise(info, result);
1221             return;
1222         }
1223     } else {
1224         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1225         return;
1226     }
1227     NG::MenuParam menuParam;
1228     JSRef<JSObject> menuObj;
1229     if (paramCnt == LENGTH_THREE && info[INDEX_TWO]->IsObject()) {
1230         menuObj = JSRef<JSObject>::Cast(info[INDEX_TWO]);
1231     }
1232     JSViewAbstract::ParseContentMenuCommonParam(info, menuObj, menuParam);
1233     auto ret = JSViewAbstract::OpenMenu(menuParam, menuContentNode, targetId);
1234     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1235         ret = ERROR_CODE_NO_ERROR;
1236     }
1237     ReturnPromise(info, ret);
1238     return;
1239 }
1240 
JSUpdateMenu(const JSCallbackInfo & info)1241 void JSViewContext::JSUpdateMenu(const JSCallbackInfo& info)
1242 {
1243     auto paramCnt = info.Length();
1244     if (paramCnt < LENGTH_TWO) {
1245         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1246         return;
1247     }
1248     auto menuContentNode = ParseContentNode(info);
1249     if (menuContentNode == nullptr) {
1250         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1251         return;
1252     }
1253     bool isPartialUpdate = false;
1254     if (paramCnt == LENGTH_THREE) {
1255         if (!info[INDEX_TWO]->IsBoolean()) {
1256             ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1257             return;
1258         }
1259         isPartialUpdate = info[INDEX_TWO]->ToBoolean();
1260     }
1261     NG::MenuParam menuParam;
1262     if (paramCnt >= LENGTH_TWO && info[INDEX_ONE]->IsObject()) {
1263         if (isPartialUpdate) {
1264             auto result = GetMenuParam(menuParam, menuContentNode);
1265             if (result != ERROR_CODE_NO_ERROR && result != ERROR_CODE_INTERNAL_ERROR) {
1266                 ReturnPromise(info, result);
1267                 return;
1268             }
1269         }
1270         auto menuObj = JSRef<JSObject>::Cast(info[INDEX_ONE]);
1271         JSViewAbstract::ParseContentMenuCommonParam(info, menuObj, menuParam);
1272     } else {
1273         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1274         return;
1275     }
1276     auto ret = JSViewAbstract::UpdateMenu(menuParam, menuContentNode);
1277     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1278         ret = ERROR_CODE_NO_ERROR;
1279     }
1280     ReturnPromise(info, ret);
1281     return;
1282 }
1283 
JSCloseMenu(const JSCallbackInfo & info)1284 void JSViewContext::JSCloseMenu(const JSCallbackInfo& info)
1285 {
1286     auto paramCnt = info.Length();
1287     if (paramCnt < LENGTH_ONE) {
1288         ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
1289         return;
1290     }
1291     auto menuContentNode = ParseContentNode(info);
1292     if (menuContentNode == nullptr) {
1293         ReturnPromise(info, ERROR_CODE_DIALOG_CONTENT_ERROR);
1294         return;
1295     }
1296     auto ret = JSViewAbstract::CloseMenu(menuContentNode);
1297     if (ret == ERROR_CODE_INTERNAL_ERROR) {
1298         ret = ERROR_CODE_NO_ERROR;
1299     }
1300     ReturnPromise(info, ret);
1301     return;
1302 }
1303 
IsFollowingSystemFontScale(const JSCallbackInfo & info)1304 void JSViewContext::IsFollowingSystemFontScale(const JSCallbackInfo& info)
1305 {
1306     auto container = Container::CurrentSafely();
1307     CHECK_NULL_VOID(container);
1308     auto pipelineContext = container->GetPipelineContext();
1309     CHECK_NULL_VOID(pipelineContext);
1310     auto follow = pipelineContext->IsFollowSystem();
1311     auto followRef = JSRef<JSVal>::Make(JSVal(ToJSValue(follow)));
1312     info.SetReturnValue(followRef);
1313     return;
1314 }
1315 
GetMaxFontScale(const JSCallbackInfo & info)1316 void JSViewContext::GetMaxFontScale(const JSCallbackInfo& info)
1317 {
1318     auto container = Container::CurrentSafely();
1319     CHECK_NULL_VOID(container);
1320     auto pipelineContext = container->GetPipelineContext();
1321     CHECK_NULL_VOID(pipelineContext);
1322     auto maxFontScale = pipelineContext->GetMaxAppFontScale();
1323     auto maxFontScaleRef = JSRef<JSVal>::Make(JSVal(ToJSValue(maxFontScale)));
1324     info.SetReturnValue(maxFontScaleRef);
1325     return;
1326 }
1327 
SetEnableSwipeBack(const JSCallbackInfo & info)1328 void JSViewContext::SetEnableSwipeBack(const JSCallbackInfo& info)
1329 {
1330     if (info.Length() < 1) {
1331         return;
1332     }
1333     if (!info[0]->IsBoolean()) {
1334         return;
1335     }
1336     auto container = Container::CurrentSafely();
1337     CHECK_NULL_VOID(container);
1338     auto pipelineContext = container->GetPipelineContext();
1339     CHECK_NULL_VOID(pipelineContext);
1340     pipelineContext->SetEnableSwipeBack(info[0]->ToBoolean());
1341 }
1342 
JSBind(BindingTarget globalObj)1343 void JSViewContext::JSBind(BindingTarget globalObj)
1344 {
1345     JSClass<JSViewContext>::Declare("Context");
1346     JSClass<JSViewContext>::StaticMethod("animation", JSAnimation);
1347     JSClass<JSViewContext>::StaticMethod("animateTo", JSAnimateTo);
1348     JSClass<JSViewContext>::StaticMethod("animateToImmediately", JSAnimateToImmediately);
1349     JSClass<JSViewContext>::StaticMethod("keyframeAnimateTo", JSKeyframeAnimateTo);
1350     JSClass<JSViewContext>::StaticMethod("setDynamicDimming", SetDynamicDimming);
1351     JSClass<JSViewContext>::StaticMethod("openBindSheet", JSOpenBindSheet);
1352     JSClass<JSViewContext>::StaticMethod("updateBindSheet", JSUpdateBindSheet);
1353     JSClass<JSViewContext>::StaticMethod("closeBindSheet", JSCloseBindSheet);
1354     JSClass<JSViewContext>::StaticMethod("openPopup", JSOpenPopup);
1355     JSClass<JSViewContext>::StaticMethod("updatePopup", JSUpdatePopup);
1356     JSClass<JSViewContext>::StaticMethod("closePopup", JSClosePopup);
1357     JSClass<JSViewContext>::StaticMethod("openMenu", JSOpenMenu);
1358     JSClass<JSViewContext>::StaticMethod("updateMenu", JSUpdateMenu);
1359     JSClass<JSViewContext>::StaticMethod("closeMenu", JSCloseMenu);
1360     JSClass<JSViewContext>::StaticMethod("isFollowingSystemFontScale", IsFollowingSystemFontScale);
1361     JSClass<JSViewContext>::StaticMethod("getMaxFontScale", GetMaxFontScale);
1362     JSClass<JSViewContext>::StaticMethod("bindTabsToScrollable", JSTabsFeature::BindTabsToScrollable);
1363     JSClass<JSViewContext>::StaticMethod("unbindTabsFromScrollable", JSTabsFeature::UnbindTabsFromScrollable);
1364     JSClass<JSViewContext>::StaticMethod("bindTabsToNestedScrollable", JSTabsFeature::BindTabsToNestedScrollable);
1365     JSClass<JSViewContext>::StaticMethod(
1366         "unbindTabsFromNestedScrollable", JSTabsFeature::UnbindTabsFromNestedScrollable);
1367     JSClass<JSViewContext>::StaticMethod("enableSwipeBack", JSViewContext::SetEnableSwipeBack);
1368     JSClass<JSViewContext>::Bind<>(globalObj);
1369 }
1370 
1371 } // namespace OHOS::Ace::Framework
1372