/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/declarative_frontend/jsview/js_view.h" #include "uicast_interface/uicast_context_impl.h" #include "uicast_interface/uicast_impl.h" #include "base/log/ace_trace.h" #include "base/memory/ace_type.h" #include "base/memory/referenced.h" #include "base/utils/utils.h" #include "core/common/container.h" #include "core/components_ng/base/view_full_update_model.h" #include "core/components_ng/base/view_full_update_model_ng.h" #include "core/components_ng/base/view_partial_update_model.h" #include "core/components_ng/base/view_partial_update_model_ng.h" #include "core/components_ng/base/view_stack_model.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/pipeline/base/element_register.h" #include "frameworks/bridge/declarative_frontend/engine/js_execution_scope_defines.h" #include "frameworks/bridge/declarative_frontend/jsview/js_view_stack_processor.h" #include "frameworks/bridge/declarative_frontend/jsview/models/view_full_update_model_impl.h" #include "frameworks/bridge/declarative_frontend/jsview/models/view_partial_update_model_impl.h" namespace OHOS::Ace { std::unique_ptr ViewFullUpdateModel::instance_ = nullptr; std::unique_ptr ViewPartialUpdateModel::instance_ = nullptr; ViewFullUpdateModel* ViewFullUpdateModel::GetInstance() { if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::ViewFullUpdateModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::ViewFullUpdateModelNG()); } else { instance_.reset(new Framework::ViewFullUpdateModelImpl()); } #endif } return instance_.get(); } ViewPartialUpdateModel* ViewPartialUpdateModel::GetInstance() { if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::ViewPartialUpdateModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::ViewPartialUpdateModelNG()); } else { instance_.reset(new Framework::ViewPartialUpdateModelImpl()); } #endif } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { void JSView::MarkStatic() { isStatic_ = true; { UICastImpl::CacheCmd("UICast::View::markStatic", std::to_string(uniqueId_)); } } bool JSView::NeedsUpdate() { { if (UICastContextImpl::NeedsRebuild()) { isStatic_ = false; return true; } } return needsUpdate_; } void JSView::JSBind(BindingTarget object) { JSViewPartialUpdate::JSBind(object); JSViewFullUpdate::JSBind(object); } void JSView::RenderJSExecution() { JAVASCRIPT_EXECUTION_SCOPE_STATIC; if (!jsViewFunction_) { LOGE("JSView: InternalRender jsViewFunction_ error"); return; } { { UICastImpl::CacheCmd("UICast::View::locate", std::to_string(uniqueId_)); } ACE_SCORING_EVENT("Component.AboutToRender"); jsViewFunction_->ExecuteAboutToRender(); } { ACE_SCORING_EVENT("Component.Build"); ViewStackModel::GetInstance()->PushKey(viewId_); jsViewFunction_->ExecuteRender(); ViewStackModel::GetInstance()->PopKey(); } { ACE_SCORING_EVENT("Component.OnRenderDone"); jsViewFunction_->ExecuteOnRenderDone(); { UICastImpl::SendCmd(); } if (notifyRenderDone_) { notifyRenderDone_(); } } } void JSView::SyncInstanceId() { restoreInstanceId_ = Container::CurrentId(); ContainerScope::UpdateCurrent(instanceId_); } void JSView::RestoreInstanceId() { ContainerScope::UpdateCurrent(restoreInstanceId_); } void JSView::JsSetCardId(int64_t cardId) { cardId_ = cardId; } void JSView::JsGetCardId(const JSCallbackInfo& info) { info.SetReturnValue(JSRef::Make(ToJSValue(cardId_))); } JSViewFullUpdate::JSViewFullUpdate(const std::string& viewId, JSRef jsObject, JSRef jsRenderFunction) { viewId_ = viewId; jsViewFunction_ = AceType::MakeRefPtr(jsObject, jsRenderFunction); jsViewObject_ = jsObject; LOGD("JSViewFullUpdate constructor"); } JSViewFullUpdate::~JSViewFullUpdate() { LOGD("JSViewFullUpdate destructor"); jsViewFunction_.Reset(); }; RefPtr JSViewFullUpdate::CreateViewNode() { auto appearFunc = [weak = AceType::WeakClaim(this)] { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Appear"); if (jsView->viewNode_.Invalid() && jsView->jsViewFunction_) { jsView->jsViewFunction_->ExecuteAppear(); } }; auto renderFunction = [weak = AceType::WeakClaim(this)]() -> RefPtr { auto jsView = weak.Upgrade(); return jsView ? jsView->InternalRender() : nullptr; }; auto pageTransitionFunction = [weak = AceType::WeakClaim(this)]() { auto jsView = weak.Upgrade(); if (!jsView || !jsView->jsViewFunction_) { return; } { ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Transition"); jsView->jsViewFunction_->ExecuteTransition(); } }; auto updateViewNodeFunction = [weak = AceType::WeakClaim(this)](const RefPtr& node) { auto jsView = weak.Upgrade(); if (jsView) { jsView->viewNode_ = node; } }; auto removeFunction = [weak = AceType::WeakClaim(this)]() -> void { auto jsView = weak.Upgrade(); if (jsView && jsView->jsViewFunction_) { jsView->jsViewFunction_->ExecuteDisappear(); } }; NodeInfo info = { .viewId = viewId_, .appearFunc = std::move(appearFunc), .renderFunc = std::move(renderFunction), .removeFunc = std::move(removeFunction), .updateNodeFunc = std::move(updateViewNodeFunction), .isStatic = IsStatic() }; if (jsViewFunction_ && jsViewFunction_->HasPageTransition()) { info.pageTransitionFunc = std::move(pageTransitionFunction); } return ViewFullUpdateModel::GetInstance()->CreateNode(std::move(info)); } RefPtr JSViewFullUpdate::InternalRender() { JAVASCRIPT_EXECUTION_SCOPE_STATIC; needsUpdate_ = false; RenderJSExecution(); CleanUpAbandonedChild(); jsViewFunction_->Destroy(); return ViewStackModel::GetInstance()->Finish(); } /** * marks the JSView's composed component as needing update / rerender */ void JSViewFullUpdate::MarkNeedUpdate() { ACE_SCOPED_TRACE("JSView::MarkNeedUpdate"); needsUpdate_ = ViewFullUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_); } void JSViewFullUpdate::Destroy(JSView* parentCustomView) { LOGD("JSViewFullUpdate::Destroy start"); DestroyChild(parentCustomView); { ACE_SCORING_EVENT("Component[" + viewId_ + "].Disappear"); jsViewFunction_->ExecuteDisappear(); } { ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToBeDeleted"); jsViewFunction_->ExecuteAboutToBeDeleted(); } jsViewObject_.Reset(); LOGD("JSViewFullUpdate::Destroy end"); } void JSViewFullUpdate::Create(const JSCallbackInfo& info) { LOGD("Creating new View for full update"); ACE_DCHECK(!Container::IsCurrentUsePartialUpdate()); if (info[0]->IsObject()) { JSRef object = JSRef::Cast(info[0]); auto* view = object->Unwrap(); if (view == nullptr) { LOGE("JSView is null"); return; } ViewStackModel::GetInstance()->Push(view->CreateViewNode(), true); { UICastImpl::ViewCreate(view->UICastGetViewId(), view->uniqueId_, view); } } else { LOGE("JSView Object is expected."); } } void JSViewFullUpdate::JSBind(BindingTarget object) { LOGD("JSViewFullUpdate::Bind"); JSClass::Declare("NativeViewFullUpdate"); JSClass::StaticMethod("create", &JSViewFullUpdate::Create); JSClass::Method("markNeedUpdate", &JSViewFullUpdate::MarkNeedUpdate); JSClass::Method("syncInstanceId", &JSViewFullUpdate::SyncInstanceId); JSClass::Method("restoreInstanceId", &JSViewFullUpdate::RestoreInstanceId); JSClass::Method("needsUpdate", &JSViewFullUpdate::NeedsUpdate); JSClass::Method("markStatic", &JSViewFullUpdate::MarkStatic); JSClass::Method("setCardId", &JSViewFullUpdate::JsSetCardId); JSClass::CustomMethod("getCardId", &JSViewFullUpdate::JsGetCardId); JSClass::CustomMethod("findChildById", &JSViewFullUpdate::FindChildById); JSClass::CustomMethod("findChildByIdForPreview", &JSViewFullUpdate::FindChildByIdForPreview); JSClass::Inherit(); JSClass::Bind(object, ConstructorCallback, DestructorCallback); } void JSViewFullUpdate::FindChildById(const JSCallbackInfo& info) { LOGD("JSView::FindChildById"); if (info[0]->IsNumber() || info[0]->IsString()) { std::string viewId = info[0]->ToString(); info.SetReturnValue(GetChildById(viewId)); } else { LOGE("JSView FindChildById with invalid arguments."); JSException::Throw("%s", "JSView FindChildById with invalid arguments."); } } void JSViewFullUpdate::FindChildByIdForPreview(const JSCallbackInfo& info) { std::string viewId = info[0]->ToString(); if (viewId_ == viewId) { info.SetReturnValue(jsViewObject_); return; } JSRef targetView = JSRef::New(); for (auto&& child : customViewChildren_) { if (GetChildByViewId(viewId, child.second, targetView)) { break; } } auto view = targetView->Unwrap(); if (view) { LOGD("find targetView success"); info.SetReturnValue(targetView); } return; } bool JSViewFullUpdate::GetChildByViewId( const std::string& viewId, JSRef& childView, JSRef& targetView) { auto* view = childView->Unwrap(); if (view && view->viewId_ == viewId) { targetView = childView; return true; } for (auto&& child : view->customViewChildren_) { if (GetChildByViewId(viewId, child.second, targetView)) { return true; } } return false; } void JSViewFullUpdate::ConstructorCallback(const JSCallbackInfo& info) { JSRef thisObj = info.This(); JSRef renderFunc = thisObj->GetProperty("render"); if (!renderFunc->IsFunction()) { LOGE("View derived classes must provide render(){...} function"); JSException::Throw("%s", "View derived classes must provide render(){...} function"); return; } int argc = info.Length(); if (argc > 1 && (info[0]->IsNumber() || info[0]->IsString())) { std::string viewId = info[0]->ToString(); auto instance = AceType::MakeRefPtr(viewId, info.This(), JSRef::Cast(renderFunc)); auto context = info.GetExecutionContext(); instance->SetContext(context); instance->IncRefCount(); info.SetReturnValue(AceType::RawPtr(instance)); std::string parentViewId = ""; int parentUniqueId = -1; if (!info[1]->IsUndefined() && info[1]->IsObject()) { JSRef parentObj = JSRef::Cast(info[1]); auto* parentView = parentObj->Unwrap(); if (parentView != nullptr) { auto id = parentView->AddChildById(viewId, info.This()); instance->id_ = id; parentViewId = parentView->viewId_; parentUniqueId = parentView->uniqueId_; } } LOGD("JSView ConstructorCallback: %{public}s", instance->id_.c_str()); { instance->uniqueId_ = UICastImpl::GetViewUniqueID(parentUniqueId); UICastImpl::ViewConstructor( instance->viewId_, instance->uniqueId_, parentViewId, parentUniqueId, AceType::RawPtr(instance)); } } else { LOGE("JSView creation with invalid arguments."); JSException::Throw("%s", "JSView creation with invalid arguments."); } } void JSViewFullUpdate::DestructorCallback(JSViewFullUpdate* view) { if (view == nullptr) { LOGE("JSViewFullUpdate::DestructorCallback failed: the view is nullptr"); return; } LOGD("JSViewFullUpdate(DestructorCallback) start: %{public}s", view->id_.c_str()); view->DecRefCount(); LOGD("JSViewFullUpdate(DestructorCallback) end"); } void JSViewFullUpdate::DestroyChild(JSView* parentCustomView) { LOGD("JSViewFullUpdate::DestroyChild start"); for (auto&& child : customViewChildren_) { auto* view = child.second->Unwrap(); if (view != nullptr) { view->Destroy(this); } child.second.Reset(); } customViewChildren_.clear(); for (auto&& lazyChild : customViewChildrenWithLazy_) { auto* view = lazyChild.second->Unwrap(); if (view != nullptr) { view->Destroy(this); } lazyChild.second.Reset(); } customViewChildrenWithLazy_.clear(); LOGD("JSViewFullUpdate::DestroyChild end"); } void JSViewFullUpdate::CleanUpAbandonedChild() { auto startIter = customViewChildren_.begin(); auto endIter = customViewChildren_.end(); std::vector removedViewIds; while (startIter != endIter) { auto found = lastAccessedViewIds_.find(startIter->first); if (found == lastAccessedViewIds_.end()) { LOGD(" found abandoned view with id %{public}s", startIter->first.c_str()); removedViewIds.emplace_back(startIter->first); auto* view = startIter->second->Unwrap(); if (view != nullptr) { view->Destroy(this); } startIter->second.Reset(); } ++startIter; } for (auto& viewId : removedViewIds) { customViewChildren_.erase(viewId); } lastAccessedViewIds_.clear(); } JSRef JSViewFullUpdate::GetChildById(const std::string& viewId) { std::string id = ViewStackModel::GetInstance()->ProcessViewId(viewId); auto found = customViewChildren_.find(id); if (found != customViewChildren_.end()) { ChildAccessedById(id); return found->second; } auto lazyItem = customViewChildrenWithLazy_.find(id); if (lazyItem != customViewChildrenWithLazy_.end()) { return lazyItem->second; } return {}; } std::string JSViewFullUpdate::AddChildById(const std::string& viewId, const JSRef& obj) { std::string id = ViewStackModel::GetInstance()->ProcessViewId(viewId); JSView* jsView = nullptr; if (isLazyForEachProcessed_) { auto result = customViewChildrenWithLazy_.try_emplace(id, obj); if (!result.second) { jsView = result.first->second->Unwrap(); result.first->second = obj; } else { lazyItemGroups_[lazyItemGroupId_].emplace_back(id); } } else { auto result = customViewChildren_.try_emplace(id, obj); if (!result.second) { jsView = result.first->second->Unwrap(); result.first->second = obj; } ChildAccessedById(id); } if (jsView != nullptr) { jsView->Destroy(this); } return id; } void JSViewFullUpdate::RemoveChildGroupById(const std::string& viewId) { // js runtime may be released CHECK_JAVASCRIPT_SCOPE_AND_RETURN; JAVASCRIPT_EXECUTION_SCOPE_STATIC; LOGD("JSViewFullUpdate::RemoveChildGroupById in lazy for each case: %{public}s", viewId.c_str()); auto iter = lazyItemGroups_.find(viewId); if (iter == lazyItemGroups_.end()) { LOGI("can not find this group to delete: %{public}s", viewId.c_str()); return; } std::vector removedViewIds; for (auto&& item : iter->second) { auto removeView = customViewChildrenWithLazy_.find(item); if (removeView != customViewChildrenWithLazy_.end()) { if (!removeView->second.IsEmpty()) { auto* view = removeView->second->Unwrap(); if (view != nullptr) { view->Destroy(this); } removeView->second.Reset(); } removedViewIds.emplace_back(item); } } for (auto&& removeId : removedViewIds) { customViewChildrenWithLazy_.erase(removeId); } lazyItemGroups_.erase(iter); } void JSViewFullUpdate::ChildAccessedById(const std::string& viewId) { lastAccessedViewIds_.emplace(viewId); } // ================================================================= std::map> JSViewStackProcessor::viewMap_; JSViewPartialUpdate::JSViewPartialUpdate(JSRef jsViewObject) { jsViewFunction_ = AceType::MakeRefPtr(jsViewObject); LOGD("JSViewPartialUpdate constructor"); // keep the reference to the JS View object to prevent GC jsViewObject_ = jsViewObject; } JSViewPartialUpdate::~JSViewPartialUpdate() { LOGD("JSViewPartialUpdate destructor"); jsViewFunction_.Reset(); }; RefPtr JSViewPartialUpdate::CreateViewNode() { auto updateViewIdFunc = [weak = AceType::WeakClaim(this)](const std::string viewId) { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); jsView->viewId_ = viewId; }; auto appearFunc = [weak = AceType::WeakClaim(this)]() { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Appear"); if (jsView->jsViewFunction_) { jsView->jsViewFunction_->ExecuteAppear(); } }; auto renderFunction = [weak = AceType::WeakClaim(this)]() -> RefPtr { auto jsView = weak.Upgrade(); CHECK_NULL_RETURN(jsView, nullptr); if (!jsView->isFirstRender_) { LOGW("the js view has already called initial render"); return nullptr; } jsView->isFirstRender_ = false; return jsView->InitialRender(); }; auto updateFunction = [weak = AceType::WeakClaim(this)]() -> void { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); if (!jsView->needsUpdate_) { LOGW("the js view does not need to update"); return; } jsView->needsUpdate_ = false; LOGD("Rerender function start for ComposedElement elmtId %{public}s - start...", jsView->viewId_.c_str()); { ACE_SCOPED_TRACE("JSView: ExecuteRerender"); jsView->jsViewFunction_->ExecuteRerender(); } for (const UpdateTask& updateTask : jsView->pendingUpdateTasks_) { ViewPartialUpdateModel::GetInstance()->FlushUpdateTask(updateTask); } jsView->pendingUpdateTasks_.clear(); }; auto reloadFunction = [weak = AceType::WeakClaim(this)](bool deep) { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); CHECK_NULL_VOID(jsView->jsViewFunction_); jsView->jsViewFunction_->ExecuteReload(deep); }; // @Component level complete reload, can detect added/deleted frame nodes auto completeReloadFunc = [weak = AceType::WeakClaim(this)]() -> RefPtr { auto jsView = weak.Upgrade(); CHECK_NULL_RETURN(jsView, nullptr); return jsView->InitialRender(); }; auto pageTransitionFunction = [weak = AceType::WeakClaim(this)]() { auto jsView = weak.Upgrade(); if (!jsView || !jsView->jsViewFunction_) { return; } { ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Transition"); jsView->jsViewFunction_->ExecuteTransition(); } }; auto removeFunction = [weak = AceType::WeakClaim(this)]() -> void { LOGD("call remove view function"); auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); jsView->Destroy(nullptr); jsView->viewNode_.Reset(); }; auto updateViewNodeFunction = [weak = AceType::WeakClaim(this)](const RefPtr& node) { auto jsView = weak.Upgrade(); if (jsView) { jsView->viewNode_ = node; } }; auto nodeUpdateFunc = [weak = AceType::WeakClaim(this)](int32_t nodeId) { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); CHECK_NULL_VOID(jsView->jsViewFunction_); jsView->jsViewFunction_->ExecuteForceNodeRerender(nodeId); }; NodeInfoPU info = { .appearFunc = std::move(appearFunc), .renderFunc = std::move(renderFunction), .updateFunc = std::move(updateFunction), .removeFunc = std::move(removeFunction), .updateNodeFunc = std::move(updateViewNodeFunction), .pageTransitionFunc = std::move(pageTransitionFunction), .reloadFunc = std::move(reloadFunction), .completeReloadFunc = std::move(completeReloadFunc), .nodeUpdateFunc = std::move(nodeUpdateFunc), .hasMeasureOrLayout = jsViewFunction_->HasMeasure() || jsViewFunction_->HasLayout(), .isStatic = IsStatic() }; auto measureFunc = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); jsView->jsViewFunction_->ExecuteMeasure(layoutWrapper); }; if (jsViewFunction_->HasMeasure()) { info.measureFunc = std::move(measureFunc); } auto layoutFunc = [weak = AceType::WeakClaim(this)](NG::LayoutWrapper* layoutWrapper) -> void { auto jsView = weak.Upgrade(); CHECK_NULL_VOID(jsView); jsView->jsViewFunction_->ExecuteLayout(layoutWrapper); }; if (jsViewFunction_->HasLayout()) { info.layoutFunc = std::move(layoutFunc); } auto node = ViewPartialUpdateModel::GetInstance()->CreateNode(std::move(info)); #ifdef PREVIEW auto uiNode = AceType::DynamicCast(node); if (uiNode) { Framework::JSViewStackProcessor::SetViewMap(std::to_string(uiNode->GetId()), jsViewObject_); } #endif return node; } RefPtr JSViewPartialUpdate::InitialRender() { needsUpdate_ = false; RenderJSExecution(); return ViewStackModel::GetInstance()->Finish(); } // parentCustomView in not used by PartialUpdate void JSViewPartialUpdate::Destroy(JSView* parentCustomView) { if (jsViewFunction_ == nullptr) { // already called Destroy before return; } LOGD("JSViewPartialUpdate::Destroy start"); { ACE_SCORING_EVENT("Component[" + viewId_ + "].Disappear"); jsViewFunction_->ExecuteDisappear(); } { ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToBeDeleted"); jsViewFunction_->ExecuteAboutToBeDeleted(); } pendingUpdateTasks_.clear(); jsViewFunction_->Destroy(); jsViewFunction_.Reset(); // release reference to JS view object, and allow GC, calls DestructorCallback jsViewObject_.Reset(); LOGD("JSViewPartialUpdate::Destroy end"); } void JSViewPartialUpdate::MarkNeedUpdate() { needsUpdate_ = ViewPartialUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_); } /** * in JS View.create(new View(...)); * used for FullRender case, not for re-render case */ void JSViewPartialUpdate::Create(const JSCallbackInfo& info) { LOGD("Creating new JSViewPartialUpdate for partial update"); ACE_DCHECK(Container::IsCurrentUsePartialUpdate()); if (info[0]->IsObject()) { JSRef object = JSRef::Cast(info[0]); auto* view = object->Unwrap(); if (view == nullptr) { LOGE("View is null"); return; } ViewStackModel::GetInstance()->Push(view->CreateViewNode(), true); } else { LOGE("View Object is expected."); } } void JSViewPartialUpdate::JSBind(BindingTarget object) { LOGD("JSViewPartialUpdate::Bind"); JSClass::Declare("NativeViewPartialUpdate"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSViewPartialUpdate::Create, opt); JSClass::Method("markNeedUpdate", &JSViewPartialUpdate::MarkNeedUpdate); JSClass::Method("syncInstanceId", &JSViewPartialUpdate::SyncInstanceId); JSClass::Method("restoreInstanceId", &JSViewPartialUpdate::RestoreInstanceId); JSClass::Method("markStatic", &JSViewPartialUpdate::MarkStatic); JSClass::Method("finishUpdateFunc", &JSViewPartialUpdate::JsFinishUpdateFunc); JSClass::Method("setCardId", &JSViewPartialUpdate::JsSetCardId); JSClass::CustomMethod("getCardId", &JSViewPartialUpdate::JsGetCardId); JSClass::CustomMethod("getDeletedElemtIds", &JSViewPartialUpdate::JsGetDeletedElemtIds); JSClass::CustomMethod( "deletedElmtIdsHaveBeenPurged", &JSViewPartialUpdate::JsDeletedElmtIdsHaveBeenPurged); JSClass::Method("elmtIdExists", &JSViewPartialUpdate::JsElementIdExists); JSClass::CustomMethod("isLazyItemRender", &JSViewPartialUpdate::JSGetProxiedItemRenderState); JSClass::CustomMethod("isFirstRender", &JSViewPartialUpdate::IsFirstRender); JSClass::CustomMethod( "findChildByIdForPreview", &JSViewPartialUpdate::FindChildByIdForPreview); JSClass::Inherit(); JSClass::Bind(object, ConstructorCallback, DestructorCallback); } void JSViewPartialUpdate::ConstructorCallback(const JSCallbackInfo& info) { LOGD("creating C++ and JS View Objects ..."); JSRef thisObj = info.This(); auto* instance = new JSViewPartialUpdate(thisObj); auto context = info.GetExecutionContext(); instance->SetContext(context); // The JS object owns the C++ object: // make sure the C++ is not destroyed when RefPtr thisObj goes out of scope // JSView::DestructorCallback has view->DecRefCount() instance->IncRefCount(); info.SetReturnValue(instance); } void JSViewPartialUpdate::DestructorCallback(JSViewPartialUpdate* view) { if (view == nullptr) { LOGE("JSViewPartialUpdate::DestructorCallback failed: the view is nullptr"); return; } LOGD("JSViewPartialUpdate(DestructorCallback) start"); view->DecRefCount(); LOGD("JSViewPartialUpdate(DestructorCallback) end"); } // =========================================================== // partial update own functions start below // =========================================================== void JSViewPartialUpdate::JsFinishUpdateFunc(int32_t elmtId) { ViewPartialUpdateModel::GetInstance()->FinishUpdate( viewNode_, elmtId, [weak = AceType::WeakClaim(this)](const UpdateTask& task) { auto jsView = weak.Upgrade(); if (jsView) { jsView->pendingUpdateTasks_.push_back(task); } }); } void JSViewPartialUpdate::JsGetDeletedElemtIds(const JSCallbackInfo& info) { LOGD("JSView, getting elmtIds of all deleted Elements from ElementRegister:"); JSRef jsArr = JSRef::Cast(info[0]); std::unordered_set& removedElements = ElementRegister::GetInstance()->GetRemovedItems(); size_t index = jsArr->Length(); for (const auto& rmElmtId : removedElements) { LOGD(" array removed elmtId %{public}d", rmElmtId); JSRef jsRmElmtId = JSRef::Make(ToJSValue(static_cast(rmElmtId))); jsArr->SetValueAt(index++, jsRmElmtId); } } void JSViewPartialUpdate::JsDeletedElmtIdsHaveBeenPurged(const JSCallbackInfo& info) { JSRef jsArr = JSRef::Cast(info[0]); for (size_t i = 0; i < jsArr->Length(); i++) { const JSRef strId = jsArr->GetValueAt(i); ElementRegister::GetInstance()->ClearRemovedItems(strId->ToNumber()); } } bool JSViewPartialUpdate::JsElementIdExists(int32_t elmtId) { return ElementRegister::GetInstance()->Exists(elmtId); } void JSViewPartialUpdate::JSGetProxiedItemRenderState(const JSCallbackInfo& info) { if (info.Length() != 1) { LOGE("JSView::JSGetProxiedItemRenderState. elmtId parameter expected"); info.SetReturnValue(JSRef::Make(ToJSValue(false))); return; } const auto elmtId = info[0]->ToNumber(); if (elmtId == ElementRegister::UndefinedElementId) { LOGE("JSView::JSGetProxiedItemRenderState. elmtId must not be undefined"); info.SetReturnValue(JSRef::Make(ToJSValue(false))); return; } // TODO: Check this return value auto result = false; // set boolean return value to JS info.SetReturnValue(JSRef::Make(ToJSValue(result))); } void JSViewPartialUpdate::IsFirstRender(const JSCallbackInfo& info) { info.SetReturnValue(JSRef::Make(ToJSValue(isFirstRender_))); } void JSViewPartialUpdate::FindChildByIdForPreview(const JSCallbackInfo& info) { LOGD("JSViewPartialUpdate::FindChildByIdForPreview"); std::string viewId = info[0]->ToString(); JSRef targetView = Framework::JSViewStackProcessor::GetViewById(viewId); info.SetReturnValue(targetView); return; } } // namespace OHOS::Ace::Framework