• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "bridge/declarative_frontend/jsview/js_view_context.h"
17 
18 #include <functional>
19 
20 #include "base/log/jank_frame_report.h"
21 #include "base/utils/system_properties.h"
22 #include "bridge/common/utils/engine_helper.h"
23 #include "bridge/common/utils/utils.h"
24 #include "bridge/declarative_frontend/engine/functions/js_function.h"
25 #include "bridge/declarative_frontend/jsview/models/view_context_model_impl.h"
26 #include "core/common/ace_engine.h"
27 #include "core/components_ng/pattern/view_context/view_context_model_ng.h"
28 
29 #ifdef USE_ARK_ENGINE
30 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
31 #endif
32 
33 namespace OHOS::Ace {
34 
35 std::unique_ptr<ViewContextModel> ViewContextModel::instance_ = nullptr;
36 std::mutex ViewContextModel::mutex_;
37 
GetInstance()38 ViewContextModel* ViewContextModel::GetInstance()
39 {
40     if (!instance_) {
41         std::lock_guard<std::mutex> lock(mutex_);
42         if (!instance_) {
43 #ifdef NG_BUILD
44             instance_.reset(new NG::ViewContextModelNG());
45 #else
46             if (Container::IsCurrentUseNewPipeline()) {
47                 instance_.reset(new NG::ViewContextModelNG());
48             } else {
49                 instance_.reset(new Framework::ViewContextModelImpl());
50             }
51 #endif
52         }
53     }
54     return instance_.get();
55 }
56 
57 } // namespace OHOS::Ace
58 
59 namespace OHOS::Ace::Framework {
60 namespace {
61 
62 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
63 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,JSRef<JSFunc> jsAnimateToFunc,std::function<void ()> & onFinishEvent)64 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
65     JSRef<JSFunc> jsAnimateToFunc, std::function<void()>& onFinishEvent)
66 {
67     auto triggerId = Container::CurrentId();
68     AceEngine::Get().NotifyContainers([triggerId, option](const RefPtr<Container>& container) {
69         auto context = container->GetPipelineContext();
70         if (!context) {
71             // pa container do not have pipeline context.
72             return;
73         }
74         if (!container->GetSettings().usingSharedRuntime) {
75             return;
76         }
77         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
78             return;
79         }
80         auto frontendType = context->GetFrontendType();
81         if (frontendType != FrontendType::DECLARATIVE_JS && frontendType != FrontendType::JS_PLUGIN) {
82             LOGW("Not compatible frontType(%{public}d) for declarative. containerId: %{public}d", frontendType,
83                 container->GetInstanceId());
84         }
85         ContainerScope scope(container->GetInstanceId());
86         context->FlushBuild();
87         if (context->GetInstanceId() == triggerId) {
88             return;
89         }
90         context->PrepareOpenImplicitAnimation();
91     });
92     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
93     pipelineContext->SetSyncAnimationOption(option);
94     // Execute the function.
95     jsAnimateToFunc->Call(jsAnimateToFunc);
96     AceEngine::Get().NotifyContainers([triggerId](const RefPtr<Container>& container) {
97         auto context = container->GetPipelineContext();
98         if (!context) {
99             // pa container do not have pipeline context.
100             return;
101         }
102         if (!container->GetSettings().usingSharedRuntime) {
103             return;
104         }
105         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
106             return;
107         }
108         auto frontendType = context->GetFrontendType();
109         if (frontendType != FrontendType::DECLARATIVE_JS && frontendType != FrontendType::JS_PLUGIN) {
110             LOGW("Not compatible frontType(%{public}d) for declarative. containerId: %{public}d", frontendType,
111                 container->GetInstanceId());
112         }
113         ContainerScope scope(container->GetInstanceId());
114         context->FlushBuild();
115         if (context->GetInstanceId() == triggerId) {
116             return;
117         }
118         context->PrepareCloseImplicitAnimation();
119     });
120     pipelineContext->SetSyncAnimationOption(AnimationOption());
121     pipelineContext->CloseImplicitAnimation();
122 }
123 
AnimateToForFaMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,const JSCallbackInfo & info,std::function<void ()> & onFinishEvent)124 void AnimateToForFaMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
125     const JSCallbackInfo& info, std::function<void()>& onFinishEvent)
126 {
127     pipelineContext->FlushBuild();
128     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
129     pipelineContext->SetSyncAnimationOption(option);
130     JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
131     jsAnimateToFunc->Call(info[1]);
132     pipelineContext->FlushBuild();
133     pipelineContext->SetSyncAnimationOption(AnimationOption());
134     pipelineContext->CloseImplicitAnimation();
135 }
136 
137 } // namespace
138 
CreateAnimation(const std::unique_ptr<JsonValue> & animationArgs,const std::function<float (float)> & jsFunc,bool isForm)139 const AnimationOption JSViewContext::CreateAnimation(
140     const std::unique_ptr<JsonValue>& animationArgs, const std::function<float(float)>& jsFunc, bool isForm)
141 {
142     AnimationOption option = AnimationOption();
143     if (!animationArgs) {
144         LOGW("CreateAnimation: animationArgs is null");
145         return option;
146     }
147     // If the attribute does not exist, the default value is used.
148     auto duration = animationArgs->GetInt("duration", DEFAULT_DURATION);
149     auto delay = animationArgs->GetInt("delay", 0);
150     auto iterations = animationArgs->GetInt("iterations", 1);
151     auto tempo = animationArgs->GetDouble("tempo", 1.0);
152     if (SystemProperties::GetRosenBackendEnabled() && NearZero(tempo)) {
153         // set duration to 0 to disable animation.
154         LOGI("tempo near 0, set duration to 0.");
155         duration = 0;
156     }
157     auto direction = StringToAnimationDirection(animationArgs->GetString("playMode", "normal"));
158     RefPtr<Curve> curve;
159     auto curveArgs = animationArgs->GetValue("curve");
160     if (curveArgs->IsString()) {
161         curve = CreateCurve(animationArgs->GetString("curve", DOM_ANIMATION_TIMING_FUNCTION_EASE_IN_OUT));
162     } else if (curveArgs->IsObject()) {
163         auto curveString = curveArgs->GetValue("__curveString");
164         if (!curveString) {
165             // Default AnimationOption which is invalid.
166             return option;
167         }
168         auto aniTimFunc = curveString->GetString();
169 
170         std::string customFuncName(DOM_ANIMATION_TIMING_FUNCTION_CUSTOM);
171         if (aniTimFunc == customFuncName) {
172             curve = CreateCurve(jsFunc);
173         } else {
174             curve = CreateCurve(aniTimFunc);
175         }
176     } else {
177         curve = Curves::EASE_IN_OUT;
178     }
179 
180     // limit animation for ArkTS Form
181     if (isForm) {
182         if (duration > static_cast<int32_t>(DEFAULT_DURATION)) {
183             LOGW("Form delay is not allowed to be set to a value greater than 1000ms, set it to 1000ms");
184             duration = static_cast<int32_t>(DEFAULT_DURATION);
185         }
186         if (delay != 0) {
187             LOGW("Form delay is not allowed to be set to a value other than 0, set it to 0");
188             delay = 0;
189         }
190         if (SystemProperties::IsFormAnimationLimited() && iterations != 1) {
191             LOGW("Form iterations is not allowed to be set to a value other than 1, set it to 1.");
192             iterations = 1;
193         }
194         if (!NearEqual(tempo, 1.0)) {
195             LOGW("Form tempo is not allowed to be set to a value other than 1.0, set it to 1.0.");
196             tempo = 1.0;
197         }
198     }
199 
200     option.SetDuration(duration);
201     option.SetDelay(delay);
202     option.SetIteration(iterations);
203     option.SetTempo(tempo);
204     option.SetAnimationDirection(direction);
205     option.SetCurve(curve);
206     return option;
207 }
208 
ParseCallBackFunction(const JSRef<JSObject> & obj)209 std::function<float(float)> ParseCallBackFunction(const JSRef<JSObject>& obj)
210 {
211     std::function<float(float)> customCallBack = nullptr;
212     JSRef<JSVal> curveVal = obj->GetProperty("curve");
213     if (curveVal->IsObject()) {
214         JSRef<JSObject> curveobj = JSRef<JSObject>::Cast(curveVal);
215         JSRef<JSVal> onCallBack = curveobj->GetProperty("__curveCustomFunc");
216         if (onCallBack->IsFunction()) {
217             RefPtr<JsFunction> jsFuncCallBack =
218                 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCallBack));
219             customCallBack = [func = std::move(jsFuncCallBack), id = Container::CurrentId()](float time) -> float {
220                 ContainerScope scope(id);
221                 JSRef<JSVal> params[1];
222                 params[0] = JSRef<JSVal>::Make(ToJSValue(time));
223                 auto result = func->ExecuteJS(1, params);
224                 auto resultValue = result->IsNumber() ? result->ToNumber<float>() : 1.0f;
225                 if (resultValue < 0 || resultValue > 1) {
226                     LOGI("The interpolate return  value error = %{public}f ", resultValue);
227                 }
228                 return resultValue;
229             };
230         }
231     }
232     return customCallBack;
233 }
234 
JSAnimation(const JSCallbackInfo & info)235 void JSViewContext::JSAnimation(const JSCallbackInfo& info)
236 {
237     ACE_FUNCTION_TRACE();
238     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
239     if (!scopedDelegate) {
240         // this case usually means there is no foreground container, need to figure out the reason.
241         LOGE("scopedDelegate is null, please check");
242         return;
243     }
244     if (info.Length() < 1) {
245         LOGE("The arg is wrong, it is supposed to have 1 object argument.");
246         return;
247     }
248     AnimationOption option = AnimationOption();
249     auto container = Container::Current();
250     CHECK_NULL_VOID(container);
251     auto pipelineContextBase = container->GetPipelineContext();
252     CHECK_NULL_VOID(pipelineContextBase);
253     if (!pipelineContextBase->GetEnableImplicitAnimation() && pipelineContextBase->IsFormRender()) {
254         LOGW("Form need enable implicit animation in finish callback.");
255         return;
256     }
257     if (info[0]->IsNull() || !info[0]->IsObject()) {
258         ViewContextModel::GetInstance()->closeAnimation(option, true);
259         return;
260     }
261     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
262     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
263     std::function<void()> onFinishEvent;
264     if (onFinish->IsFunction()) {
265         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
266         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
267                             id = Container::CurrentId()]() {
268             ContainerScope scope(id);
269             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
270             func->Execute();
271         };
272     }
273     auto animationArgs = JsonUtil::ParseJsonString(info[0]->ToString());
274     if (animationArgs->IsNull()) {
275         LOGE("Js Parse failed. animationArgs is null.");
276         ViewContextModel::GetInstance()->closeAnimation(option, false);
277         return;
278     }
279 
280     option = CreateAnimation(animationArgs, ParseCallBackFunction(obj), pipelineContextBase->IsFormRender());
281     option.SetOnFinishEvent(onFinishEvent);
282     if (SystemProperties::GetRosenBackendEnabled()) {
283         option.SetAllowRunningAsynchronously(true);
284     }
285     ViewContextModel::GetInstance()->openAnimation(option);
286     JankFrameReport::ReportJSAnimation();
287 }
288 
JSAnimateTo(const JSCallbackInfo & info)289 void JSViewContext::JSAnimateTo(const JSCallbackInfo& info)
290 {
291     ACE_FUNCTION_TRACE();
292     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
293     if (!scopedDelegate) {
294         // this case usually means there is no foreground container, need to figure out the reason.
295         LOGE("scopedDelegate is null, please check");
296         return;
297     }
298     if (info.Length() < 2) {
299         LOGE("The arg is wrong, it is supposed to have two arguments.");
300         return;
301     }
302     if (!info[0]->IsObject()) {
303         LOGE("1st argument is not object.");
304         return;
305     }
306     // 2nd argument should be a closure passed to the animateTo function.
307     if (!info[1]->IsFunction()) {
308         LOGE("2nd argument is not a function.");
309         return;
310     }
311 
312     auto container = Container::Current();
313     CHECK_NULL_VOID(container);
314     auto pipelineContext = container->GetPipelineContext();
315     CHECK_NULL_VOID(pipelineContext);
316     if (!pipelineContext->GetEnableImplicitAnimation() && pipelineContext->IsFormRender()) {
317         LOGW("Form need enable implicit animation in finish callback.");
318         return;
319     }
320 
321     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
322     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
323     std::function<void()> onFinishEvent;
324     if (onFinish->IsFunction()) {
325         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
326         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
327                             id = Container::CurrentId()]() {
328             ContainerScope scope(id);
329             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
330             func->Execute();
331         };
332     }
333 
334     auto animationArgs = JsonUtil::ParseJsonString(info[0]->ToString());
335     if (animationArgs->IsNull()) {
336         LOGE("Js Parse failed. animationArgs is null.");
337         return;
338     }
339 
340     AnimationOption option =
341         CreateAnimation(animationArgs, ParseCallBackFunction(obj), pipelineContext->IsFormRender());
342     if (SystemProperties::GetRosenBackendEnabled()) {
343         bool usingSharedRuntime = container->GetSettings().usingSharedRuntime;
344         LOGD("RSAnimationInfo: Begin JSAnimateTo, usingSharedRuntime: %{public}d", usingSharedRuntime);
345         if (usingSharedRuntime) {
346             if (pipelineContext->IsLayouting()) {
347                 LOGW("pipeline is layouting, post animateTo, duration:%{public}d, curve:%{public}s",
348                     option.GetDuration(), option.GetCurve() ? option.GetCurve()->ToString().c_str() : "");
349                 pipelineContext->GetTaskExecutor()->PostTask(
350                     [id = Container::CurrentId(), option, func = JSRef<JSFunc>::Cast(info[1]),
351                         onFinishEvent]() mutable {
352                         ContainerScope scope(id);
353                         auto container = Container::Current();
354                         CHECK_NULL_VOID(container);
355                         auto pipelineContext = container->GetPipelineContext();
356                         CHECK_NULL_VOID(pipelineContext);
357                         AnimateToForStageMode(pipelineContext, option, func, onFinishEvent);
358                     },
359                     TaskExecutor::TaskType::UI);
360                 return;
361             }
362             AnimateToForStageMode(pipelineContext, option, JSRef<JSFunc>::Cast(info[1]), onFinishEvent);
363         } else {
364             AnimateToForFaMode(pipelineContext, option, info, onFinishEvent);
365         }
366         LOGD("RSAnimationInfo: End JSAnimateTo");
367     } else {
368         pipelineContext->FlushBuild();
369         pipelineContext->SaveExplicitAnimationOption(option);
370         // Execute the function.
371         JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
372         jsAnimateToFunc->Call(info[1]);
373         pipelineContext->FlushBuild();
374         pipelineContext->CreateExplicitAnimator(onFinishEvent);
375         pipelineContext->ClearExplicitAnimationOption();
376     }
377 }
378 
JSBind(BindingTarget globalObj)379 void JSViewContext::JSBind(BindingTarget globalObj)
380 {
381     JSClass<JSViewContext>::Declare("Context");
382     JSClass<JSViewContext>::StaticMethod("animation", JSAnimation);
383     JSClass<JSViewContext>::StaticMethod("animateTo", JSAnimateTo);
384     JSClass<JSViewContext>::Bind<>(globalObj);
385 }
386 
387 } // namespace OHOS::Ace::Framework
388