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