• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/v8/v8_animation_bridge.h"
17 
18 #include "base/log/event_report.h"
19 #include "base/log/log.h"
20 #include "base/memory/referenced.h"
21 #include "base/utils/string_utils.h"
22 #include "core/animation/keyframe_animation.h"
23 #include "core/components/tween/tween_component.h"
24 #include "frameworks/bridge/common/utils/utils.h"
25 #include "frameworks/bridge/js_frontend/engine/v8/v8_engine.h"
26 #include "frameworks/bridge/js_frontend/engine/v8/v8_utils.h"
27 #include "frameworks/bridge/js_frontend/js_ace_page.h"
28 
29 namespace OHOS::Ace::Framework {
30 namespace {
31 
GetPageById(v8::Isolate * isolate,int32_t pageId)32 RefPtr<JsAcePage> GetPageById(v8::Isolate* isolate, int32_t pageId)
33 {
34     LOGD("Enter GetPageById");
35     if (isolate == nullptr) {
36         LOGE("Isolate is null.");
37         return nullptr;
38     }
39     auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
40     return (*delegate)->GetPage(pageId);
41 }
42 
GetCurrentNodeId(const v8::Local<v8::Context> & ctx,v8::Local<v8::Object> value)43 inline NodeId GetCurrentNodeId(const v8::Local<v8::Context>& ctx, v8::Local<v8::Object> value)
44 {
45     v8::Isolate* isolate = ctx->GetIsolate();
46     v8::HandleScope handleScope(isolate);
47     NodeId id = value->Get(ctx, v8::String::NewFromUtf8(isolate, "__nodeId").ToLocalChecked())
48                     .ToLocalChecked()
49                     ->Int32Value(ctx)
50                     .ToChecked();
51     return id < 0 ? 0 : id;
52 }
53 
GetCurrentPageId(const v8::Local<v8::Context> & ctx,v8::Local<v8::Object> value)54 inline int32_t GetCurrentPageId(const v8::Local<v8::Context>& ctx, v8::Local<v8::Object> value)
55 {
56     v8::Isolate* isolate = ctx->GetIsolate();
57     v8::HandleScope handleScope(isolate);
58     int32_t id = value->Get(ctx, v8::String::NewFromUtf8(isolate, "__pageId").ToLocalChecked())
59                      .ToLocalChecked()
60                      ->Int32Value(ctx)
61                      .ToChecked();
62     return id < 0 ? 0 : id;
63 }
64 
HandleJsAnimationContext(const v8::Local<v8::Context> & ctx,int32_t pageId,int32_t nodeId,AnimationOperation operation)65 void HandleJsAnimationContext(
66     const v8::Local<v8::Context>& ctx, int32_t pageId, int32_t nodeId, AnimationOperation operation)
67 {
68     v8::Isolate* isolate = ctx->GetIsolate();
69     auto page = GetPageById(isolate, pageId);
70     if (!page) {
71         LOGE("no page found for nodeId: %{public}d", nodeId);
72         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
73         return;
74     }
75     auto task = AceType::MakeRefPtr<V8AnimationBridgeTaskOperation>(operation);
76     page->PushCommand(AceType::MakeRefPtr<JsCommandAnimation>(nodeId, task));
77     if (page->CheckPageCreated()) {
78         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
79         (*delegate)->TriggerPageUpdate(page->GetPageId());
80     }
81 }
82 
83 const std::vector<std::tuple<std::string, v8::FunctionCallback, v8::FunctionCallback>> V8_ANIMATION_FUNCS = {
84     { "playState", V8AnimationBridgeUtils::JsAnimationPlayStateGet, V8AnimationBridgeUtils::JsAnimationPlayStateSet },
85     { "startTime", V8AnimationBridgeUtils::JsAnimationStartTimeGet, V8AnimationBridgeUtils::JsAnimationStartTimeSet },
86     { "pending", V8AnimationBridgeUtils::JsAnimationPendingGet, V8AnimationBridgeUtils::JsAnimationPendingSet }
87 };
88 
CallAnimationStartJs(const WeakPtr<V8AnimationBridge> & bridgeWeak,v8::Isolate * isolate)89 void CallAnimationStartJs(const WeakPtr<V8AnimationBridge>& bridgeWeak, v8::Isolate* isolate)
90 {
91     auto bridge = bridgeWeak.Upgrade();
92     if (!bridge) {
93         LOGE("Call Animation Start Js Failed. animation bridge is null.");
94         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
95         return;
96     }
97     v8::HandleScope handleScope(isolate);
98     auto animatorObject = bridge->GetJsObject();
99     if (animatorObject.IsEmpty()) {
100         LOGE("Animation Object is null");
101         return;
102     }
103     auto context = bridge->GetContext();
104     v8::Local<v8::Value> proto =
105         animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "onstart").ToLocalChecked()).ToLocalChecked();
106     if (!proto->IsFunction()) {
107         LOGE("cannot find 'CallAnimationStartJs' function from animation object, maybe no callback at all.");
108         return;
109     }
110     v8::TryCatch tryCatch(isolate);
111     v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
112     v8::Local<v8::Value> funcRes;
113     v8::Local<v8::Object> global = context->Global();
114     bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
115     if (!succ) {
116         V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_START_ERROR);
117         return;
118     }
119 }
120 
CallAnimationFinishJs(const WeakPtr<V8AnimationBridge> & bridgeWeak,v8::Isolate * isolate,const RefPtr<JsAcePage> & page)121 void CallAnimationFinishJs(const WeakPtr<V8AnimationBridge>& bridgeWeak, v8::Isolate* isolate,
122     const RefPtr<JsAcePage>& page)
123 {
124     auto bridge = bridgeWeak.Upgrade();
125     if (!bridge) {
126         LOGE("Call Animation Finish Js Failed. animation bridge is null.");
127         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
128         return;
129     }
130     v8::HandleScope handleScope(isolate);
131     auto animationObject = bridge->GetJsObject();
132     if (animationObject.IsEmpty()) {
133         LOGE("Animation Object is null");
134         return;
135     }
136     auto context = bridge->GetContext();
137     v8::Local<v8::Value> proto =
138         animationObject->Get(context, v8::String::NewFromUtf8(isolate, "onfinish").ToLocalChecked()).ToLocalChecked();
139     if (!proto->IsFunction()) {
140         LOGE("cannot find 'CallAnimationFinishJs' function from animation object, maybe no callback at all.");
141         return;
142     }
143     v8::TryCatch tryCatch(isolate);
144     v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
145     v8::Local<v8::Value> funcRes;
146     v8::Local<v8::Object> global = context->Global();
147     bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
148     if (!succ) {
149         V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_FINISH_ERROR, 0, page);
150         return;
151     }
152 }
153 
CallAnimationCancelJs(const WeakPtr<V8AnimationBridge> & bridgeWeak,v8::Isolate * isolate,const RefPtr<JsAcePage> & page)154 void CallAnimationCancelJs(const WeakPtr<V8AnimationBridge>& bridgeWeak, v8::Isolate* isolate,
155     const RefPtr<JsAcePage>& page)
156 {
157     auto bridge = bridgeWeak.Upgrade();
158     if (!bridge) {
159         LOGE("Call Animation Cancel Js Failed. animation bridge is null.");
160         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
161         return;
162     }
163     v8::HandleScope handleScope(isolate);
164     auto animationObject = bridge->GetJsObject();
165     if (animationObject.IsEmpty()) {
166         LOGE("Animation Object is null");
167         return;
168     }
169     auto context = bridge->GetContext();
170     v8::Local<v8::Value> proto =
171         animationObject->Get(context, v8::String::NewFromUtf8(isolate, "oncancel").ToLocalChecked()).ToLocalChecked();
172     if (!proto->IsFunction()) {
173         LOGE("cannot find 'CallAnimationCancelJs' function from animation object, maybe no callback at all.");
174         return;
175     }
176     LOGD("animation oncancel event call");
177     v8::TryCatch tryCatch(isolate);
178     v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
179     v8::Local<v8::Value> funcRes;
180     v8::Local<v8::Object> global = context->Global();
181     bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
182     if (!succ) {
183         V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_CANCEL_ERROR, 0, page);
184         return;
185     }
186 }
187 
CallAnimationRepeatJs(const WeakPtr<V8AnimationBridge> & bridgeWeak,v8::Isolate * isolate,const RefPtr<JsAcePage> & page)188 void CallAnimationRepeatJs(const WeakPtr<V8AnimationBridge>& bridgeWeak, v8::Isolate* isolate,
189     const RefPtr<JsAcePage>& page)
190 {
191     auto bridge = bridgeWeak.Upgrade();
192     if (!bridge) {
193         LOGE("Call Animation Repeat Js Failed. animation bridge is null.");
194         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
195         return;
196     }
197     v8::HandleScope handleScope(isolate);
198     auto animationObject = bridge->GetJsObject();
199     if (animationObject.IsEmpty()) {
200         LOGE("Animation Object is null");
201         return;
202     }
203     auto context = bridge->GetContext();
204     v8::Local<v8::Value> proto =
205         animationObject->Get(context, v8::String::NewFromUtf8(isolate, "onrepeat").ToLocalChecked()).ToLocalChecked();
206     if (!proto->IsFunction()) {
207         LOGE("cannot find 'CallAnimationRepeatJs' function from animation object, maybe no callback at all.");
208         return;
209     }
210     LOGD("animation onrepeat event call");
211     v8::TryCatch tryCatch(isolate);
212     v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
213     v8::Local<v8::Value> funcRes;
214     v8::Local<v8::Object> global = context->Global();
215     bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
216     if (!succ) {
217         V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_REPEAT_ERROR, 0, page);
218         return;
219     }
220 }
221 
JsUpdatePlayState(v8::Isolate * isolate,const WeakPtr<V8AnimationBridge> & bridgeWeak,const char * state)222 void JsUpdatePlayState(v8::Isolate* isolate, const WeakPtr<V8AnimationBridge>& bridgeWeak, const char* state)
223 {
224     auto bridge = bridgeWeak.Upgrade();
225     if (!bridge || (isolate == nullptr)) {
226         LOGW("Set playState to Stop failed. bridge or isolate is null.");
227         return;
228     }
229     v8::HandleScope handleScope(isolate);
230     auto ctx = bridge->GetContext();
231     v8::Context::Scope contextScope(ctx);
232     auto animationContext = bridge->GetJsObject();
233     animationContext
234         ->Set(ctx, v8::String::NewFromUtf8(isolate, "__playState").ToLocalChecked(),
235             v8::String::NewFromUtf8(isolate, state).ToLocalChecked())
236         .ToChecked();
237 }
238 
AddListenerForEventCallback(const WeakPtr<V8AnimationBridge> & bridgeWeak,const RefPtr<Animator> & animator,v8::Isolate * isolate,const RefPtr<JsAcePage> & page)239 void AddListenerForEventCallback(const WeakPtr<V8AnimationBridge>& bridgeWeak,
240     const RefPtr<Animator>& animator, v8::Isolate* isolate, const RefPtr<JsAcePage>& page)
241 {
242     animator->AddStopListener([isolate, bridgeWeak, page] {
243         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
244         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
245         jsTaskExecutor.PostTask([bridgeWeak, isolate, page]() mutable {
246             LOGI("call animation onfinish event");
247             CallAnimationFinishJs(bridgeWeak, isolate, page);
248         });
249     });
250     animator->AddIdleListener([isolate, bridgeWeak, page] {
251         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
252         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
253         jsTaskExecutor.PostTask([bridgeWeak, isolate, page]() mutable {
254             LOGI("call animation oncancel event");
255             CallAnimationCancelJs(bridgeWeak, isolate, page);
256         });
257     });
258     animator->AddRepeatListener([isolate, bridgeWeak, page] {
259         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
260         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
261         jsTaskExecutor.PostTask([bridgeWeak, isolate, page]() mutable {
262             LOGI("call animation onrepeat event");
263             CallAnimationRepeatJs(bridgeWeak, isolate, page);
264         });
265     });
266 }
267 
268 } // namespace
269 
V8AnimationBridge(const v8::Local<v8::Context> & ctx,v8::Isolate * instance,v8::Local<v8::Object> animationContext,NodeId nodeId)270 V8AnimationBridge::V8AnimationBridge(
271     const v8::Local<v8::Context>& ctx, v8::Isolate* instance, v8::Local<v8::Object> animationContext, NodeId nodeId)
272     : instance_(instance), nodeId_(nodeId) // ??? how to dup animationContext
273 {
274     animationObject_.Reset(instance_, animationContext);
275     ctx_.Reset(instance_, ctx);
276 }
277 
JsAnimationStartTimeGet(const v8::FunctionCallbackInfo<v8::Value> & info)278 void V8AnimationBridgeUtils::JsAnimationStartTimeGet(const v8::FunctionCallbackInfo<v8::Value>& info)
279 {
280     v8::Isolate* isolate = info.GetIsolate();
281     v8::HandleScope handleScope(isolate);
282     auto context = isolate->GetCurrentContext();
283 
284     int32_t nodeId = GetCurrentNodeId(context, info.Holder());
285     int32_t pageId = GetCurrentPageId(context, info.Holder());
286     auto page = GetPageById(isolate, pageId);
287     if (!page) {
288         LOGE("JsAnimationStartTimeGet: no page found for nodeId: %{public}d", nodeId);
289         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
290         return;
291     }
292     auto domDocument = page->GetDomDocument();
293     if (!domDocument) {
294         LOGE("JsAnimationStartTimeGet failed, DomDocument is null.");
295         return;
296     }
297     auto domNode = domDocument->GetDOMNodeById(nodeId);
298     if (!domNode) {
299         LOGE("JsAnimationStartTimeGet failed, DomNode is null.");
300         return;
301     }
302     auto tweenComponent = domNode->GetTweenComponent();
303     if (tweenComponent) {
304         auto option = tweenComponent->GetCustomTweenOption();
305         auto startTime = option.GetDelay();
306         info.Holder()
307             ->Set(context, v8::String::NewFromUtf8(isolate, "__startTime").ToLocalChecked(),
308                 v8::Int32::New(isolate, startTime))
309             .ToChecked();
310     }
311     v8::Local<v8::Value> value =
312         info.Holder()->Get(context, v8::String::NewFromUtf8(isolate, "__startTime").ToLocalChecked()).ToLocalChecked();
313     info.GetReturnValue().Set(value);
314 }
315 
JsAnimationStartTimeSet(const v8::FunctionCallbackInfo<v8::Value> & info)316 void V8AnimationBridgeUtils::JsAnimationStartTimeSet(const v8::FunctionCallbackInfo<v8::Value>& info)
317 {
318     if (info.Length() != 1) {
319         LOGE("Not valid Length for info. length: %{public}d", info.Length());
320         return;
321     }
322     v8::Local<v8::Value> value = info[0];
323     v8::Isolate* isolate = info.GetIsolate();
324     v8::String::Utf8Value jsStartTime(isolate, value);
325     if (!(*jsStartTime)) {
326         return;
327     }
328     std::string startTime(*jsStartTime);
329 
330     auto ctx = isolate->GetCurrentContext();
331     int32_t nodeId = GetCurrentNodeId(ctx, info.Holder());
332     int32_t pageId = GetCurrentPageId(ctx, info.Holder());
333     auto page = GetPageById(isolate, pageId);
334     if (!page) {
335         LOGE("JsAnimationStartTimeSet: no page found for nodeId: %{public}d", nodeId);
336         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
337         return;
338     }
339     auto task = AceType::MakeRefPtr<V8AnimationBridgeTaskStartTime>(startTime);
340     page->PushCommand(AceType::MakeRefPtr<JsCommandAnimation>(nodeId, task));
341 }
342 
JsAnimationPendingGet(const v8::FunctionCallbackInfo<v8::Value> & info)343 void V8AnimationBridgeUtils::JsAnimationPendingGet(const v8::FunctionCallbackInfo<v8::Value>& info)
344 {
345     v8::Isolate* isolate = info.GetIsolate();
346     v8::HandleScope handleScope(isolate);
347     auto context = isolate->GetCurrentContext();
348 
349     int32_t nodeId = GetCurrentNodeId(context, info.Holder());
350     int32_t pageId = GetCurrentPageId(context, info.Holder());
351     auto page = GetPageById(isolate, pageId);
352     if (!page) {
353         LOGE("JsAnimationPendingGet: no page found for nodeId: %{public}d", nodeId);
354         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
355         return;
356     }
357     auto domDocument = page->GetDomDocument();
358     if (!domDocument) {
359         LOGE("JsAnimationPendingGet failed, DomDocument is null.");
360         return;
361     }
362     auto domNode = domDocument->GetDOMNodeById(nodeId);
363     if (!domNode) {
364         LOGE("JsAnimationPendingGet failed, DomNode is null.");
365         return;
366     }
367     auto tweenComponent = domNode->GetTweenComponent();
368     if (tweenComponent) {
369         auto controller = tweenComponent->GetAnimator();
370         if (controller) {
371             info.Holder()
372                 ->Set(context, v8::String::NewFromUtf8(isolate, "__pending").ToLocalChecked(),
373                     v8::Boolean::New(isolate, controller->IsPending()))
374                 .ToChecked();
375         }
376     }
377     v8::Local<v8::Value> value =
378         info.Holder()->Get(context, v8::String::NewFromUtf8(isolate, "__pending").ToLocalChecked()).ToLocalChecked();
379     info.GetReturnValue().Set(value);
380 }
381 
JsAnimationPendingSet(const v8::FunctionCallbackInfo<v8::Value> & info)382 void V8AnimationBridgeUtils::JsAnimationPendingSet(const v8::FunctionCallbackInfo<v8::Value>& info) {}
383 
JsAnimationPlayStateGet(const v8::FunctionCallbackInfo<v8::Value> & info)384 void V8AnimationBridgeUtils::JsAnimationPlayStateGet(const v8::FunctionCallbackInfo<v8::Value>& info)
385 {
386     v8::Isolate* isolate = info.GetIsolate();
387     v8::HandleScope handleScope(isolate);
388     auto context = isolate->GetCurrentContext();
389 
390     v8::Local<v8::Value> value =
391         info.Holder()->Get(context, v8::String::NewFromUtf8(isolate, "__playState").ToLocalChecked()).ToLocalChecked();
392     info.GetReturnValue().Set(value);
393 }
394 
JsAnimationPlayStateSet(const v8::FunctionCallbackInfo<v8::Value> & info)395 void V8AnimationBridgeUtils::JsAnimationPlayStateSet(const v8::FunctionCallbackInfo<v8::Value>& info)
396 {
397     if (info.Length() != 1) {
398         LOGE("Not valid Length for info. length: %{public}d", info.Length());
399         return;
400     }
401     v8::Local<v8::Value> value = info[0];
402     v8::Isolate* isolate = info.GetIsolate();
403     v8::String::Utf8Value jsPlayState(isolate, value);
404     if (!(*jsPlayState)) {
405         return;
406     }
407     std::string playState(*jsPlayState);
408     AnimationOperation operation = StringToAnimationOperation(playState);
409     auto ctx = isolate->GetCurrentContext();
410     int32_t nodeId = GetCurrentNodeId(ctx, info.Holder());
411     int32_t pageId = GetCurrentPageId(ctx, info.Holder());
412     auto page = GetPageById(isolate, pageId);
413     if (!page) {
414         LOGE("no page found for nodeId: %{public}d", nodeId);
415         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
416         return;
417     }
418     auto task = AceType::MakeRefPtr<V8AnimationBridgeTaskOperation>(operation);
419     page->PushCommand(AceType::MakeRefPtr<JsCommandAnimation>(nodeId, task));
420 }
421 
JsCreateAnimation(const RefPtr<JsAcePage> & page,const std::string & param)422 void V8AnimationBridge::JsCreateAnimation(const RefPtr<JsAcePage>& page, const std::string& param)
423 {
424     std::vector<std::unordered_map<std::string, std::string>> animationFrames;
425     std::unordered_map<std::string, double> animationDoubleOptions;
426     std::unordered_map<std::string, std::string> animationStringOptions;
427     int32_t iterations = 0;
428 
429     BaseAnimationBridgeUtils::JsParseAnimationFrames(param, animationFrames);
430     BaseAnimationBridgeUtils::JsParseAnimationOptions(
431         param, iterations, animationDoubleOptions, animationStringOptions);
432     auto tweenOption = TweenOption();
433     auto iterEasing = animationStringOptions.find(DOM_ANIMATION_EASING);
434     if (iterEasing != animationStringOptions.end()) {
435         tweenOption.SetCurve(CreateCurve(iterEasing->second));
436     }
437     std::vector<Dimension> transformOrigin = BaseAnimationBridgeUtils::HandleTransformOrigin(animationFrames);
438     if (transformOrigin.size() == BaseAnimationBridgeUtils::TRANSFORM_ORIGIN_DEFAULT_SIZE) {
439         tweenOption.SetTransformOrigin(transformOrigin.front(), transformOrigin.back());
440     }
441     auto iterDuration = animationDoubleOptions.find(DOM_ANIMATION_DURATION_API);
442     if (iterDuration != animationDoubleOptions.end()) {
443         tweenOption.SetDuration(iterDuration->second);
444     }
445     auto iterFill = animationStringOptions.find(DOM_ANIMATION_FILL);
446     if (iterFill != animationStringOptions.end()) {
447         tweenOption.SetFillMode(StringToFillMode(iterFill->second));
448     }
449     auto iterDirection = animationStringOptions.find(DOM_ANIMATION_DIRECTION_API);
450     if (iterDirection != animationStringOptions.end()) {
451         tweenOption.SetAnimationDirection(StringToAnimationDirection(iterDirection->second));
452     }
453     auto iterDelay = animationDoubleOptions.find(DOM_ANIMATION_DELAY_API);
454     if (iterDelay != animationDoubleOptions.end()) {
455         tweenOption.SetDelay(iterDelay->second);
456     }
457     tweenOption.SetIteration(iterations);
458     if (!page) {
459         LOGE("JsCreateAnimation failed, Page is null.");
460         return;
461     }
462     auto domDocument = page->GetDomDocument();
463     if (!domDocument) {
464         LOGE("JsCreateAnimation failed, DomDocument is null.");
465         return;
466     }
467     auto domNode = domDocument->GetDOMNodeById(nodeId_);
468     if (!domNode) {
469         LOGE("JsCreateAnimation failed, DomNode is null.");
470         return;
471     }
472     domNode->ParseAnimationStyle(animationFrames);
473     domNode->TweenOptionSetKeyframes(tweenOption);
474     if (tweenOption.IsValid()) {
475         domNode->SetCustomAnimationStyleUpdate(true);
476     }
477     RefPtr<Animator> animator = AceType::MakeRefPtr<Animator>();
478     auto tweenComponent = domNode->GetTweenComponent();
479     if (!tweenComponent) {
480         tweenComponent = AceType::MakeRefPtr<TweenComponent>(
481             BaseAnimationBridgeUtils::COMPONENT_PREFIX + std::to_string(nodeId_), domNode->GetTag());
482         domNode->SetTweenComponent(tweenComponent);
483     }
484     LOGD("parse animate parameters for nodeId: %d", nodeId_);
485     tweenComponent->SetAnimator(animator);
486     BaseAnimationBridgeUtils::SetTweenComponentParams(nullptr, animationFrames, tweenComponent, tweenOption);
487     AddListenerForEventCallback(AceType::WeakClaim(this), animator, instance_, page);
488     domNode->GenerateComponentNode();
489     page->PushDirtyNode(nodeId_);
490 }
491 
SetPlayStateCallbacksWithListenerId(RefPtr<Animator> & animator)492 void V8AnimationBridge::SetPlayStateCallbacksWithListenerId(RefPtr<Animator>& animator)
493 {
494     WeakPtr<V8AnimationBridge> bridgeWeak = AceType::WeakClaim(this);
495     animator->RemoveStopListener(finishListenerId_);
496     finishListenerId_ = animator->AddStopListener([bridgeWeak, instance = instance_] {
497         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(instance->GetData(V8EngineInstance::FRONTEND_DELEGATE));
498         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
499         jsTaskExecutor.PostTask([instance, bridgeWeak]() mutable {
500             JsUpdatePlayState(instance, bridgeWeak, DOM_ANIMATION_PLAY_STATE_FINISHED);
501         });
502     });
503     animator->RemoveIdleListener(idleListenerId_);
504     idleListenerId_ = animator->AddIdleListener([bridgeWeak, instance = instance_] {
505         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(instance->GetData(V8EngineInstance::FRONTEND_DELEGATE));
506         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
507         jsTaskExecutor.PostTask([instance, bridgeWeak]() mutable {
508             JsUpdatePlayState(instance, bridgeWeak, DOM_ANIMATION_PLAY_STATE_IDLE);
509         });
510     });
511 }
512 
SetPlayStateCallbacks(RefPtr<Animator> & animator)513 void V8AnimationBridge::SetPlayStateCallbacks(RefPtr<Animator>& animator)
514 {
515     if (!animator) {
516         LOGE("Set PlayState callbacks failed. animator is null.");
517         return;
518     }
519     WeakPtr<V8AnimationBridge> bridgeWeak = AceType::WeakClaim(this);
520     SetPlayStateCallbacksWithListenerId(animator);
521     animator->ClearPauseListeners();
522     animator->AddPauseListener([bridgeWeak, instance = instance_] {
523         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(instance->GetData(V8EngineInstance::FRONTEND_DELEGATE));
524         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
525         jsTaskExecutor.PostTask([instance, bridgeWeak]() mutable {
526             JsUpdatePlayState(instance, bridgeWeak, DOM_ANIMATION_PLAY_STATE_PAUSED);
527         });
528     });
529     animator->ClearStartListeners();
530     animator->AddStartListener([bridgeWeak, instance = instance_] {
531         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(instance->GetData(V8EngineInstance::FRONTEND_DELEGATE));
532         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
533         jsTaskExecutor.PostTask([instance, bridgeWeak]() mutable {
534             CallAnimationStartJs(bridgeWeak, instance);
535             JsUpdatePlayState(instance, bridgeWeak, DOM_ANIMATION_PLAY_STATE_RUNNING);
536         });
537     });
538     animator->ClearResumeListeners();
539     animator->AddResumeListener([bridgeWeak, instance = instance_] {
540         auto delegate = static_cast<RefPtr<FrontendDelegate>*>(instance->GetData(V8EngineInstance::FRONTEND_DELEGATE));
541         auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
542         jsTaskExecutor.PostTask([instance, bridgeWeak]() mutable {
543             JsUpdatePlayState(instance, bridgeWeak, DOM_ANIMATION_PLAY_STATE_RUNNING);
544         });
545     });
546 }
547 
JsAnimationPlay(const v8::FunctionCallbackInfo<v8::Value> & args)548 void V8AnimationBridgeUtils::JsAnimationPlay(const v8::FunctionCallbackInfo<v8::Value>& args)
549 {
550     if (args.Length() != 0) {
551         LOGE("args length error, length: %{public}d", args.Length());
552         return;
553     }
554     v8::Isolate* isolate = args.GetIsolate();
555     v8::HandleScope handleScope(isolate);
556     auto ctx = isolate->GetCurrentContext();
557     int32_t nodeId = GetCurrentNodeId(ctx, args.Holder());
558     int32_t pageId = GetCurrentPageId(ctx, args.Holder());
559     HandleJsAnimationContext(ctx, pageId, nodeId, AnimationOperation::PLAY);
560 }
561 
JsAnimationFinish(const v8::FunctionCallbackInfo<v8::Value> & args)562 void V8AnimationBridgeUtils::JsAnimationFinish(const v8::FunctionCallbackInfo<v8::Value>& args)
563 {
564     if (args.Length() != 0) {
565         LOGE("args length error, length: %{public}d", args.Length());
566         return;
567     }
568 
569     v8::Isolate* isolate = args.GetIsolate();
570     v8::HandleScope handleScope(isolate);
571     auto ctx = isolate->GetCurrentContext();
572     int32_t nodeId = GetCurrentNodeId(ctx, args.Holder());
573     int32_t pageId = GetCurrentPageId(ctx, args.Holder());
574     HandleJsAnimationContext(ctx, pageId, nodeId, AnimationOperation::FINISH);
575 }
576 
JsAnimationPause(const v8::FunctionCallbackInfo<v8::Value> & args)577 void V8AnimationBridgeUtils::JsAnimationPause(const v8::FunctionCallbackInfo<v8::Value>& args)
578 {
579     if (args.Length() != 0) {
580         LOGE("args length error, length: %{public}d", args.Length());
581         return;
582     }
583     v8::Isolate* isolate = args.GetIsolate();
584     v8::HandleScope handleScope(isolate);
585     auto ctx = isolate->GetCurrentContext();
586     int32_t nodeId = GetCurrentNodeId(ctx, args.Holder());
587     int32_t pageId = GetCurrentPageId(ctx, args.Holder());
588     HandleJsAnimationContext(ctx, pageId, nodeId, AnimationOperation::PAUSE);
589 }
590 
JsAnimationCancel(const v8::FunctionCallbackInfo<v8::Value> & args)591 void V8AnimationBridgeUtils::JsAnimationCancel(const v8::FunctionCallbackInfo<v8::Value>& args)
592 {
593     if (args.Length() != 0) {
594         LOGE("args length error, length: %{public}d", args.Length());
595         return;
596     }
597     v8::Isolate* isolate = args.GetIsolate();
598     v8::HandleScope handleScope(isolate);
599     auto ctx = isolate->GetCurrentContext();
600     int32_t nodeId = GetCurrentNodeId(ctx, args.Holder());
601     int32_t pageId = GetCurrentPageId(ctx, args.Holder());
602     HandleJsAnimationContext(ctx, pageId, nodeId, AnimationOperation::CANCEL);
603 }
604 
JsAnimationReverse(const v8::FunctionCallbackInfo<v8::Value> & args)605 void V8AnimationBridgeUtils::JsAnimationReverse(const v8::FunctionCallbackInfo<v8::Value>& args)
606 {
607     if (args.Length() != 0) {
608         LOGE("args length error, length: %{public}d", args.Length());
609         return;
610     }
611     v8::Isolate* isolate = args.GetIsolate();
612     v8::HandleScope handleScope(isolate);
613     auto ctx = isolate->GetCurrentContext();
614     int32_t nodeId = GetCurrentNodeId(ctx, args.Holder());
615     int32_t pageId = GetCurrentPageId(ctx, args.Holder());
616     HandleJsAnimationContext(ctx, pageId, nodeId, AnimationOperation::REVERSE);
617 }
618 
CreateAnimationContext(v8::Local<v8::Context> & ctx,int32_t pageId,NodeId nodeId)619 v8::Local<v8::Object> V8AnimationBridgeUtils::CreateAnimationContext(
620     v8::Local<v8::Context>& ctx, int32_t pageId, NodeId nodeId)
621 {
622     const std::unordered_map<const char*, v8::Local<v8::Function>> contextTable = {
623         { "play", v8::Function::New(ctx, JsAnimationPlay, v8::Local<v8::Value>(), 0).ToLocalChecked() },
624         { "finish", v8::Function::New(ctx, JsAnimationFinish, v8::Local<v8::Value>(), 0).ToLocalChecked() },
625         { "pause", v8::Function::New(ctx, JsAnimationPause, v8::Local<v8::Value>(), 0).ToLocalChecked() },
626         { "cancel", v8::Function::New(ctx, JsAnimationCancel, v8::Local<v8::Value>(), 0).ToLocalChecked() },
627         { "reverse", v8::Function::New(ctx, JsAnimationReverse, v8::Local<v8::Value>(), 0).ToLocalChecked() },
628     };
629 
630     auto animationContext = v8::Object::New(ctx->GetIsolate());
631     for (const auto& iter : contextTable) {
632         animationContext->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), iter.first).ToLocalChecked(), iter.second)
633             .ToChecked();
634     }
635     animationContext
636         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__nodeId").ToLocalChecked(),
637             v8::Int32::New(ctx->GetIsolate(), nodeId))
638         .ToChecked();
639     animationContext
640         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__pageId").ToLocalChecked(),
641             v8::Int32::New(ctx->GetIsolate(), pageId))
642         .ToChecked();
643     animationContext
644         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__playState").ToLocalChecked(),
645         v8::String::NewFromUtf8(ctx->GetIsolate(), DOM_ANIMATION_PLAY_STATE_IDLE).ToLocalChecked())
646         .ToChecked();
647     animationContext
648         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "finished").ToLocalChecked(),
649             v8::Boolean::New(ctx->GetIsolate(), 0))
650         .ToChecked();
651     for (const auto& item : V8_ANIMATION_FUNCS) {
652         auto getterTempl = v8::FunctionTemplate::New(ctx->GetIsolate(), std::get<1>(item));
653         auto setterTempl = v8::FunctionTemplate::New(ctx->GetIsolate(), std::get<2>(item));
654         animationContext->SetAccessorProperty(
655             v8::String::NewFromUtf8(ctx->GetIsolate(), std::get<0>(item).c_str()).ToLocalChecked(),
656             getterTempl->GetFunction(ctx).ToLocalChecked(), setterTempl->GetFunction(ctx).ToLocalChecked());
657     }
658     return animationContext;
659 }
660 
V8AnimationBridgeTaskCreate(const RefPtr<FrontendDelegate> & delegate,const RefPtr<V8AnimationBridge> & bridge,std::string param)661 V8AnimationBridgeTaskCreate::V8AnimationBridgeTaskCreate(
662     const RefPtr<FrontendDelegate>& delegate, const RefPtr<V8AnimationBridge>& bridge, std::string param)
663     : bridge_(bridge), delegate_(delegate), param_(std::move(param))
664 {}
665 
AnimationBridgeTaskFunc(const RefPtr<JsAcePage> & page,NodeId nodeId)666 void V8AnimationBridgeTaskCreate::AnimationBridgeTaskFunc(const RefPtr<JsAcePage>& page, NodeId nodeId)
667 {
668     if (!bridge_ || !page) {
669         LOGE("Create Animation Bridge failed. bridge or page is null.");
670         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
671         return;
672     }
673     auto bridgeFree = AceType::DynamicCast<V8AnimationBridge>(page->GetAnimationBridge(nodeId));
674     auto sp = delegate_.Upgrade();
675     if (sp) {
676         auto jsTaskExecutor = sp->GetAnimationJsTask();
677         if (bridgeFree) {
678             auto weakBridge = AceType::WeakClaim(AceType::RawPtr(bridgeFree));
679             jsTaskExecutor.PostTask([weakBridge]() mutable {
680                 auto bridgeFree = weakBridge.Upgrade();
681                 if (bridgeFree != nullptr) {
682                     bridgeFree.Reset();
683                 }
684             });
685         }
686     }
687     bridge_->JsCreateAnimation(page, param_);
688     page->AddAnimationBridge(nodeId, bridge_);
689 }
690 
AnimationBridgeTaskFunc(const RefPtr<JsAcePage> & page,NodeId nodeId)691 void V8AnimationBridgeTaskOperation::AnimationBridgeTaskFunc(const RefPtr<JsAcePage>& page, NodeId nodeId)
692 {
693     if (!page) {
694         LOGE("AnimationBridgeTaskFunc failed, page is nullptr.");
695         return;
696     }
697     auto animationBridge = AceType::DynamicCast<V8AnimationBridge>(page->GetAnimationBridge(nodeId));
698     if (!animationBridge) {
699         LOGE("no animation bridge found for nodeId: %{public}d", nodeId);
700         EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
701         return;
702     }
703     auto domDocument = page->GetDomDocument();
704     if (!domDocument) {
705         LOGE("Animation operation failed, DomDocument is null.");
706         return;
707     }
708     auto domNode = domDocument->GetDOMNodeById(nodeId);
709     if (!domNode) {
710         LOGE("Animation operation failed, DomNode is null.");
711         return;
712     }
713     auto tweenComponent = domNode->GetTweenComponent();
714     if (tweenComponent) {
715         tweenComponent->SetCustomAnimationOperation(operation_);
716     }
717 
718     RefPtr<Animator> animator;
719     if (tweenComponent) {
720         animator = tweenComponent->GetAnimator();
721     }
722     if (animator) {
723         animationBridge->SetPlayStateCallbacks(animator);
724     }
725     domNode->GenerateComponentNode();
726     page->PushDirtyNode(nodeId);
727 }
728 
AnimationBridgeTaskFunc(const RefPtr<JsAcePage> & page,NodeId nodeId)729 void V8AnimationBridgeTaskStartTime::AnimationBridgeTaskFunc(const RefPtr<JsAcePage>& page, NodeId nodeId)
730 {
731     if (!page) {
732         LOGE("V8AnimationBridgeTaskStartTime: Get page is error");
733         return;
734     }
735     auto domDocument = page->GetDomDocument();
736     if (!domDocument) {
737         LOGE("V8AnimationBridgeTaskStartTime failed, DomDocument is null.");
738         return;
739     }
740     auto domNode = domDocument->GetDOMNodeById(nodeId);
741     if (!domNode) {
742         LOGE("V8AnimationBridgeTaskStartTime failed, DomNode is null.");
743         return;
744     }
745     auto tweenComponent = domNode->GetTweenComponent();
746     if (tweenComponent) {
747         auto option = tweenComponent->GetCustomTweenOption();
748         option.SetDelay(StringToInt(startTime_));
749         tweenComponent->SetCustomTweenOption(option);
750     }
751 }
752 
753 } // namespace OHOS::Ace::Framework
754