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