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 "bridge/declarative_frontend/jsview/js_view.h"
17
18 #include "base/log/ace_checker.h"
19 #include "base/log/ace_performance_check.h"
20 #include "base/log/ace_trace.h"
21 #include "base/memory/ace_type.h"
22 #include "base/memory/referenced.h"
23 #include "base/utils/system_properties.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/engine_helper.h"
26 #include "bridge/declarative_frontend/engine/js_execution_scope_defines.h"
27 #include "bridge/declarative_frontend/engine/js_types.h"
28 #include "bridge/declarative_frontend/jsview/js_view_stack_processor.h"
29 #include "bridge/declarative_frontend/jsview/models/view_full_update_model_impl.h"
30 #include "bridge/declarative_frontend/jsview/models/view_partial_update_model_impl.h"
31 #include "core/common/container.h"
32 #include "core/common/container_scope.h"
33 #include "core/components_ng/base/ui_node.h"
34 #include "core/components_ng/base/view_full_update_model.h"
35 #include "core/components_ng/base/view_full_update_model_ng.h"
36 #include "core/components_ng/base/view_partial_update_model.h"
37 #include "core/components_ng/base/view_partial_update_model_ng.h"
38 #include "core/components_ng/base/view_stack_model.h"
39 #include "core/components_ng/layout/layout_wrapper.h"
40 #include "core/pipeline/base/element_register.h"
41
42 namespace OHOS::Ace {
43
44 std::unique_ptr<ViewFullUpdateModel> ViewFullUpdateModel::instance_ = nullptr;
45 std::mutex ViewFullUpdateModel::mutex_;
46 std::unique_ptr<ViewPartialUpdateModel> ViewPartialUpdateModel::instance_ = nullptr;
47 std::mutex ViewPartialUpdateModel::mutex_;
48
GetInstance()49 ViewFullUpdateModel* ViewFullUpdateModel::GetInstance()
50 {
51 if (!instance_) {
52 std::lock_guard<std::mutex> lock(mutex_);
53 if (!instance_) {
54 #ifdef NG_BUILD
55 instance_.reset(new NG::ViewFullUpdateModelNG());
56 #else
57 if (Container::IsCurrentUseNewPipeline()) {
58 instance_.reset(new NG::ViewFullUpdateModelNG());
59 } else {
60 instance_.reset(new Framework::ViewFullUpdateModelImpl());
61 }
62 #endif
63 }
64 }
65 return instance_.get();
66 }
67
GetInstance()68 ViewPartialUpdateModel* ViewPartialUpdateModel::GetInstance()
69 {
70 if (!instance_) {
71 std::lock_guard<std::mutex> lock(mutex_);
72 if (!instance_) {
73 #ifdef NG_BUILD
74 instance_.reset(new NG::ViewPartialUpdateModelNG());
75 #else
76 if (Container::IsCurrentUseNewPipeline()) {
77 instance_.reset(new NG::ViewPartialUpdateModelNG());
78 } else {
79 instance_.reset(new Framework::ViewPartialUpdateModelImpl());
80 }
81 #endif
82 }
83 }
84 return instance_.get();
85 }
86
87 } // namespace OHOS::Ace
88
89 namespace OHOS::Ace::Framework {
90
JSBind(BindingTarget object)91 void JSView::JSBind(BindingTarget object)
92 {
93 JSViewPartialUpdate::JSBind(object);
94 JSViewFullUpdate::JSBind(object);
95 }
96
RenderJSExecution()97 void JSView::RenderJSExecution()
98 {
99 JAVASCRIPT_EXECUTION_SCOPE_STATIC;
100 if (!jsViewFunction_) {
101 LOGE("JSView: InternalRender jsViewFunction_ error");
102 return;
103 }
104 {
105 ACE_SCORING_EVENT("Component.AboutToRender");
106 jsViewFunction_->ExecuteAboutToRender();
107 }
108 {
109 ACE_SCORING_EVENT("Component.Build");
110 ViewStackModel::GetInstance()->PushKey(viewId_);
111 jsViewFunction_->ExecuteRender();
112 ViewStackModel::GetInstance()->PopKey();
113 }
114 {
115 ACE_SCORING_EVENT("Component.OnRenderDone");
116 jsViewFunction_->ExecuteOnRenderDone();
117 if (notifyRenderDone_) {
118 notifyRenderDone_();
119 }
120 }
121 }
122
SyncInstanceId()123 void JSView::SyncInstanceId()
124 {
125 restoreInstanceId_ = Container::CurrentId();
126 ContainerScope::UpdateCurrent(instanceId_);
127 }
128
RestoreInstanceId()129 void JSView::RestoreInstanceId()
130 {
131 ContainerScope::UpdateCurrent(restoreInstanceId_);
132 }
133
GetInstanceId(const JSCallbackInfo & info)134 void JSView::GetInstanceId(const JSCallbackInfo& info)
135 {
136 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(instanceId_)));
137 }
138
JsSetCardId(int64_t cardId)139 void JSView::JsSetCardId(int64_t cardId)
140 {
141 cardId_ = cardId;
142 }
143
JsGetCardId(const JSCallbackInfo & info)144 void JSView::JsGetCardId(const JSCallbackInfo& info)
145 {
146 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(cardId_)));
147 }
148
JSViewFullUpdate(const std::string & viewId,JSRef<JSObject> jsObject,JSRef<JSFunc> jsRenderFunction)149 JSViewFullUpdate::JSViewFullUpdate(const std::string& viewId, JSRef<JSObject> jsObject, JSRef<JSFunc> jsRenderFunction)
150 {
151 viewId_ = viewId;
152 jsViewFunction_ = AceType::MakeRefPtr<ViewFunctions>(jsObject, jsRenderFunction);
153 jsViewObject_ = jsObject;
154 LOGD("JSViewFullUpdate constructor");
155 }
156
~JSViewFullUpdate()157 JSViewFullUpdate::~JSViewFullUpdate()
158 {
159 LOGD("JSViewFullUpdate destructor");
160 jsViewFunction_.Reset();
161 };
162
CreateViewNode()163 RefPtr<AceType> JSViewFullUpdate::CreateViewNode()
164 {
165 auto appearFunc = [weak = AceType::WeakClaim(this)] {
166 auto jsView = weak.Upgrade();
167 CHECK_NULL_VOID(jsView);
168 ContainerScope scope(jsView->GetInstanceId());
169 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Appear");
170 if (jsView->viewNode_.Invalid() && jsView->jsViewFunction_) {
171 jsView->jsViewFunction_->ExecuteAppear();
172 }
173 };
174
175 auto renderFunction = [weak = AceType::WeakClaim(this)]() -> RefPtr<AceType> {
176 auto jsView = weak.Upgrade();
177 CHECK_NULL_RETURN(jsView, nullptr);
178 ContainerScope scope(jsView->GetInstanceId());
179 return jsView->InternalRender();
180 };
181
182 auto pageTransitionFunction = [weak = AceType::WeakClaim(this)]() {
183 auto jsView = weak.Upgrade();
184 if (!jsView || !jsView->jsViewFunction_) {
185 return;
186 }
187 {
188 ContainerScope scope(jsView->GetInstanceId());
189 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Transition");
190 jsView->jsViewFunction_->ExecuteTransition();
191 }
192 };
193
194 auto updateViewNodeFunction = [weak = AceType::WeakClaim(this)](const RefPtr<AceType>& node) {
195 auto jsView = weak.Upgrade();
196 if (jsView) {
197 jsView->viewNode_ = node;
198 }
199 };
200
201 auto removeFunction = [weak = AceType::WeakClaim(this)]() -> void {
202 auto jsView = weak.Upgrade();
203 if (jsView && jsView->jsViewFunction_) {
204 ContainerScope scope(jsView->GetInstanceId());
205 jsView->jsViewFunction_->ExecuteDisappear();
206 }
207 };
208
209 NodeInfo info = { .viewId = viewId_,
210 .appearFunc = std::move(appearFunc),
211 .renderFunc = std::move(renderFunction),
212 .removeFunc = std::move(removeFunction),
213 .updateNodeFunc = std::move(updateViewNodeFunction),
214 .isStatic = IsStatic() };
215
216 if (jsViewFunction_ && jsViewFunction_->HasPageTransition()) {
217 info.pageTransitionFunc = std::move(pageTransitionFunction);
218 }
219
220 return ViewFullUpdateModel::GetInstance()->CreateNode(std::move(info));
221 }
222
InternalRender()223 RefPtr<AceType> JSViewFullUpdate::InternalRender()
224 {
225 JAVASCRIPT_EXECUTION_SCOPE_STATIC;
226 needsUpdate_ = false;
227 RenderJSExecution();
228 CleanUpAbandonedChild();
229 jsViewFunction_->Destroy();
230 return ViewStackModel::GetInstance()->Finish();
231 }
232
233 /**
234 * marks the JSView's composed component as needing update / rerender
235 */
MarkNeedUpdate()236 void JSViewFullUpdate::MarkNeedUpdate()
237 {
238 ACE_SCOPED_TRACE("JSView::MarkNeedUpdate");
239 needsUpdate_ = ViewFullUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_);
240 }
241
Destroy(JSView * parentCustomView)242 void JSViewFullUpdate::Destroy(JSView* parentCustomView)
243 {
244 LOGD("JSViewFullUpdate::Destroy start");
245 DestroyChild(parentCustomView);
246 {
247 ACE_SCORING_EVENT("Component[" + viewId_ + "].Disappear");
248 jsViewFunction_->ExecuteDisappear();
249 }
250 {
251 ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToBeDeleted");
252 jsViewFunction_->ExecuteAboutToBeDeleted();
253 }
254 jsViewObject_.Reset();
255 LOGD("JSViewFullUpdate::Destroy end");
256 }
257
Create(const JSCallbackInfo & info)258 void JSViewFullUpdate::Create(const JSCallbackInfo& info)
259 {
260 LOGD("Creating new View for full update");
261 ACE_DCHECK(!Container::IsCurrentUsePartialUpdate());
262
263 if (info[0]->IsObject()) {
264 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
265 auto* view = object->Unwrap<JSViewFullUpdate>();
266 if (view == nullptr) {
267 LOGE("JSView is null");
268 return;
269 }
270 ViewStackModel::GetInstance()->Push(view->CreateViewNode(), true);
271 } else {
272 LOGE("JSView Object is expected.");
273 }
274 }
275
JSBind(BindingTarget object)276 void JSViewFullUpdate::JSBind(BindingTarget object)
277 {
278 LOGD("JSViewFullUpdate::Bind");
279 JSClass<JSViewFullUpdate>::Declare("NativeViewFullUpdate");
280 JSClass<JSViewFullUpdate>::StaticMethod("create", &JSViewFullUpdate::Create);
281 JSClass<JSViewFullUpdate>::Method("markNeedUpdate", &JSViewFullUpdate::MarkNeedUpdate);
282 JSClass<JSViewFullUpdate>::Method("syncInstanceId", &JSViewFullUpdate::SyncInstanceId);
283 JSClass<JSViewFullUpdate>::Method("restoreInstanceId", &JSViewFullUpdate::RestoreInstanceId);
284 JSClass<JSViewFullUpdate>::CustomMethod("getInstanceId", &JSViewFullUpdate::GetInstanceId);
285 JSClass<JSViewFullUpdate>::Method("needsUpdate", &JSViewFullUpdate::NeedsUpdate);
286 JSClass<JSViewFullUpdate>::Method("markStatic", &JSViewFullUpdate::MarkStatic);
287 JSClass<JSViewFullUpdate>::Method("setCardId", &JSViewFullUpdate::JsSetCardId);
288 JSClass<JSViewFullUpdate>::CustomMethod("getCardId", &JSViewFullUpdate::JsGetCardId);
289 JSClass<JSViewFullUpdate>::CustomMethod("findChildById", &JSViewFullUpdate::FindChildById);
290 JSClass<JSViewFullUpdate>::CustomMethod("findChildByIdForPreview", &JSViewFullUpdate::FindChildByIdForPreview);
291 JSClass<JSViewFullUpdate>::InheritAndBind<JSViewAbstract>(object, ConstructorCallback, DestructorCallback);
292 }
293
FindChildById(const JSCallbackInfo & info)294 void JSViewFullUpdate::FindChildById(const JSCallbackInfo& info)
295 {
296 LOGD("JSView::FindChildById");
297 if (info[0]->IsNumber() || info[0]->IsString()) {
298 std::string viewId = info[0]->ToString();
299 info.SetReturnValue(GetChildById(viewId));
300 } else {
301 LOGE("JSView FindChildById with invalid arguments.");
302 JSException::Throw("%s", "JSView FindChildById with invalid arguments.");
303 }
304 }
305
FindChildByIdForPreview(const JSCallbackInfo & info)306 void JSViewFullUpdate::FindChildByIdForPreview(const JSCallbackInfo& info)
307 {
308 if (!info[0]->IsNumber()) {
309 LOGE("info[0] is not a number");
310 return;
311 }
312 std::string viewId = std::to_string(info[0]->ToNumber<int32_t>());
313 if (viewId_ == viewId) {
314 info.SetReturnValue(jsViewObject_);
315 return;
316 }
317 JSRef<JSObject> targetView = JSRef<JSObject>::New();
318 for (auto&& child : customViewChildren_) {
319 if (GetChildByViewId(viewId, child.second, targetView)) {
320 break;
321 }
322 }
323 auto view = targetView->Unwrap<JSViewFullUpdate>();
324 if (view) {
325 LOGD("find targetView success");
326 info.SetReturnValue(targetView);
327 }
328 return;
329 }
330
GetChildByViewId(const std::string & viewId,JSRef<JSObject> & childView,JSRef<JSObject> & targetView)331 bool JSViewFullUpdate::GetChildByViewId(
332 const std::string& viewId, JSRef<JSObject>& childView, JSRef<JSObject>& targetView)
333 {
334 auto* view = childView->Unwrap<JSViewFullUpdate>();
335 if (view && view->viewId_ == viewId) {
336 targetView = childView;
337 return true;
338 }
339 for (auto&& child : view->customViewChildren_) {
340 if (GetChildByViewId(viewId, child.second, targetView)) {
341 return true;
342 }
343 }
344 return false;
345 }
346
ConstructorCallback(const JSCallbackInfo & info)347 void JSViewFullUpdate::ConstructorCallback(const JSCallbackInfo& info)
348 {
349 JSRef<JSObject> thisObj = info.This();
350 JSRef<JSVal> renderFunc = thisObj->GetProperty("render");
351 if (!renderFunc->IsFunction()) {
352 LOGE("View derived classes must provide render(){...} function");
353 JSException::Throw("%s", "View derived classes must provide render(){...} function");
354 return;
355 }
356
357 int argc = info.Length();
358 if (argc > 1 && (info[0]->IsNumber() || info[0]->IsString())) {
359 std::string viewId = info[0]->ToString();
360 auto instance = AceType::MakeRefPtr<JSViewFullUpdate>(viewId, info.This(), JSRef<JSFunc>::Cast(renderFunc));
361 auto context = info.GetExecutionContext();
362 instance->SetContext(context);
363 instance->IncRefCount();
364 info.SetReturnValue(AceType::RawPtr(instance));
365 if (!info[1]->IsUndefined() && info[1]->IsObject()) {
366 JSRef<JSObject> parentObj = JSRef<JSObject>::Cast(info[1]);
367 auto* parentView = parentObj->Unwrap<JSViewFullUpdate>();
368 if (parentView != nullptr) {
369 auto id = parentView->AddChildById(viewId, info.This());
370 instance->id_ = id;
371 }
372 }
373 LOGD("JSView ConstructorCallback: %{public}s", instance->id_.c_str());
374 } else {
375 LOGE("JSView creation with invalid arguments.");
376 JSException::Throw("%s", "JSView creation with invalid arguments.");
377 }
378 }
379
DestructorCallback(JSViewFullUpdate * view)380 void JSViewFullUpdate::DestructorCallback(JSViewFullUpdate* view)
381 {
382 if (view == nullptr) {
383 LOGE("JSViewFullUpdate::DestructorCallback failed: the view is nullptr");
384 return;
385 }
386 LOGD("JSViewFullUpdate(DestructorCallback) start: %{public}s", view->id_.c_str());
387 view->DecRefCount();
388 LOGD("JSViewFullUpdate(DestructorCallback) end");
389 }
390
DestroyChild(JSView * parentCustomView)391 void JSViewFullUpdate::DestroyChild(JSView* parentCustomView)
392 {
393 LOGD("JSViewFullUpdate::DestroyChild start");
394 for (auto&& child : customViewChildren_) {
395 auto* view = child.second->Unwrap<JSView>();
396 if (view != nullptr) {
397 view->Destroy(this);
398 }
399 child.second.Reset();
400 }
401 customViewChildren_.clear();
402 for (auto&& lazyChild : customViewChildrenWithLazy_) {
403 auto* view = lazyChild.second->Unwrap<JSView>();
404 if (view != nullptr) {
405 view->Destroy(this);
406 }
407 lazyChild.second.Reset();
408 }
409 customViewChildrenWithLazy_.clear();
410 LOGD("JSViewFullUpdate::DestroyChild end");
411 }
412
CleanUpAbandonedChild()413 void JSViewFullUpdate::CleanUpAbandonedChild()
414 {
415 auto startIter = customViewChildren_.begin();
416 auto endIter = customViewChildren_.end();
417 std::vector<std::string> removedViewIds;
418 while (startIter != endIter) {
419 auto found = lastAccessedViewIds_.find(startIter->first);
420 if (found == lastAccessedViewIds_.end()) {
421 LOGD(" found abandoned view with id %{public}s", startIter->first.c_str());
422 removedViewIds.emplace_back(startIter->first);
423 auto* view = startIter->second->Unwrap<JSView>();
424 if (view != nullptr) {
425 view->Destroy(this);
426 }
427 startIter->second.Reset();
428 }
429 ++startIter;
430 }
431
432 for (auto& viewId : removedViewIds) {
433 customViewChildren_.erase(viewId);
434 }
435
436 lastAccessedViewIds_.clear();
437 }
438
GetChildById(const std::string & viewId)439 JSRef<JSObject> JSViewFullUpdate::GetChildById(const std::string& viewId)
440 {
441 std::string id = ViewStackModel::GetInstance()->ProcessViewId(viewId);
442 auto found = customViewChildren_.find(id);
443 if (found != customViewChildren_.end()) {
444 ChildAccessedById(id);
445 return found->second;
446 }
447 auto lazyItem = customViewChildrenWithLazy_.find(id);
448 if (lazyItem != customViewChildrenWithLazy_.end()) {
449 return lazyItem->second;
450 }
451 return {};
452 }
453
AddChildById(const std::string & viewId,const JSRef<JSObject> & obj)454 std::string JSViewFullUpdate::AddChildById(const std::string& viewId, const JSRef<JSObject>& obj)
455 {
456 std::string id = ViewStackModel::GetInstance()->ProcessViewId(viewId);
457 JSView* jsView = nullptr;
458 if (isLazyForEachProcessed_) {
459 auto result = customViewChildrenWithLazy_.try_emplace(id, obj);
460 if (!result.second) {
461 jsView = result.first->second->Unwrap<JSView>();
462 result.first->second = obj;
463 } else {
464 lazyItemGroups_[lazyItemGroupId_].emplace_back(id);
465 }
466 } else {
467 auto result = customViewChildren_.try_emplace(id, obj);
468 if (!result.second) {
469 jsView = result.first->second->Unwrap<JSView>();
470 result.first->second = obj;
471 }
472 ChildAccessedById(id);
473 }
474 if (jsView != nullptr) {
475 jsView->Destroy(this);
476 }
477 return id;
478 }
479
RemoveChildGroupById(const std::string & viewId)480 void JSViewFullUpdate::RemoveChildGroupById(const std::string& viewId)
481 {
482 // js runtime may be released
483 CHECK_JAVASCRIPT_SCOPE_AND_RETURN;
484 JAVASCRIPT_EXECUTION_SCOPE_STATIC;
485 LOGD("JSViewFullUpdate::RemoveChildGroupById in lazy for each case: %{public}s", viewId.c_str());
486 auto iter = lazyItemGroups_.find(viewId);
487 if (iter == lazyItemGroups_.end()) {
488 LOGI("can not find this group to delete: %{public}s", viewId.c_str());
489 return;
490 }
491 std::vector<std::string> removedViewIds;
492 for (auto&& item : iter->second) {
493 auto removeView = customViewChildrenWithLazy_.find(item);
494 if (removeView != customViewChildrenWithLazy_.end()) {
495 if (!removeView->second.IsEmpty()) {
496 auto* view = removeView->second->Unwrap<JSView>();
497 if (view != nullptr) {
498 view->Destroy(this);
499 }
500 removeView->second.Reset();
501 }
502 removedViewIds.emplace_back(item);
503 }
504 }
505
506 for (auto&& removeId : removedViewIds) {
507 customViewChildrenWithLazy_.erase(removeId);
508 }
509 lazyItemGroups_.erase(iter);
510 }
511
ChildAccessedById(const std::string & viewId)512 void JSViewFullUpdate::ChildAccessedById(const std::string& viewId)
513 {
514 lastAccessedViewIds_.emplace(viewId);
515 }
516
517 // =================================================================
518
519 std::map<std::string, JSRef<JSObject>> JSViewStackProcessor::viewMap_;
520
JSViewPartialUpdate(JSRef<JSObject> jsViewObject)521 JSViewPartialUpdate::JSViewPartialUpdate(JSRef<JSObject> jsViewObject)
522 {
523 jsViewFunction_ = AceType::MakeRefPtr<ViewFunctions>(jsViewObject);
524 LOGD("JSViewPartialUpdate constructor");
525 // keep the reference to the JS View object to prevent GC
526 jsViewObject_ = jsViewObject;
527 }
528
~JSViewPartialUpdate()529 JSViewPartialUpdate::~JSViewPartialUpdate()
530 {
531 LOGD("JSViewPartialUpdate destructor");
532 jsViewFunction_.Reset();
533 };
534
CreateViewNode()535 RefPtr<AceType> JSViewPartialUpdate::CreateViewNode()
536 {
537 auto updateViewIdFunc = [weak = AceType::WeakClaim(this)](const std::string viewId) {
538 auto jsView = weak.Upgrade();
539 CHECK_NULL_VOID(jsView);
540 jsView->viewId_ = viewId;
541 };
542
543 auto appearFunc = [weak = AceType::WeakClaim(this)]() {
544 auto jsView = weak.Upgrade();
545 CHECK_NULL_VOID(jsView);
546 ContainerScope scope(jsView->GetInstanceId());
547 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Appear");
548 if (jsView->jsViewFunction_) {
549 jsView->jsViewFunction_->ExecuteAppear();
550 }
551 };
552
553 auto renderFunction = [weak = AceType::WeakClaim(this)]() -> RefPtr<AceType> {
554 auto jsView = weak.Upgrade();
555 CHECK_NULL_RETURN(jsView, nullptr);
556 ContainerScope scope(jsView->GetInstanceId());
557 if (!jsView->isFirstRender_) {
558 LOGW("the js view has already called initial render");
559 return nullptr;
560 }
561 jsView->isFirstRender_ = false;
562 return jsView->InitialRender();
563 };
564
565 auto updateFunction = [weak = AceType::WeakClaim(this)]() -> void {
566 auto jsView = weak.Upgrade();
567 CHECK_NULL_VOID(jsView);
568 ContainerScope scope(jsView->GetInstanceId());
569 if (!jsView->needsUpdate_) {
570 LOGW("the js view does not need to update");
571 return;
572 }
573 jsView->needsUpdate_ = false;
574 LOGD("Rerender function start for ComposedElement elmtId %{public}s - start...", jsView->viewId_.c_str());
575 {
576 ACE_SCOPED_TRACE("JSView: ExecuteRerender");
577 jsView->jsViewFunction_->ExecuteRerender();
578 }
579 for (const UpdateTask& updateTask : jsView->pendingUpdateTasks_) {
580 ViewPartialUpdateModel::GetInstance()->FlushUpdateTask(updateTask);
581 }
582 jsView->pendingUpdateTasks_.clear();
583 };
584
585 auto reloadFunction = [weak = AceType::WeakClaim(this)](bool deep) {
586 auto jsView = weak.Upgrade();
587 CHECK_NULL_VOID(jsView);
588 CHECK_NULL_VOID(jsView->jsViewFunction_);
589 ContainerScope scope(jsView->GetInstanceId());
590 jsView->jsViewFunction_->ExecuteReload(deep);
591 };
592
593 // @Component level complete reload, can detect added/deleted frame nodes
594 auto completeReloadFunc = [weak = AceType::WeakClaim(this)]() -> RefPtr<AceType> {
595 auto jsView = weak.Upgrade();
596 CHECK_NULL_RETURN(jsView, nullptr);
597 ContainerScope scope(jsView->GetInstanceId());
598 return jsView->InitialRender();
599 };
600
601 auto pageTransitionFunction = [weak = AceType::WeakClaim(this)]() {
602 auto jsView = weak.Upgrade();
603 CHECK_NULL_VOID_NOLOG(jsView);
604 CHECK_NULL_VOID_NOLOG(jsView->jsViewFunction_);
605 ContainerScope scope(jsView->GetInstanceId());
606 {
607 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Transition");
608 jsView->jsViewFunction_->ExecuteTransition();
609 }
610 };
611
612 auto removeFunction = [weak = AceType::WeakClaim(this)]() -> void {
613 LOGD("call remove view function");
614 auto jsView = weak.Upgrade();
615 CHECK_NULL_VOID(jsView);
616 ContainerScope scope(jsView->GetInstanceId());
617 jsView->Destroy(nullptr);
618 jsView->viewNode_.Reset();
619 };
620
621 auto updateViewNodeFunction = [weak = AceType::WeakClaim(this)](const RefPtr<AceType>& node) {
622 auto jsView = weak.Upgrade();
623 CHECK_NULL_VOID_NOLOG(jsView);
624 jsView->viewNode_ = node;
625 };
626
627 auto nodeUpdateFunc = [weak = AceType::WeakClaim(this)](int32_t nodeId) {
628 auto jsView = weak.Upgrade();
629 CHECK_NULL_VOID(jsView);
630 CHECK_NULL_VOID(jsView->jsViewFunction_);
631 ContainerScope scope(jsView->GetInstanceId());
632 jsView->jsViewFunction_->ExecuteForceNodeRerender(nodeId);
633 };
634
635 auto recycleCustomNode = [weak = AceType::WeakClaim(this)](const RefPtr<NG::CustomNodeBase>& recycleNode) -> void {
636 auto jsView = weak.Upgrade();
637 CHECK_NULL_VOID(jsView);
638 ContainerScope scope(jsView->GetInstanceId());
639 recycleNode->ResetRecycle();
640 AceType::DynamicCast<NG::UINode>(recycleNode)->SetActive(false);
641 jsView->SetRecycleCustomNode(recycleNode);
642 jsView->jsViewFunction_->ExecuteAboutToRecycle();
643 jsView->jsViewFunction_->ExecuteRecycle(jsView->GetRecycleCustomNodeName());
644 };
645
646 auto setActiveFunc = [weak = AceType::WeakClaim(this)](bool active) -> void {
647 if (!AceApplicationInfo::GetInstance().IsDelayedUpdateOnInactive()) {
648 return;
649 }
650 auto jsView = weak.Upgrade();
651 CHECK_NULL_VOID(jsView);
652 ContainerScope scope(jsView->GetInstanceId());
653 jsView->jsViewFunction_->ExecuteSetActive(active);
654 };
655
656 NodeInfoPU info = { .appearFunc = std::move(appearFunc),
657 .renderFunc = std::move(renderFunction),
658 .updateFunc = std::move(updateFunction),
659 .removeFunc = std::move(removeFunction),
660 .updateNodeFunc = std::move(updateViewNodeFunction),
661 .pageTransitionFunc = std::move(pageTransitionFunction),
662 .reloadFunc = std::move(reloadFunction),
663 .completeReloadFunc = std::move(completeReloadFunc),
664 .nodeUpdateFunc = std::move(nodeUpdateFunc),
665 .recycleCustomNodeFunc = recycleCustomNode,
666 .setActiveFunc = std::move(setActiveFunc),
667 .hasMeasureOrLayout = jsViewFunction_->HasMeasure() || jsViewFunction_->HasLayout() ||
668 jsViewFunction_->HasMeasureSize() || jsViewFunction_->HasPlaceChildren(),
669 .isStatic = IsStatic(),
670 .jsViewName = GetJSViewName() };
671
672 auto measureFunc = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void {
673 auto jsView = weak.Upgrade();
674 CHECK_NULL_VOID(jsView);
675 ContainerScope scope(jsView->GetInstanceId());
676 jsView->jsViewFunction_->ExecuteMeasure(layoutWrapper);
677 };
678 if (jsViewFunction_->HasMeasure()) {
679 info.measureFunc = std::move(measureFunc);
680 }
681
682 auto layoutFunc = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void {
683 auto jsView = weak.Upgrade();
684 CHECK_NULL_VOID(jsView);
685 ContainerScope scope(jsView->GetInstanceId());
686 jsView->jsViewFunction_->ExecuteLayout(layoutWrapper);
687 };
688 if (jsViewFunction_->HasLayout()) {
689 info.layoutFunc = std::move(layoutFunc);
690 }
691
692 if (jsViewFunction_->HasMeasureSize()) {
693 auto measureSizeFunc = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void {
694 auto jsView = weak.Upgrade();
695 CHECK_NULL_VOID(jsView);
696 ContainerScope scope(jsView->GetInstanceId());
697 jsView->jsViewFunction_->ExecuteMeasureSize(layoutWrapper);
698 };
699 info.measureSizeFunc = std::move(measureSizeFunc);
700 }
701
702 if (jsViewFunction_->HasPlaceChildren()) {
703 auto placeChildren = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void {
704 auto jsView = weak.Upgrade();
705 CHECK_NULL_VOID(jsView);
706 ContainerScope scope(jsView->GetInstanceId());
707 jsView->jsViewFunction_->ExecutePlaceChildren(layoutWrapper);
708 };
709 info.placeChildrenFunc = std::move(placeChildren);
710 }
711
712 auto node = ViewPartialUpdateModel::GetInstance()->CreateNode(std::move(info));
713 #ifdef PREVIEW
714 auto uiNode = AceType::DynamicCast<NG::UINode>(node);
715 if (uiNode) {
716 Framework::JSViewStackProcessor::SetViewMap(std::to_string(uiNode->GetId()), jsViewObject_);
717 }
718 #endif
719
720 if (AceChecker::IsPerformanceCheckEnabled()) {
721 auto uiNode = AceType::DynamicCast<NG::UINode>(node);
722 if (uiNode) {
723 auto codeInfo = EngineHelper::GetPositionOnJsCode();
724 uiNode->SetRow(codeInfo.first);
725 uiNode->SetCol(codeInfo.second);
726 }
727 }
728 return node;
729 }
730
InitialRender()731 RefPtr<AceType> JSViewPartialUpdate::InitialRender()
732 {
733 needsUpdate_ = false;
734 RenderJSExecution();
735 return ViewStackModel::GetInstance()->Finish();
736 }
737
738 // parentCustomView in not used by PartialUpdate
Destroy(JSView * parentCustomView)739 void JSViewPartialUpdate::Destroy(JSView* parentCustomView)
740 {
741 if (jsViewFunction_ == nullptr) {
742 // already called Destroy before
743 return;
744 }
745
746 LOGD("JSViewPartialUpdate::Destroy start");
747 {
748 ACE_SCORING_EVENT("Component[" + viewId_ + "].Disappear");
749 jsViewFunction_->ExecuteDisappear();
750 }
751 {
752 ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToBeDeleted");
753 jsViewFunction_->ExecuteAboutToBeDeleted();
754 }
755 pendingUpdateTasks_.clear();
756 jsViewFunction_->Destroy();
757 jsViewFunction_.Reset();
758
759 // release reference to JS view object, and allow GC, calls DestructorCallback
760 jsViewObject_.Reset();
761 LOGD("JSViewPartialUpdate::Destroy end");
762 }
763
MarkNeedUpdate()764 void JSViewPartialUpdate::MarkNeedUpdate()
765 {
766 needsUpdate_ = ViewPartialUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_);
767 }
768
769 /**
770 * in JS View.create(new View(...));
771 * used for FullRender case, not for re-render case
772 */
Create(const JSCallbackInfo & info)773 void JSViewPartialUpdate::Create(const JSCallbackInfo& info)
774 {
775 LOGD("Creating new JSViewPartialUpdate for partial update");
776 ACE_DCHECK(Container::IsCurrentUsePartialUpdate());
777
778 if (info[0]->IsObject()) {
779 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
780 auto* view = object->Unwrap<JSView>();
781 if (view == nullptr) {
782 LOGE("View is null");
783 return;
784 }
785 ViewStackModel::GetInstance()->Push(view->CreateViewNode(), true);
786 } else {
787 LOGE("View Object is expected.");
788 }
789 }
790
791 enum {
792 PARAM_VIEW_OBJ = 0,
793 PARAM_IS_RECYCLE,
794 PARAM_NODE_NAME,
795 PARAM_RECYCLE_UPDATE_FUNC,
796
797 PARAM_SIZE,
798 };
799
ParseRecycleParams(const JSCallbackInfo & info,JSRef<JSVal> (& params)[PARAM_SIZE])800 bool ParseRecycleParams(const JSCallbackInfo& info, JSRef<JSVal> (¶ms)[PARAM_SIZE])
801 {
802 if (info.Length() != PARAM_SIZE) {
803 return false;
804 }
805 if (!info[PARAM_VIEW_OBJ]->IsObject()) {
806 return false;
807 }
808 if (!info[PARAM_IS_RECYCLE]->IsBoolean()) {
809 return false;
810 }
811 if (!info[PARAM_NODE_NAME]->IsString()) {
812 return false;
813 }
814 if (!info[PARAM_RECYCLE_UPDATE_FUNC]->IsFunction()) {
815 return false;
816 }
817
818 for (int32_t idx = PARAM_VIEW_OBJ; idx < PARAM_SIZE; ++idx) {
819 params[idx] = info[idx];
820 }
821 return true;
822 }
823
824 /**
825 * in JS ViewPU.createRecycle(...)
826 * create a recyclable custom node
827 */
CreateRecycle(const JSCallbackInfo & info)828 void JSViewPartialUpdate::CreateRecycle(const JSCallbackInfo& info)
829 {
830 ACE_DCHECK(Container::IsCurrentUsePartialUpdate());
831
832 JSRef<JSVal> params[PARAM_SIZE];
833 if (!ParseRecycleParams(info, params)) {
834 LOGE("Invalid parameters");
835 return;
836 }
837
838 auto viewObj = JSRef<JSObject>::Cast(params[PARAM_VIEW_OBJ]);
839 auto* view = viewObj->Unwrap<JSViewPartialUpdate>();
840 if (!view) {
841 LOGE("Invalid JSView");
842 return;
843 }
844 auto recycle = params[PARAM_IS_RECYCLE]->ToBoolean();
845 auto nodeName = params[PARAM_NODE_NAME]->ToString();
846 auto jsRecycleUpdateFunc =
847 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(params[PARAM_RECYCLE_UPDATE_FUNC]));
848 auto recycleUpdateFunc = [weak = AceType::WeakClaim(view), execCtx = info.GetExecutionContext(),
849 func = std::move(jsRecycleUpdateFunc)]() -> void {
850 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
851 auto jsView = weak.Upgrade();
852 CHECK_NULL_VOID(jsView);
853 jsView->SetIsRecycleRerender(true);
854 func->ExecuteJS();
855 jsView->SetIsRecycleRerender(false);
856 };
857
858 // update view and node property
859 view->SetRecycleCustomNodeName(nodeName);
860
861 // get or create recycle node
862 if (recycle) {
863 auto node = view->GetCachedRecycleNode();
864 node->SetRecycleRenderFunc(std::move(recycleUpdateFunc));
865 auto newElmtId = ViewStackModel::GetInstance()->GetElmtIdToAccountFor();
866 auto uiNode = AceType::DynamicCast<NG::UINode>(node);
867 ElementRegister::GetInstance()->UpdateRecycleElmtId(uiNode->GetId(), newElmtId);
868 uiNode->UpdateRecycleElmtId(newElmtId);
869 ViewStackModel::GetInstance()->Push(node, true);
870 } else {
871 ViewStackModel::GetInstance()->Push(view->CreateViewNode(), true);
872 }
873 }
874
JSBind(BindingTarget object)875 void JSViewPartialUpdate::JSBind(BindingTarget object)
876 {
877 LOGD("JSViewPartialUpdate::Bind");
878 JSClass<JSViewPartialUpdate>::Declare("NativeViewPartialUpdate");
879 MethodOptions opt = MethodOptions::NONE;
880
881 JSClass<JSViewPartialUpdate>::StaticMethod("create", &JSViewPartialUpdate::Create, opt);
882 JSClass<JSViewPartialUpdate>::StaticMethod("createRecycle", &JSViewPartialUpdate::CreateRecycle, opt);
883 JSClass<JSViewPartialUpdate>::Method("markNeedUpdate", &JSViewPartialUpdate::MarkNeedUpdate);
884 JSClass<JSViewPartialUpdate>::Method("syncInstanceId", &JSViewPartialUpdate::SyncInstanceId);
885 JSClass<JSViewPartialUpdate>::Method("restoreInstanceId", &JSViewPartialUpdate::RestoreInstanceId);
886 JSClass<JSViewPartialUpdate>::CustomMethod("getInstanceId", &JSViewPartialUpdate::GetInstanceId);
887 JSClass<JSViewPartialUpdate>::Method("markStatic", &JSViewPartialUpdate::MarkStatic);
888 JSClass<JSViewPartialUpdate>::Method("finishUpdateFunc", &JSViewPartialUpdate::JsFinishUpdateFunc);
889 JSClass<JSViewPartialUpdate>::Method("setCardId", &JSViewPartialUpdate::JsSetCardId);
890 JSClass<JSViewPartialUpdate>::CustomMethod("getCardId", &JSViewPartialUpdate::JsGetCardId);
891 JSClass<JSViewPartialUpdate>::Method("elmtIdExists", &JSViewPartialUpdate::JsElementIdExists);
892 JSClass<JSViewPartialUpdate>::CustomMethod("isLazyItemRender", &JSViewPartialUpdate::JSGetProxiedItemRenderState);
893 JSClass<JSViewPartialUpdate>::CustomMethod("isFirstRender", &JSViewPartialUpdate::IsFirstRender);
894 JSClass<JSViewPartialUpdate>::CustomMethod(
895 "findChildByIdForPreview", &JSViewPartialUpdate::FindChildByIdForPreview);
896 JSClass<JSViewPartialUpdate>::CustomMethod(
897 "resetRecycleCustomNode", &JSViewPartialUpdate::JSResetRecycleCustomNode);
898 JSClass<JSViewPartialUpdate>::InheritAndBind<JSViewAbstract>(object, ConstructorCallback, DestructorCallback);
899 }
900
ConstructorCallback(const JSCallbackInfo & info)901 void JSViewPartialUpdate::ConstructorCallback(const JSCallbackInfo& info)
902 {
903 LOGD("creating C++ and JS View Objects ...");
904 JSRef<JSObject> thisObj = info.This();
905
906 // Get js view name by this.constructor.name
907 JSRef<JSObject> constructor = thisObj->GetProperty("constructor");
908 JSRef<JSVal> jsViewName = constructor->GetProperty("name");
909 auto viewName = jsViewName->ToString();
910 auto* instance = new JSViewPartialUpdate(thisObj);
911
912 auto context = info.GetExecutionContext();
913 instance->SetContext(context);
914 instance->SetJSViewName(viewName);
915
916 // The JS object owns the C++ object:
917 // make sure the C++ is not destroyed when RefPtr thisObj goes out of scope
918 // JSView::DestructorCallback has view->DecRefCount()
919 instance->IncRefCount();
920
921 info.SetReturnValue(instance);
922 }
923
DestructorCallback(JSViewPartialUpdate * view)924 void JSViewPartialUpdate::DestructorCallback(JSViewPartialUpdate* view)
925 {
926 if (view == nullptr) {
927 LOGE("JSViewPartialUpdate::DestructorCallback failed: the view is nullptr");
928 return;
929 }
930 LOGD("JSViewPartialUpdate(DestructorCallback) start");
931 view->DecRefCount();
932 LOGD("JSViewPartialUpdate(DestructorCallback) end");
933 }
934
935 // ===========================================================
936 // partial update own functions start below
937 // ===========================================================
938
JsFinishUpdateFunc(int32_t elmtId)939 void JSViewPartialUpdate::JsFinishUpdateFunc(int32_t elmtId)
940 {
941 ViewPartialUpdateModel::GetInstance()->FinishUpdate(
942 viewNode_, elmtId, [weak = AceType::WeakClaim(this)](const UpdateTask& task) {
943 auto jsView = weak.Upgrade();
944 if (jsView) {
945 jsView->pendingUpdateTasks_.push_back(task);
946 }
947 });
948 }
949
JsElementIdExists(int32_t elmtId)950 bool JSViewPartialUpdate::JsElementIdExists(int32_t elmtId)
951 {
952 return ElementRegister::GetInstance()->Exists(elmtId);
953 }
954
JSGetProxiedItemRenderState(const JSCallbackInfo & info)955 void JSViewPartialUpdate::JSGetProxiedItemRenderState(const JSCallbackInfo& info)
956 {
957 if (info.Length() != 1) {
958 LOGE("JSView::JSGetProxiedItemRenderState. elmtId parameter expected");
959 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
960 return;
961 }
962 const auto elmtId = info[0]->ToNumber<int32_t>();
963
964 if (elmtId == ElementRegister::UndefinedElementId) {
965 LOGE("JSView::JSGetProxiedItemRenderState. elmtId must not be undefined");
966 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
967 return;
968 }
969
970 // TODO: Check this return value
971 auto result = false;
972
973 // set boolean return value to JS
974 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(result)));
975 }
976
IsFirstRender(const JSCallbackInfo & info)977 void JSViewPartialUpdate::IsFirstRender(const JSCallbackInfo& info)
978 {
979 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(isFirstRender_)));
980 }
981
FindChildByIdForPreview(const JSCallbackInfo & info)982 void JSViewPartialUpdate::FindChildByIdForPreview(const JSCallbackInfo& info)
983 {
984 LOGD("JSViewPartialUpdate::FindChildByIdForPreview");
985 if (!info[0]->IsNumber()) {
986 LOGE("info[0] is not a number");
987 return;
988 }
989 std::string viewId = std::to_string(info[0]->ToNumber<int32_t>());
990 JSRef<JSObject> targetView = Framework::JSViewStackProcessor::GetViewById(viewId);
991 info.SetReturnValue(targetView);
992 return;
993 }
994
995 } // namespace OHOS::Ace::Framework
996