• 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/utils/system_properties.h"
21 #include "bridge/common/utils/engine_helper.h"
22 #include "bridge/common/utils/utils.h"
23 #include "bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "bridge/declarative_frontend/view_stack_processor.h"
25 #include "core/common/ace_engine.h"
26 #include "core/common/container_scope.h"
27 #include "core/components/common/properties/animation_option.h"
28 #include "core/components_ng/base/view_stack_processor.h"
29 
30 #ifdef USE_V8_ENGINE
31 #include "bridge/declarative_frontend/engine/v8/v8_declarative_engine.h"
32 #elif USE_QUICKJS_ENGINE
33 #include "bridge/declarative_frontend/engine/quickjs/qjs_declarative_engine_instance.h"
34 #elif USE_ARK_ENGINE
35 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
36 #endif
37 
38 namespace OHOS::Ace::Framework {
39 namespace {
40 
41 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
42 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,const JSCallbackInfo & info,std::function<void ()> & onFinishEvent)43 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
44     const JSCallbackInfo& info, std::function<void()>& onFinishEvent)
45 {
46     auto triggerId = Container::CurrentId();
47     AceEngine::Get().NotifyContainers([triggerId, option](const RefPtr<Container>& container) {
48         auto context = container->GetPipelineContext();
49         if (!context) {
50             // pa container do not have pipeline context.
51             return;
52         }
53         if (!container->GetSettings().usingSharedRuntime) {
54             return;
55         }
56         auto frontendType = context->GetFrontendType();
57         if (frontendType != FrontendType::DECLARATIVE_JS && frontendType != FrontendType::JS_PLUGIN) {
58             LOGW("Not compatible frontType(%{public}d) for declarative. containerId: %{public}d", frontendType,
59                 container->GetInstanceId());
60         }
61         ContainerScope scope(container->GetInstanceId());
62         context->FlushBuild();
63         if (context->GetInstanceId() == triggerId) {
64             return;
65         }
66         context->PrepareOpenImplicitAnimation();
67     });
68     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
69     if (!Container::IsCurrentUseNewPipeline()) {
70         pipelineContext->SetSyncAnimationOption(option);
71     }
72     // Execute the function.
73     JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
74     jsAnimateToFunc->Call(info[1]);
75     AceEngine::Get().NotifyContainers([triggerId](const RefPtr<Container>& container) {
76         auto context = container->GetPipelineContext();
77         if (!context) {
78             // pa container do not have pipeline context.
79             return;
80         }
81         if (!container->GetSettings().usingSharedRuntime) {
82             return;
83         }
84         auto frontendType = context->GetFrontendType();
85         if (frontendType != FrontendType::DECLARATIVE_JS && frontendType != FrontendType::JS_PLUGIN) {
86             LOGW("Not compatible frontType(%{public}d) for declarative. containerId: %{public}d", frontendType,
87                 container->GetInstanceId());
88         }
89         ContainerScope scope(container->GetInstanceId());
90         context->FlushBuild();
91         if (context->GetInstanceId() == triggerId) {
92             return;
93         }
94         context->PrepareCloseImplicitAnimation();
95     });
96     if (!Container::IsCurrentUseNewPipeline()) {
97         pipelineContext->SetSyncAnimationOption(AnimationOption());
98     }
99     pipelineContext->CloseImplicitAnimation();
100 }
101 
AnimateToForFaMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,const JSCallbackInfo & info,std::function<void ()> & onFinishEvent)102 void AnimateToForFaMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
103     const JSCallbackInfo& info, std::function<void()>& onFinishEvent)
104 {
105     pipelineContext->FlushBuild();
106     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
107     if (!Container::IsCurrentUseNewPipeline()) {
108         pipelineContext->SetSyncAnimationOption(option);
109     }
110     JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
111     jsAnimateToFunc->Call(info[1]);
112     pipelineContext->FlushBuild();
113     if (!Container::IsCurrentUseNewPipeline()) {
114         pipelineContext->SetSyncAnimationOption(AnimationOption());
115     }
116     pipelineContext->CloseImplicitAnimation();
117 }
118 
119 } // namespace
120 
CreateAnimation(const std::unique_ptr<JsonValue> & animationArgs,bool isForm)121 const AnimationOption JSViewContext::CreateAnimation(const std::unique_ptr<JsonValue>& animationArgs, bool isForm)
122 {
123     AnimationOption option = AnimationOption();
124     if (!animationArgs) {
125         LOGW("CreateAnimation: animationArgs is null");
126         return option;
127     }
128     // If the attribute does not exist, the default value is used.
129     auto duration = animationArgs->GetInt("duration", DEFAULT_DURATION);
130     auto delay = animationArgs->GetInt("delay", 0);
131     auto iterations = animationArgs->GetInt("iterations", 1);
132     auto tempo = animationArgs->GetDouble("tempo", 1.0);
133     auto direction = StringToAnimationDirection(animationArgs->GetString("playMode", "normal"));
134     RefPtr<Curve> curve;
135     auto curveArgs = animationArgs->GetValue("curve");
136     if (curveArgs->IsString()) {
137         curve = CreateCurve(animationArgs->GetString("curve", "linear"));
138     } else if (curveArgs->IsObject()) {
139         auto curveString = curveArgs->GetValue("__curveString");
140         if (!curveString) {
141             // Default AnimationOption which is invalid.
142             return option;
143         }
144         curve = CreateCurve(curveString->GetString());
145     } else {
146         curve = Curves::EASE_IN_OUT;
147     }
148 
149     // limit animation for ArkTS Form
150     if (isForm) {
151         duration = std::min(duration, static_cast<int32_t>(DEFAULT_DURATION));
152         delay = 0;
153         iterations = 1;
154         tempo = 1.0;
155     }
156 
157     option.SetDuration(duration);
158     option.SetDelay(delay);
159     option.SetIteration(iterations);
160     option.SetTempo(tempo);
161     option.SetAnimationDirection(direction);
162     option.SetCurve(curve);
163     return option;
164 }
165 
JSAnimation(const JSCallbackInfo & info)166 void JSViewContext::JSAnimation(const JSCallbackInfo& info)
167 {
168     LOGD("JSAnimation");
169     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
170     if (!scopedDelegate) {
171         // this case usually means there is no foreground container, need to figure out the reason.
172         LOGE("scopedDelegate is null, please check");
173         return;
174     }
175     if (info.Length() < 1) {
176         LOGE("The arg is wrong, it is supposed to have 1 object argument.");
177         return;
178     }
179     AnimationOption option = AnimationOption();
180     auto container = Container::Current();
181     CHECK_NULL_VOID(container);
182     auto pipelineContextBase = container->GetPipelineContext();
183     CHECK_NULL_VOID(pipelineContextBase);
184     if (info[0]->IsNull() || !info[0]->IsObject()) {
185         if (Container::IsCurrentUseNewPipeline()) {
186             NG::ViewStackProcessor::GetInstance()->FlushImplicitAnimation();
187             pipelineContextBase->CloseImplicitAnimation();
188         } else {
189             LOGE("JSAnimation: info[0] is null or not object.");
190             ViewStackProcessor::GetInstance()->SetImplicitAnimationOption(option);
191         }
192         return;
193     }
194     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
195     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
196     std::function<void()> onFinishEvent;
197     if (onFinish->IsFunction()) {
198         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
199         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
200                             id = Container::CurrentId()]() {
201             ContainerScope scope(id);
202             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
203             func->Execute();
204         };
205     }
206     auto animationArgs = JsonUtil::ParseJsonString(info[0]->ToString());
207     if (animationArgs->IsNull()) {
208         LOGE("Js Parse failed. animationArgs is null.");
209         if (Container::IsCurrentUseNewPipeline()) {
210             pipelineContextBase->CloseImplicitAnimation();
211         } else {
212             ViewStackProcessor::GetInstance()->SetImplicitAnimationOption(option);
213         }
214         return;
215     }
216     option = CreateAnimation(animationArgs, pipelineContextBase->IsFormRender());
217     option.SetOnFinishEvent(onFinishEvent);
218     if (SystemProperties::GetRosenBackendEnabled()) {
219         option.SetAllowRunningAsynchronously(true);
220     }
221     if (Container::IsCurrentUseNewPipeline()) {
222         pipelineContextBase->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
223     } else {
224         ViewStackProcessor::GetInstance()->SetImplicitAnimationOption(option);
225     }
226 }
227 
JSAnimateTo(const JSCallbackInfo & info)228 void JSViewContext::JSAnimateTo(const JSCallbackInfo& info)
229 {
230     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
231     if (!scopedDelegate) {
232         // this case usually means there is no foreground container, need to figure out the reason.
233         LOGE("scopedDelegate is null, please check");
234         return;
235     }
236     if (info.Length() < 2) {
237         LOGE("The arg is wrong, it is supposed to have two arguments.");
238         return;
239     }
240     if (!info[0]->IsObject()) {
241         LOGE("1st argument is not object.");
242         return;
243     }
244     // 2nd argument should be a closure passed to the animateTo function.
245     if (!info[1]->IsFunction()) {
246         LOGE("2nd argument is not a function.");
247         return;
248     }
249 
250     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
251     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
252     std::function<void()> onFinishEvent;
253     if (onFinish->IsFunction()) {
254         RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
255         onFinishEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
256                             id = Container::CurrentId()]() {
257             ContainerScope scope(id);
258             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
259             func->Execute();
260         };
261     }
262 
263     auto animationArgs = JsonUtil::ParseJsonString(info[0]->ToString());
264     if (animationArgs->IsNull()) {
265         LOGE("Js Parse failed. animationArgs is null.");
266         return;
267     }
268 
269     auto container = Container::Current();
270     CHECK_NULL_VOID(container);
271     auto pipelineContext = container->GetPipelineContext();
272     CHECK_NULL_VOID(pipelineContext);
273 
274     AnimationOption option = CreateAnimation(animationArgs, pipelineContext->IsFormRender());
275     if (SystemProperties::GetRosenBackendEnabled()) {
276         bool usingSharedRuntime = container->GetSettings().usingSharedRuntime;
277         LOGD("RSAnimationInfo: Begin JSAnimateTo, usingSharedRuntime: %{public}d", usingSharedRuntime);
278         if (usingSharedRuntime) {
279             AnimateToForStageMode(pipelineContext, option, info, onFinishEvent);
280         } else {
281             AnimateToForFaMode(pipelineContext, option, info, onFinishEvent);
282         }
283         LOGD("RSAnimationInfo: End JSAnimateTo");
284     } else {
285         pipelineContext->FlushBuild();
286         pipelineContext->SaveExplicitAnimationOption(option);
287         // Execute the function.
288         JSRef<JSFunc> jsAnimateToFunc = JSRef<JSFunc>::Cast(info[1]);
289         jsAnimateToFunc->Call(info[1]);
290         pipelineContext->FlushBuild();
291         pipelineContext->CreateExplicitAnimator(onFinishEvent);
292         pipelineContext->ClearExplicitAnimationOption();
293     }
294 }
295 
JSBind(BindingTarget globalObj)296 void JSViewContext::JSBind(BindingTarget globalObj)
297 {
298     JSClass<JSViewContext>::Declare("Context");
299     JSClass<JSViewContext>::StaticMethod("animation", JSAnimation);
300     JSClass<JSViewContext>::StaticMethod("animateTo", JSAnimateTo);
301     JSClass<JSViewContext>::Bind<>(globalObj);
302 }
303 
304 } // namespace OHOS::Ace::Framework
305