1 /*
2  * Copyright (c) 2024 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 #ifndef FRAMEWORKS_BRIDGE_CJ_FRONTEND_CPP_VIEW_NATIVE_VIEW_H
17 #define FRAMEWORKS_BRIDGE_CJ_FRONTEND_CPP_VIEW_NATIVE_VIEW_H
18 
19 #include <list>
20 #include <unordered_set>
21 
22 #include "base/utils/macros.h"
23 #include "bridge/cj_frontend/cppview/view_abstract.h"
24 #include "bridge/cj_frontend/interfaces/cj_ffi/cj_macro.h"
25 #include "core/components_ng/base/view_partial_update_model.h"
26 #include "ffi_remote_data.h"
27 
28 namespace OHOS::Ace::Framework {
29 
30 class NativeView;
31 
32 ACE_EXPORT bool LoadNativeView(const sptr<NativeView>& view);
33 
34 ACE_EXPORT std::string GetProcessViewId(int64_t id);
35 
36 /**
37  * delegate for cj view object
38  */
39 class RemoteView : public FFI::RemoteData {
40     CJ_REMOTE_CLASS(RemoteView)
41 
42 public:
43     void OnShow();
44     void OnHide();
45     bool OnBackPress();
46     void UpdateWithJson(const std::string&);
47     void OnAppear();
48     void OnTransition();
49     void OnAboutToRender();
50     void Render();
51     void Rerender();
52     void OnAfterRender();
53     void OnDisappear();
54     void OnAboutToBeDeleted();
55     void Reload(bool deep);
56 
57 private:
58     void VoidCallback(void (*cjFunc)(int64_t), const char* funcName);
59 };
60 
61 /**
62  * NativeView render process have 3 steps
63  *  1. create ComposedComponent, set build children callback
64  *  2. ComposedComponent create ComposedElement and PerformBuild
65  *  3. build children component by CallRenderFunction(set by step 1)
66  *
67  *  why can't build children component on step 1, because `InternalRender` is not reentrant.
68  *  When a NativeView contains a NativeView child, that will cause unexpected result.
69  *
70  *  how `InternalRender` can't reentrant, see `ViewStackProcessor::GetInstance()->Finish();`,
71  *  ViewStackProcessor has a global component tree, and `Finish()` will wrap the whole tree to a Component,
72  *  meanwhile clear the tree. when build a tree in the middle of another tree building process,
73  *  when subtree finish building by `ViewStackProcessor::GetInstance()->Finish();`, the subtree will be wrap at
74  *  wrong start, and the previous tree will lose some nodes.
75  */
76 class ACE_EXPORT NativeView : public ViewAbstract {
77     DECL_TYPE(NativeView, ViewAbstract)
78     using UpdateFuncResult = std::tuple<int32_t, RefPtr<Component>, RefPtr<Component>>;
79 
80 public:
81     explicit NativeView(sptr<RemoteView> cjView);
82     ~NativeView() override;
83 
84     void Destroy();
85 
86     RefPtr<AceType> CreateUI();
87     RefPtr<AceType> InitialUIRender();
88 
89     void SyncInstanceId();
90     void RestoreInstanceId();
91     void MarkNeedUpdate();
92     void FlushReload();
NeedsUpdate()93     bool NeedsUpdate() const
94     {
95         return needsUpdate_;
96     }
IsFullUpdate()97     bool IsFullUpdate() const { return !useNewPipeline_; }
98 
SetRenderDoneCallback(std::function<void ()> callback)99     void SetRenderDoneCallback(std::function<void()> callback)
100     {
101         onRenderDone_ = std::move(callback);
102     }
103 
104     /**
105      * Views which do not have a state can mark static.
106      * The element will be reused and re-render will be skipped.
107      */
MarkStatic()108     void MarkStatic()
109     {
110         isStatic_ = true;
111     }
IsStatic()112     bool IsStatic() const
113     {
114         return isStatic_;
115     }
116 
IsFirstRender()117     bool IsFirstRender() const
118     {
119         return isFirstRender_;
120     }
121 
IsUseNewPipeline()122     bool IsUseNewPipeline() const { return useNewPipeline_; }
123 
124     /**
125      * During render function execution, the child customview with same id will
126      * be recycled if they exist already in our child map. The ones which are not
127      * recycled needs to be cleaned. So After render function execution, clean the
128      * abandoned child customview.
129      */
130     void CleanUpAbandonedChild();
131 
132     void FireOnShow();
133     void FireOnHide();
134     bool FireOnBackPress();
135     void FireOnTransition();
136     void ExecuteUpdateWithValueParams(const std::string& jsonData);
137 
138     static void Create(const sptr<NativeView>& view);
139 
140     /**
141      * Last step of executing an partial update function
142      * get the result component from ViewStackProcessor
143      * add it to the queue to [elmtId, Component] to
144      * execute an local update on in the UI thread
145      * parameters
146      * elmtId of the Component/Element that's updated
147      * removedElementId : Array<number>  ids of Elements that were removed while updating
148      * caused by if condition toggle or ForEach array deleted / replaced items
149      * return boolean - true means success
150      */
151     void FinishUpdateFunc(int32_t elmtId);
152 
153     // The process of Component to Element sync leads to Elements being
154     // deleted. ElementRegister keeps track of these deletions
155     // before the framework can forget about these elmtIds
156     // these need to be removed from its own book keeping
157     // state variables keep track of dependent elmtIds and
158     // View objects keep a map elmtId -> update function,
159     // both on TS side.
160     // View.purgeDeletedElmtIds cleans both state variables
161     // and update function map from deleted ElmtIds
162     // afterwards it informs the ElementRegister that elmtIds
163     // it was able to purge.
164     // only then ElementRegister can forget about these elmtIds
165     void GetDeletedElemtIds(std::vector<int64_t>& vec);
166 
167     // JS signature: View.deletedElmtIdsHaveBeenPurged(elmtIds : number[])
168     // inform ElementRegister that given deleted eltIds
169     // have been deleted from partial updates book keeping
170     // at this point ElementRegister can forget about the,
171     void DeletedElmtIdsHaveBeenPurged(std::vector<int64_t>& vec);
172 
GetInstanceId()173     int32_t GetInstanceId() const
174     {
175         return instanceId_;
176     }
177 
GetViewNode()178     RefPtr<AceType> GetViewNode() const
179     {
180         return node_.Upgrade();
181     }
182 
183 private:
184     /**
185      * cjView is the delegate for cj_custom_view object, it should be assigned once NativeView is created
186      * and release after NativeView is released. It's possible to reuse a NativeView to bind several cj_custom_view
187      * objects, but it should not, just keep things simple. One NativeView bind one cj_custom_view, never bind
188      * a second object, keep it that way.
189      */
190     sptr<RemoteView> cjView_;
191 
192     // view id for custom view itself
193     std::string viewId_;
194 
195     int32_t instanceId_ = -1;
196     int32_t restoreInstanceId_ = -1;
197 
198     bool needsUpdate_ = false;
199     bool isStatic_ = false;
200 
201     std::function<void()> onRenderDone_ = nullptr;
202     bool useNewPipeline_ = false;
203     bool isFirstRender_ = true;
204     WeakPtr<AceType> node_ = nullptr;
205 
206     /* list of update function result is a triple (tuple with three entries)
207     <0> elmtId
208     <1> outmost wrapping Component
209     <2> main Component
210     */
211     std::list<UpdateTask> pendingUpdateTasks_;
212 };
213 
214 } // namespace OHOS::Ace::Framework
215 #endif // FRAMEWORKS_BRIDGE_CJ_FRONTEND_CPP_VIEW_NATIVE_VIEW_H
216