1/* 2 * Copyright (c) 2023 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 17/* UINodeRegisterProxy singeleton class 18 19Acts on behave on ElementRegister to hold elmtIds of deleted UINodes 20until these get unregistered from a ViewPU. 21 22Two flows inside the framework that involve UINodeRegisterProxy: 23 24Flow A: 251. UINode Destructor calls ElementRegister.RemoveItem(UINode's elmtId) - processing stops: 26 272. Some time later on next re-render of a ViewPU: 283. ViewPU.purgeDeletedElmtIds calls C+++ UINodeRegisterProxy.obtainElementIds(), 294. UINodeRegisterProxy.obtainElementIds() calls C++ ElementRegister::MoveRemovedItems to move elmtIds of deleted UINodes UINodeRegisterProxy 30 (those added by RemoveItems(elmtId) in step 1). 315. UINodeRegisterProxy.unregisterElmtIdsFromIViews: unregister the removedElementIds from the all the viewpus: 32 1) purge the update function in viewpu 33 2) purge the dependent element if from statevariable 34 35Flow B: 361. CustomNode destructor calls deleteFunction calls ViewPU.aboutToBeDeleted 37 note that CustomNode destructor may get called before child UINodes' destructor. 38 1) unregister its own element ids 39 2) call UINodeRegisterProxy.obtainElementIds() to get the removed elementIds from cpp and unregister from all the viewpus. 40 it may make the first viewpu aboutToBeDeleted execution time longer than before, but for rest viewpu may be faster. 41 It's function equals with purgeDeletedElmtIdsRecursively, so it is not necessary to execute purgeDeletedElmtIds for all its child recursively. 42 432. some time later or when idle: PipelineContext::OnIdle will CallJSCleanUpIdleTaskFunc to do the clean up for the removed elements which have not unregisted 44 from the stateMgmt side. 45*/ 46 47type RemovedElementInfo = { elmtId : number, tag : string }; 48// defined a globle function to clean up the removeItems when idle 49function uiNodeCleanUpIdleTask(): void { 50 stateMgmtConsole.debug(`UINodeRegisterProxy. static uiNodeCleanUpIdleTask:`); 51 UINodeRegisterProxy.obtainDeletedElmtIds(); 52 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 53} 54 55class UINodeRegisterProxy { 56 public static readonly notRecordingDependencies : number = -1; 57 public static readonly monitorIllegalV2V3StateAccess : number = -2; 58 59 public static obtainDeletedElmtIds(): void { 60 stateMgmtConsole.debug(`UINodeRegisterProxy. static obtainDeletedElmtIds:`); 61 if ((!UINodeRegisterProxy.instance_.obtainDeletedElmtIds) || typeof UINodeRegisterProxy.instance_.obtainDeletedElmtIds !== 'function') { 62 stateMgmtConsole.error(`UINodeRegisterProxy obtainDeletedElmtIds is not a function: ${UINodeRegisterProxy.instance_.obtainDeletedElmtIds}.` ); 63 } else { 64 UINodeRegisterProxy.instance_.obtainDeletedElmtIds(); 65 } 66 } 67 68 // FIXME unregisterElmtIdsFromIViews needs adaptation 69 public static unregisterElmtIdsFromIViews(): void { 70 stateMgmtConsole.debug('UINodeRegisterProxy.unregisterElmtIdsFromIViews elmtIds'); 71 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 72 } 73 74 // unregisters all the received removedElements in func parameter 75 public static unregisterRemovedElmtsFromViewPUs(removedElements: Array<number>): void { 76 stateMgmtConsole.debug(`UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs elmtIds ${removedElements}`); 77 UINodeRegisterProxy.instance_.populateRemoveElementInfo(removedElements); 78 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 79 } 80 81 public static registerModifierElmtDeleteCallback(callback): void { 82 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 83 return; 84 } 85 UINodeRegisterProxy.modifierElmtDeleteCallback_ = callback; 86 } 87 88 private populateRemoveElementInfo(removedElements: Array<number>) { 89 for (const elmtId of removedElements) { 90 this.removeElementsInfo_.push(elmtId); 91 } 92 } 93 94 /* just get the remove items from the native side 95 */ 96 private obtainDeletedElmtIds(): void { 97 stateMgmtConsole.debug('UINodeRegisterProxy.obtainDeletedElmtIds: '); 98 let removedElementsInfo = new Array<number>(); 99 ViewStackProcessor.moveDeletedElmtIds(removedElementsInfo); 100 stateMgmtConsole.debug(` ... ${removedElementsInfo.length} elmtIds newly obtained from ElementRegister: ${JSON.stringify(removedElementsInfo)} .`); 101 this.removeElementsInfo_ = removedElementsInfo; 102 } 103 104 public unregisterElmtIdsFromIViews(): void { 105 stateMgmtConsole.debug(`${this.removeElementsInfo_.length} elmtIds newly obtained from ElementRegister: ${JSON.stringify(this.removeElementsInfo_)} .`); 106 107 if (this.removeElementsInfo_.length === 0) { 108 stateMgmtConsole.debug(`${this.removeElementsInfo_.length} elmtIds needs to purgeDelete. } .`); 109 return; 110 } 111 let owningView : IView | undefined; 112 this.removeElementsInfo_.forEach((elmtId: number) => { 113 const owningViewPUWeak : WeakRef<IView> | undefined = UINodeRegisterProxy.ElementIdToOwningViewPU_.get(elmtId); 114 if (owningViewPUWeak !== undefined) { 115 owningView = owningViewPUWeak.deref(); 116 if (owningView) { 117 owningView.purgeDeleteElmtId(elmtId); 118 } else { 119 stateMgmtConsole.debug(`elmtIds ${elmtId} has not been removed because of failure of updating the weakptr of viewpu. Internal error!.`); 120 } 121 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 122 UINodeRegisterProxy.modifierElmtDeleteCallback_(elmtId); 123 } 124 } else { 125 stateMgmtConsole.debug(`elmtIds ${elmtId} cannot find its owning ViewPU, maybe this ViewPu has already been aboutToBeDeleted. Internal error!`); 126 } 127 128 // FIXME: only do this if app uses V3 129 ObserveV2.getObserve().clearBinding(elmtId); 130 }) 131 132 this.removeElementsInfo_.length = 0; 133 } 134 135 public static cleanUpDeadReferences(): void { 136 stateMgmtConsole.debug('UINodeRegisterProxy.cleanUpDeadReferences'); 137 ObserveV2.getObserve().cleanUpDeadReferences(); 138 } 139 140 public static instance_: UINodeRegisterProxy = new UINodeRegisterProxy(); 141 public removeElementsInfo_: Array<number> = new Array<number>(); 142 public static ElementIdToOwningViewPU_: Map<number, WeakRef<IView>> = new Map<number, WeakRef<IView>>(); 143 public static modifierElmtDeleteCallback_; 144} 145