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 "frameworks/bridge/js_frontend/engine/v8/v8_animator_bridge.h"
17
18 #include "base/log/event_report.h"
19 #include "base/log/log.h"
20 #include "core/common/container.h"
21 #include "frameworks/bridge/common/utils/utils.h"
22 #include "frameworks/bridge/js_frontend/engine/v8/v8_engine.h"
23 #include "frameworks/bridge/js_frontend/engine/v8/v8_utils.h"
24 #include "frameworks/bridge/js_frontend/js_ace_page.h"
25
26 namespace OHOS::Ace::Framework {
27 namespace {
28
GetPageById(v8::Isolate * isolate,int32_t pageId)29 RefPtr<JsAcePage> GetPageById(v8::Isolate* isolate, int32_t pageId)
30 {
31 LOGD("Enter GetPageById");
32 if (isolate == nullptr) {
33 LOGE("Isolate is null.");
34 return nullptr;
35 }
36 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
37 return (*delegate)->GetPage(pageId);
38 }
39
GetCurrentBridgeId(const v8::Local<v8::Context> & ctx,v8::Local<v8::Object> value)40 inline int32_t GetCurrentBridgeId(const v8::Local<v8::Context>& ctx, v8::Local<v8::Object> value)
41 {
42 v8::Isolate* isolate = ctx->GetIsolate();
43 v8::HandleScope handleScope(isolate);
44 int32_t id = value->Get(ctx, v8::String::NewFromUtf8(isolate, "__bridgeId").ToLocalChecked())
45 .ToLocalChecked()->Int32Value(ctx).ToChecked();
46 return id < 0 ? 0 : id;
47 }
48
GetCurrentPageId(const v8::Local<v8::Context> & ctx,v8::Local<v8::Object> value)49 inline int32_t GetCurrentPageId(const v8::Local<v8::Context>& ctx, v8::Local<v8::Object> value)
50 {
51 v8::Isolate* isolate = ctx->GetIsolate();
52 v8::HandleScope handleScope(isolate);
53 int32_t id = value->Get(ctx, v8::String::NewFromUtf8(isolate, "__pageId").ToLocalChecked())
54 .ToLocalChecked()->Int32Value(ctx).ToChecked();
55 return id < 0 ? 0 : id;
56 }
57
HandleJsAnimatorContext(const v8::Local<v8::Context> & ctx,int32_t pageId,int32_t bridgeId,AnimatorOperation operation)58 void HandleJsAnimatorContext(const v8::Local<v8::Context>& ctx, int32_t pageId, int32_t bridgeId,
59 AnimatorOperation operation)
60 {
61 v8::Isolate* isolate = ctx->GetIsolate();
62 auto page = GetPageById(isolate, pageId);
63 if (!page) {
64 LOGE("no page found for bridgeId: %{public}d", bridgeId);
65 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
66 return;
67 }
68 auto task = AceType::MakeRefPtr<V8AnimatorTaskOperation>(operation);
69 page->PushCommand(Referenced::MakeRefPtr<JsCommandAnimator>(bridgeId, task));
70 if (page->CheckPageCreated()) {
71 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
72 (*delegate)->TriggerPageUpdate(page->GetPageId());
73 }
74 }
75
CallAnimationStartJs(const WeakPtr<V8AnimatorBridge> & bridgeWeak,v8::Isolate * isolate)76 void CallAnimationStartJs(const WeakPtr<V8AnimatorBridge>& bridgeWeak, v8::Isolate* isolate)
77 {
78 auto bridge = bridgeWeak.Upgrade();
79 if (!bridge) {
80 LOGE("Call Animation Start Js Failed. animation bridge is null.");
81 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
82 return;
83 }
84 v8::HandleScope handleScope(isolate);
85 auto animatorObject = bridge->GetJsObject();
86 if (animatorObject.IsEmpty()) {
87 LOGE("Animation Object is null");
88 return;
89 }
90 auto context = bridge->GetContext();
91 v8::Local<v8::Value> proto =
92 animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "onstart").ToLocalChecked()).ToLocalChecked();
93 if (!proto->IsFunction()) {
94 LOGE("cannot find 'CallAnimationStartJs' function from animation object, maybe no callback at all.");
95 return;
96 }
97 v8::TryCatch tryCatch(isolate);
98 v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
99 v8::Local<v8::Value> funcRes;
100 v8::Local<v8::Object> global = context->Global();
101 bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
102 if (!succ) {
103 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_START_ERROR);
104 return;
105 }
106 }
107
CallAnimationFinishJs(const WeakPtr<V8AnimatorBridge> & bridgeWeak,v8::Isolate * isolate)108 void CallAnimationFinishJs(const WeakPtr<V8AnimatorBridge>& bridgeWeak, v8::Isolate* isolate)
109 {
110 auto bridge = bridgeWeak.Upgrade();
111 if (!bridge) {
112 LOGE("Call Animation Finish Js Failed. animation bridge is null.");
113 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
114 return;
115 }
116 v8::HandleScope handleScope(isolate);
117 auto animatorObject = bridge->GetJsObject();
118 if (animatorObject.IsEmpty()) {
119 LOGE("Animation Object is null");
120 return;
121 }
122 auto context = bridge->GetContext();
123 v8::Local<v8::Value> proto =
124 animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "onfinish").ToLocalChecked()).ToLocalChecked();
125 if (!proto->IsFunction()) {
126 LOGE("cannot find 'CallAnimationFinishJs' function from animation object, maybe no callback at all.");
127 return;
128 }
129 v8::TryCatch tryCatch(isolate);
130 v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
131 v8::Local<v8::Value> funcRes;
132 v8::Local<v8::Object> global = context->Global();
133 bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
134 if (!succ) {
135 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_FINISH_ERROR);
136 return;
137 }
138 }
139
CallAnimationCancelJs(const WeakPtr<V8AnimatorBridge> & bridgeWeak,v8::Isolate * isolate)140 void CallAnimationCancelJs(const WeakPtr<V8AnimatorBridge>& bridgeWeak, v8::Isolate* isolate)
141 {
142 auto bridge = bridgeWeak.Upgrade();
143 if (!bridge) {
144 LOGE("Call Animation Cancel Js Failed. animation bridge is null.");
145 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
146 return;
147 }
148 v8::HandleScope handleScope(isolate);
149 auto animatorObject = bridge->GetJsObject();
150 if (animatorObject.IsEmpty()) {
151 LOGE("Animation Object is null");
152 return;
153 }
154 auto context = bridge->GetContext();
155 v8::Local<v8::Value> proto =
156 animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "oncancel").ToLocalChecked()).ToLocalChecked();
157 if (!proto->IsFunction()) {
158 LOGE("cannot find 'CallAnimationCancelJs' function from animation object, maybe no callback at all.");
159 return;
160 }
161 LOGD("animation oncancel event call");
162 v8::TryCatch tryCatch(isolate);
163 v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
164 v8::Local<v8::Value> funcRes;
165 v8::Local<v8::Object> global = context->Global();
166 bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
167 if (!succ) {
168 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_CANCEL_ERROR);
169 return;
170 }
171 }
172
CallAnimationRepeatJs(const WeakPtr<V8AnimatorBridge> & bridgeWeak,v8::Isolate * isolate)173 void CallAnimationRepeatJs(const WeakPtr<V8AnimatorBridge>& bridgeWeak, v8::Isolate* isolate)
174 {
175 auto bridge = bridgeWeak.Upgrade();
176 if (!bridge) {
177 LOGE("Call Animation Repeat Js Failed. animation bridge is null.");
178 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
179 return;
180 }
181 v8::HandleScope handleScope(isolate);
182 auto animatorObject = bridge->GetJsObject();
183 if (animatorObject.IsEmpty()) {
184 LOGE("Animation Object is null");
185 return;
186 }
187 auto context = bridge->GetContext();
188 v8::Local<v8::Value> proto =
189 animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "onrepeat").ToLocalChecked()).ToLocalChecked();
190 if (!proto->IsFunction()) {
191 LOGE("cannot find 'CallAnimationRepeatJs' function from animation object, maybe no callback at all.");
192 return;
193 }
194 LOGD("animation onrepeat event call");
195 v8::TryCatch tryCatch(isolate);
196 v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
197 v8::Local<v8::Value> funcRes;
198 v8::Local<v8::Object> global = context->Global();
199 bool succ = jsFunc->Call(context, global, 0, {}).ToLocal(&funcRes);
200 if (!succ) {
201 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_REPEAT_ERROR);
202 return;
203 }
204 }
205
CallAnimationFrameJs(const WeakPtr<V8AnimatorBridge> & bridgeWeak,v8::Isolate * isolate,double value)206 void CallAnimationFrameJs(const WeakPtr<V8AnimatorBridge>& bridgeWeak, v8::Isolate* isolate, double value)
207 {
208 auto bridge = bridgeWeak.Upgrade();
209 if (!bridge) {
210 LOGE("Call Animation Repeat Js Failed. animation bridge is null.");
211 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
212 return;
213 }
214 v8::HandleScope handleScope(isolate);
215 auto animatorObject = bridge->GetJsObject();
216 if (animatorObject.IsEmpty()) {
217 LOGE("Animation Object is null");
218 return;
219 }
220 auto context = bridge->GetContext();
221 v8::Local<v8::Value> proto =
222 animatorObject->Get(context, v8::String::NewFromUtf8(isolate, "onframe").ToLocalChecked()).ToLocalChecked();
223 if (!proto->IsFunction()) {
224 LOGE("cannot find 'CallAnimationFrameJs' function from animation object, maybe no callback at all.");
225 return;
226 }
227 v8::TryCatch tryCatch(isolate);
228 v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(proto);
229 v8::Local<v8::Value> funcRes;
230 v8::Local<v8::Object> global = context->Global();
231 v8::Local<v8::Value> args[] = {v8::Number::New(isolate, value)};
232 bool succ = jsFunc->Call(context, global, 1, args).ToLocal(&funcRes);
233 if (!succ) {
234 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::ANIMATION_FRAME_ERROR);
235 return;
236 }
237 }
238
AddListenerForEventCallback(const WeakPtr<V8AnimatorBridge> & bridgeWeak,const RefPtr<Animator> & animator,v8::Isolate * isolate)239 void AddListenerForEventCallback(const WeakPtr<V8AnimatorBridge>& bridgeWeak, const RefPtr<Animator>& animator,
240 v8::Isolate* isolate)
241 {
242 animator->AddStartListener([isolate, bridgeWeak] {
243 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
244 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
245 jsTaskExecutor.PostTask([bridgeWeak, isolate]() mutable {
246 LOGI("call animation onstart event");
247 CallAnimationStartJs(bridgeWeak, isolate);
248 });
249 });
250 animator->AddStopListener([isolate, bridgeWeak] {
251 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
252 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
253 jsTaskExecutor.PostTask([bridgeWeak, isolate]() mutable {
254 LOGI("call animation onfinish event");
255 CallAnimationFinishJs(bridgeWeak, isolate);
256 });
257 });
258 animator->AddIdleListener([isolate, bridgeWeak] {
259 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
260 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
261 jsTaskExecutor.PostTask([bridgeWeak, isolate]() mutable {
262 LOGI("call animation oncancel event");
263 CallAnimationCancelJs(bridgeWeak, isolate);
264 });
265 });
266 animator->AddRepeatListener([isolate, bridgeWeak] {
267 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
268 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
269 jsTaskExecutor.PostTask([bridgeWeak, isolate]() mutable {
270 LOGI("call animation onrepeat event");
271 CallAnimationRepeatJs(bridgeWeak, isolate);
272 });
273 });
274 }
275
AddFrameListener(const WeakPtr<V8AnimatorBridge> & bridgeWeak,const RefPtr<KeyframeAnimation<double>> & animation,v8::Isolate * isolate)276 void AddFrameListener(const WeakPtr<V8AnimatorBridge>& bridgeWeak, const RefPtr<KeyframeAnimation<double>>& animation,
277 v8::Isolate* isolate)
278 {
279 animation->AddListener([isolate, bridgeWeak](double value) {
280 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
281 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
282 jsTaskExecutor.PostTask([bridgeWeak, isolate, value]() mutable {
283 CallAnimationFrameJs(bridgeWeak, isolate, value);
284 });
285 });
286 }
287
288 } // namespace
289
CreateAnimatorContext(v8::Local<v8::Context> & ctx,int32_t pageId,int32_t bridgeId)290 v8::Local<v8::Object> V8AnimatorBridgeUtils::CreateAnimatorContext(v8::Local<v8::Context>& ctx, int32_t pageId,
291 int32_t bridgeId)
292 {
293 const std::unordered_map<const char*, v8::Local<v8::Function>> contextTable = {
294 { "play", v8::Function::New(ctx, JsAnimatorPlay, v8::Local<v8::Value>(), 0).ToLocalChecked() },
295 { "finish", v8::Function::New(ctx, JsAnimatorFinish, v8::Local<v8::Value>(), 0).ToLocalChecked() },
296 { "pause", v8::Function::New(ctx, JsAnimatorPause, v8::Local<v8::Value>(), 0).ToLocalChecked() },
297 { "cancel", v8::Function::New(ctx, JsAnimatorCancel, v8::Local<v8::Value>(), 0).ToLocalChecked() },
298 { "reverse", v8::Function::New(ctx, JsAnimatorReverse, v8::Local<v8::Value>(), 0).ToLocalChecked() },
299 { "update", v8::Function::New(ctx, JsAnimatorUpdate, v8::Local<v8::Value>(), 1).ToLocalChecked() },
300 };
301 auto animatorContext = v8::Object::New(ctx->GetIsolate());
302 for (const auto& iter : contextTable) {
303 animatorContext->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), iter.first).ToLocalChecked(), iter.second)
304 .ToChecked();
305 }
306 animatorContext->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__pageId").ToLocalChecked(),
307 v8::Int32::New(ctx->GetIsolate(), pageId)).ToChecked();
308 animatorContext->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__bridgeId").ToLocalChecked(),
309 v8::Int32::New(ctx->GetIsolate(), bridgeId)).ToChecked();
310 return animatorContext;
311 }
312
JsAnimatorPlay(const v8::FunctionCallbackInfo<v8::Value> & args)313 void V8AnimatorBridgeUtils::JsAnimatorPlay(const v8::FunctionCallbackInfo<v8::Value>& args)
314 {
315 if (args.Length() != 0) {
316 LOGE("args length error, length: %{public}d", args.Length());
317 return;
318 }
319 v8::Isolate* isolate = args.GetIsolate();
320 v8::HandleScope handleScope(isolate);
321 auto ctx = isolate->GetCurrentContext();
322 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
323 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
324 HandleJsAnimatorContext(ctx, pageId, bridgeId, AnimatorOperation::PLAY);
325 }
326
JsAnimatorFinish(const v8::FunctionCallbackInfo<v8::Value> & args)327 void V8AnimatorBridgeUtils::JsAnimatorFinish(const v8::FunctionCallbackInfo<v8::Value>& args)
328 {
329 if (args.Length() != 0) {
330 LOGE("args length error, length: %{public}d", args.Length());
331 return;
332 }
333 v8::Isolate* isolate = args.GetIsolate();
334 v8::HandleScope handleScope(isolate);
335 auto ctx = isolate->GetCurrentContext();
336 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
337 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
338 HandleJsAnimatorContext(ctx, pageId, bridgeId, AnimatorOperation::FINISH);
339 }
340
JsAnimatorPause(const v8::FunctionCallbackInfo<v8::Value> & args)341 void V8AnimatorBridgeUtils::JsAnimatorPause(const v8::FunctionCallbackInfo<v8::Value>& args)
342 {
343 if (args.Length() != 0) {
344 LOGE("args length error, length: %{public}d", args.Length());
345 return;
346 }
347 v8::Isolate* isolate = args.GetIsolate();
348 v8::HandleScope handleScope(isolate);
349 auto ctx = isolate->GetCurrentContext();
350 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
351 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
352 HandleJsAnimatorContext(ctx, pageId, bridgeId, AnimatorOperation::PAUSE);
353 }
354
JsAnimatorCancel(const v8::FunctionCallbackInfo<v8::Value> & args)355 void V8AnimatorBridgeUtils::JsAnimatorCancel(const v8::FunctionCallbackInfo<v8::Value>& args)
356 {
357 if (args.Length() != 0) {
358 LOGE("args length error, length: %{public}d", args.Length());
359 return;
360 }
361 v8::Isolate* isolate = args.GetIsolate();
362 v8::HandleScope handleScope(isolate);
363 auto ctx = isolate->GetCurrentContext();
364 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
365 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
366 HandleJsAnimatorContext(ctx, pageId, bridgeId, AnimatorOperation::CANCEL);
367 }
368
JsAnimatorReverse(const v8::FunctionCallbackInfo<v8::Value> & args)369 void V8AnimatorBridgeUtils::JsAnimatorReverse(const v8::FunctionCallbackInfo<v8::Value>& args)
370 {
371 if (args.Length() != 0) {
372 LOGE("args length error, length: %{public}d", args.Length());
373 return;
374 }
375 v8::Isolate* isolate = args.GetIsolate();
376 v8::HandleScope handleScope(isolate);
377 auto ctx = isolate->GetCurrentContext();
378 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
379 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
380 HandleJsAnimatorContext(ctx, pageId, bridgeId, AnimatorOperation::REVERSE);
381 }
382
JsAnimatorUpdate(const v8::FunctionCallbackInfo<v8::Value> & args)383 void V8AnimatorBridgeUtils::JsAnimatorUpdate(const v8::FunctionCallbackInfo<v8::Value>& args)
384 {
385 if (args.Length() != 1) {
386 LOGE("args length error, length: %{public}d", args.Length());
387 return;
388 }
389 v8::Isolate* isolate = args.GetIsolate();
390 v8::HandleScope handleScope(isolate);
391 auto ctx = isolate->GetCurrentContext();
392 v8::Local<v8::Object> valObj = args[0]->ToObject(ctx).ToLocalChecked();
393 v8::Local<v8::Array> valObjProperties = valObj->GetOwnPropertyNames(ctx).ToLocalChecked();
394 uint32_t objLen = valObjProperties->Length();
395 std::unordered_map<std::string, std::string> params;
396 for (uint32_t i = 0; i < objLen; i++) {
397 v8::Local<v8::Value> valObjKey;
398 bool succ = valObjProperties->Get(ctx, i).ToLocal(&valObjKey);
399 if (!succ) {
400 LOGW("key is null. Ignoring!");
401 continue;
402 }
403 v8::String::Utf8Value valObjKeyStr(isolate, valObjKey);
404 const char* keyStr = *valObjKeyStr;
405 if (keyStr == nullptr) {
406 continue;
407 }
408 v8::Local<v8::Value> valObjVal = valObj->Get(ctx, valObjKey).ToLocalChecked();
409 if (valObjVal->IsString() || valObjVal->IsNumber()) {
410 v8::String::Utf8Value valObjValStr(isolate, valObjVal);
411 const char* valStr = *valObjValStr;
412 if (valStr == nullptr) {
413 continue;
414 }
415 params[keyStr] = valStr;
416 } else {
417 LOGD("value of unsupported type. Ignoring!");
418 }
419 }
420 int32_t pageId = GetCurrentPageId(ctx, args.Holder());
421 int32_t bridgeId = GetCurrentBridgeId(ctx, args.Holder());
422 auto page = GetPageById(isolate, pageId);
423 if (!page) {
424 LOGE("no page found for bridgeId: %{public}d", bridgeId);
425 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_PAGE_ERR);
426 return;
427 }
428 auto task = AceType::MakeRefPtr<V8AnimatorTaskUpdate>(isolate, params);
429 page->PushCommand(Referenced::MakeRefPtr<JsCommandAnimator>(bridgeId, task));
430 }
431
JsCreateBridgeId()432 int32_t V8AnimatorBridgeUtils::JsCreateBridgeId()
433 {
434 static int32_t bridgeId = 0;
435 return bridgeId++;
436 }
437
V8AnimatorBridge(const v8::Local<v8::Context> & ctx,v8::Isolate * instance,v8::Local<v8::Object> animatorContext)438 V8AnimatorBridge::V8AnimatorBridge(
439 const v8::Local<v8::Context>& ctx, v8::Isolate* instance, v8::Local<v8::Object> animatorContext)
440 : instance_(instance)
441 {
442 animatorObject_.Reset(instance_, animatorContext);
443 ctx_.Reset(instance_, ctx);
444 }
445
~V8AnimatorBridge()446 V8AnimatorBridge::~V8AnimatorBridge()
447 {
448 RefPtr<Animator> animator;
449 animator.Swap(animator_);
450 auto taskExecutor = Container::CurrentTaskExecutor();
451 if (taskExecutor) {
452 taskExecutor->PostSyncTask(
453 [&animator]() {
454 LOGI("release animator on UI thread");
455 animator.Reset();
456 },
457 TaskExecutor::TaskType::UI);
458 }
459 }
460
JsCreateAnimation(const std::string & param)461 void V8AnimatorBridge::JsCreateAnimation(const std::string& param)
462 {
463 int32_t iterations = 0;
464 double duration = 0.0;
465 double delay = 0.0;
466 std::unordered_map<std::string, double> animationDoubleParams;
467 std::unordered_map<std::string, std::string> animationStringParams;
468 BaseAnimationBridgeUtils::JsParseAnimatorParams(param, iterations, animationDoubleParams, animationStringParams);
469 RefPtr<Curve> curve;
470 std::string curveString;
471 auto iterEasing = animationStringParams.find(DOM_ANIMATION_EASING);
472 if (iterEasing != animationStringParams.end()) {
473 curveString = iterEasing->second;
474 }
475 curve = CreateCurve(curveString);
476 auto iterDuration = animationDoubleParams.find(DOM_ANIMATION_DURATION_API);
477 if (iterDuration != animationDoubleParams.end()) {
478 duration = iterDuration->second;
479 }
480 std::string fillString;
481 auto iterFill = animationStringParams.find(DOM_ANIMATION_FILL);
482 if (iterFill != animationStringParams.end()) {
483 fillString = iterFill->second;
484 }
485 auto iterDelay = animationDoubleParams.find(DOM_ANIMATION_DELAY_API);
486 if (iterDelay != animationDoubleParams.end()) {
487 delay = iterDelay->second;
488 }
489 if (!instance_) {
490 LOGE("instance is null");
491 return;
492 }
493 auto keyframeAnimation = CreateDoubleAnimation(animationDoubleParams, curve);
494 if (!animator_) {
495 animator_ = AceType::MakeRefPtr<Animator>();
496 }
497 if (!animator_->IsStopped()) {
498 animator_->Stop();
499 }
500 auto iterDirection = animationStringParams.find(DOM_ANIMATION_DIRECTION_API);
501 if (iterDirection != animationStringParams.end()) {
502 animator_->SetAnimationDirection(StringToAnimationDirection(iterDirection->second));
503 }
504 animator_->ClearInterpolators();
505 animator_->SetDuration(duration);
506 animator_->SetIteration(iterations);
507 animator_->SetStartDelay(delay);
508 animator_->SetFillMode(StringToFillMode(fillString));
509 animator_->AddInterpolator(keyframeAnimation);
510 AddListenerForEventCallback(AceType::WeakClaim(this), animator_, instance_);
511 }
512
CreateDoubleAnimation(std::unordered_map<std::string,double> & animationParams,const RefPtr<Curve> & curve)513 RefPtr<KeyframeAnimation<double>> V8AnimatorBridge::CreateDoubleAnimation(
514 std::unordered_map<std::string, double>& animationParams, const RefPtr<Curve>& curve)
515 {
516 double begin = 0.0;
517 double end = 1.0;
518 auto animationBegin = animationParams.find(DOM_ANIMATION_BEGIN);
519 if (animationBegin != animationParams.end()) {
520 begin = animationBegin->second;
521 }
522 auto animationEnd = animationParams.find(DOM_ANIMATION_END);
523 if (animationEnd != animationParams.end()) {
524 end = animationEnd->second;
525 }
526 auto keyframeBegin = AceType::MakeRefPtr<Keyframe<double>>(0.0, begin);
527 auto keyframeEnd = AceType::MakeRefPtr<Keyframe<double>>(1.0, end);
528 auto keyframeAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
529 keyframeAnimation->AddKeyframe(keyframeBegin);
530 keyframeAnimation->AddKeyframe(keyframeEnd);
531 keyframeAnimation->SetCurve(curve);
532 AddFrameListener(AceType::WeakClaim(this), keyframeAnimation, instance_);
533 return keyframeAnimation;
534 }
535
V8AnimatorTaskCreate(v8::Isolate * isolate,const RefPtr<V8AnimatorBridge> & bridge,const std::string & param)536 V8AnimatorTaskCreate::V8AnimatorTaskCreate(
537 v8::Isolate* isolate, const RefPtr<V8AnimatorBridge>& bridge, const std::string& param)
538 : bridge_(bridge), isolate_(isolate), param_(std::move(param))
539 {}
540
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)541 void V8AnimatorTaskCreate::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
542 {
543 if (!bridge_) {
544 LOGE("Create Animation Bridge failed. bridge is null.");
545 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
546 return;
547 }
548 auto bridgeFree = AceType::DynamicCast<V8AnimatorBridge>(page->GetAnimatorBridge(bridgeId));
549 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate_->GetData(V8EngineInstance::FRONTEND_DELEGATE));
550 auto jsTaskExecutor = (*delegate)->GetAnimationJsTask();
551 if (bridgeFree) {
552 auto weakBridge = AceType::WeakClaim(AceType::RawPtr(bridgeFree));
553 jsTaskExecutor.PostTask([weakBridge]() mutable {
554 auto bridgeFree = weakBridge.Upgrade();
555 if (bridgeFree != nullptr) {
556 bridgeFree.Reset();
557 }
558 });
559 }
560 page->RemoveAnimatorBridge(bridgeId);
561 bridge_->JsCreateAnimation(param_);
562 page->AddAnimatorBridge(bridgeId, bridge_);
563 }
564
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)565 void V8AnimatorTaskOperation::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
566 {
567 auto animatorBridge = AceType::DynamicCast<V8AnimatorBridge>(page->GetAnimatorBridge(bridgeId));
568 if (!animatorBridge) {
569 LOGE("no animation bridge found for bridgeId: %{public}d", bridgeId);
570 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
571 return;
572 }
573 RefPtr<Animator> animator = animatorBridge->JsGetAnimator();
574 if (!animator) {
575 LOGE("animator is null");
576 return;
577 }
578 switch (operation_) {
579 case AnimatorOperation::PLAY:
580 animator->Play();
581 break;
582 case AnimatorOperation::PAUSE:
583 animator->Pause();
584 break;
585 case AnimatorOperation::CANCEL:
586 animator->Cancel();
587 break;
588 case AnimatorOperation::FINISH:
589 animator->Finish();
590 break;
591 case AnimatorOperation::REVERSE:
592 animator->Reverse();
593 break;
594 case AnimatorOperation::NONE:
595 default:
596 break;
597 }
598 }
599
AnimatorBridgeTaskFunc(const RefPtr<JsAcePage> & page,int32_t bridgeId)600 void V8AnimatorTaskUpdate::AnimatorBridgeTaskFunc(const RefPtr<JsAcePage>& page, int32_t bridgeId)
601 {
602 auto animatorBridge = AceType::DynamicCast<V8AnimatorBridge>(page->GetAnimatorBridge(bridgeId));
603 if (!animatorBridge) {
604 LOGE("no animation bridge found for bridgeId: %{public}d", bridgeId);
605 EventReport::SendAnimationException(AnimationExcepType::ANIMATION_BRIDGE_ERR);
606 return;
607 }
608 RefPtr<Animator> animator = animatorBridge->JsGetAnimator();
609 if (!animator) {
610 LOGE("animator is null");
611 return;
612 }
613 if (!isolate_) {
614 LOGE("isolate is null");
615 return;
616 }
617 animator->ClearInterpolators();
618 UpdateAnimator(animator, animatorBridge, isolate_, params_);
619 }
620
UpdateAnimator(const RefPtr<Animator> & animator,const RefPtr<V8AnimatorBridge> & bridge,v8::Isolate * isolate,const std::unordered_map<std::string,std::string> & params)621 void V8AnimatorTaskUpdate::UpdateAnimator(const RefPtr<Animator>& animator, const RefPtr<V8AnimatorBridge>& bridge,
622 v8::Isolate* isolate, const std::unordered_map<std::string, std::string>& params)
623 {
624 int32_t iterations = 1;
625 double duration = 0.0;
626 double delay = 0.0;
627 double begin = 0.0;
628 double end = 1.0;
629 std::string curveString;
630 std::string fillString;
631 RefPtr<Curve> curve;
632 auto iterEasing = params_.find(DOM_ANIMATION_EASING);
633 if (iterEasing != params_.end()) {
634 curveString = iterEasing->second;
635 }
636 curve = CreateCurve(curveString);
637 auto iterIterations = params_.find(DOM_ANIMATION_ITERATIONS);
638 if (iterIterations != params_.end()) {
639 iterations = StringToInt(iterIterations->second);
640 }
641 auto iterDuration = params_.find(DOM_ANIMATION_DURATION_API);
642 if (iterDuration != params_.end()) {
643 duration = StringToDouble(iterDuration->second);
644 }
645 auto iterFill = params_.find(DOM_ANIMATION_FILL);
646 if (iterFill != params_.end()) {
647 fillString = iterFill->second;
648 }
649 auto iterDelay = params_.find(DOM_ANIMATION_DELAY_API);
650 if (iterDelay != params_.end()) {
651 delay = StringToDouble(iterDelay->second);
652 }
653 auto iterDirection = params_.find(DOM_ANIMATION_DIRECTION_API);
654 if (iterDirection != params_.end()) {
655 animator->SetAnimationDirection(StringToAnimationDirection(iterDirection->second));
656 }
657 auto animationBegin = params_.find(DOM_ANIMATION_BEGIN);
658 if (animationBegin != params_.end()) {
659 begin = StringToDouble(animationBegin->second);
660 }
661 auto animationEnd = params_.find(DOM_ANIMATION_END);
662 if (animationEnd != params_.end()) {
663 end = StringToDouble(animationEnd->second);
664 }
665 auto keyframeAnimation = CreateDoubleAnimation(begin, end, curve);
666 AddFrameListener(AceType::WeakClaim(RawPtr(bridge)), keyframeAnimation, isolate);
667 animator->SetDuration(duration);
668 animator->SetIteration(iterations);
669 animator->SetStartDelay(delay);
670 animator->SetFillMode(StringToFillMode(fillString));
671 animator->AddInterpolator(keyframeAnimation);
672 }
673
CreateDoubleAnimation(double begin,double end,const RefPtr<Curve> & curve)674 RefPtr<KeyframeAnimation<double>> V8AnimatorTaskUpdate::CreateDoubleAnimation(double begin, double end,
675 const RefPtr<Curve>& curve)
676 {
677 auto keyframeBegin = AceType::MakeRefPtr<Keyframe<double>>(0.0, begin);
678 auto keyframeEnd = AceType::MakeRefPtr<Keyframe<double>>(1.0, end);
679 auto keyframeAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
680 keyframeAnimation->AddKeyframe(keyframeBegin);
681 keyframeAnimation->AddKeyframe(keyframeEnd);
682 keyframeAnimation->SetCurve(curve);
683 return keyframeAnimation;
684 }
685
686 } // namespace OHOS::Ace::Framework
687