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