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