1 /*
2  * Copyright (c) 2021-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 #ifndef FOUNDATION_ACE_FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_VIEW_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_VIEW_H
18 
19 #include <functional>
20 #include <list>
21 #include <string>
22 
23 #include "base/log/ace_scoring_log.h"
24 #include "base/memory/ace_type.h"
25 #include "base/memory/referenced.h"
26 #include "core/components_ng/base/view_partial_update_model.h"
27 #include "frameworks/bridge/declarative_frontend/engine/js_ref_ptr.h"
28 #include "frameworks/bridge/declarative_frontend/jsview/js_view_abstract.h"
29 #include "frameworks/bridge/declarative_frontend/jsview/js_view_functions.h"
30 
31 namespace OHOS::Ace::Framework {
32 
33 class JSView : public JSViewAbstract, public virtual AceType {
DECLARE_ACE_TYPE(JSView,AceType)34     DECLARE_ACE_TYPE(JSView, AceType)
35 
36 public:
37     JSView() : instanceId_(Container::CurrentId()) {}
38     ~JSView() override = default;
39     virtual void Destroy(JSView* parentCustomView) = 0;
40 
41     virtual RefPtr<AceType> CreateViewNode(bool isTitleNode = false)
42     {
43         LOGE("Internal error. Not implemented");
44         return nullptr;
45     }
46 
47     void SyncInstanceId();
48     void RestoreInstanceId();
49     void GetInstanceId(const JSCallbackInfo& info);
50 
FireOnShow()51     void FireOnShow()
52     {
53         if (jsViewFunction_) {
54             ACE_SCORING_EVENT("OnShow");
55             jsViewFunction_->ExecuteShow();
56         }
57     }
58 
FireOnHide()59     void FireOnHide()
60     {
61         if (jsViewFunction_) {
62             ACE_SCORING_EVENT("OnHide");
63             jsViewFunction_->ExecuteHide();
64         }
65     }
66 
FireOnBackPress()67     bool FireOnBackPress()
68     {
69         if (jsViewFunction_) {
70             ACE_SCORING_EVENT("OnBackPress");
71             return jsViewFunction_->ExecuteOnBackPress();
72         }
73         return false;
74     }
75 
FireOnFormRecycle()76     std::string FireOnFormRecycle()
77     {
78         if (jsViewFunction_) {
79             ACE_SCORING_EVENT("OnFormRecycle");
80             return jsViewFunction_->ExecuteOnFormRecycle();
81         }
82         LOGE("jsViewFunction_ is null");
83         return "";
84     }
85 
FireOnFormRecover(const std::string & statusData)86     void FireOnFormRecover(const std::string &statusData)
87     {
88         if (jsViewFunction_) {
89             ACE_SCORING_EVENT("OnFormRecover");
90             return jsViewFunction_->ExecuteOnFormRecover(statusData);
91         }
92         LOGE("jsViewFunction_ is null");
93     }
94 
95     void RenderJSExecution();
96 
97     virtual void MarkNeedUpdate() = 0;
98 
NeedsUpdate()99     bool NeedsUpdate()
100     {
101         return needsUpdate_;
102     }
103 
104     static void JSBind(BindingTarget globalObj);
105     /**
106      * Views which do not have a state can mark static.
107      * The element will be reused and re-render will be skipped.
108      */
MarkStatic()109     void MarkStatic()
110     {
111         isStatic_ = true;
112     }
113 
IsStatic()114     bool IsStatic()
115     {
116         return isStatic_;
117     }
118 
SetContext(const JSExecutionContext & context)119     void SetContext(const JSExecutionContext& context)
120     {
121         jsViewFunction_->SetContext(context);
122     }
123 
124     // Used to set/get card id C++
SetCardId(int64_t cardId)125     void SetCardId(int64_t cardId)
126     {
127         cardId_ = cardId;
128     }
129 
GetCardId()130     int64_t GetCardId() const
131     {
132         return cardId_;
133     }
134 
RegisterRenderDoneCallback(std::function<void ()> && OnRenderDone)135     void RegisterRenderDoneCallback(std::function<void()>&& OnRenderDone)
136     {
137         notifyRenderDone_ = std::move(OnRenderDone);
138     }
139 
140     // Used to set/get card id JS
141     void JsSetCardId(int64_t cardId);
142     void JsGetCardId(const JSCallbackInfo& info);
143 
144     // Used by full update variant only from js_lazy_foreach.cpp
RemoveChildGroupById(const std::string & viewId)145     virtual void RemoveChildGroupById(const std::string& viewId) {}
MarkLazyForEachProcess(const std::string & groudId)146     virtual void MarkLazyForEachProcess(const std::string& groudId) {}
ResetLazyForEachProcess()147     virtual void ResetLazyForEachProcess() {}
ExecuteUpdateWithValueParams(const std::string & jsonData)148     virtual void ExecuteUpdateWithValueParams(const std::string& jsonData) {}
ExecuteInitiallyProvidedValue(const std::string & jsonData)149     virtual void ExecuteInitiallyProvidedValue(const std::string& jsonData) {}
150 
isFullUpdate()151     virtual bool isFullUpdate() const
152     {
153         return true;
154     }
155 
GetInstanceId()156     int32_t GetInstanceId() const
157     {
158         return instanceId_;
159     }
160 
GetViewNode()161     RefPtr<AceType> GetViewNode() const
162     {
163         return viewNode_.Upgrade();
164     }
165 
OnDumpInfo(const std::vector<std::string> & params)166     virtual void OnDumpInfo(const std::vector<std::string>& params) {}
167 
168 protected:
169     RefPtr<ViewFunctions> jsViewFunction_;
170     bool needsUpdate_ = false;
171 
172     WeakPtr<AceType> viewNode_;
173     // view id for custom view itself
174     std::string viewId_;
175 
176     // card id for eTS Card
177     // set on the root JSView of the card and inherited by all child JSViews
178     // -1 means not part of a card
179     int64_t cardId_ = -1;
180 
181 private:
182     int32_t instanceId_ = -1;
183     std::vector<int32_t> restoreInstanceIdStack_;
184     static constexpr uint32_t PRIMARY_ID_STACK_SIZE = 32;
185     uint32_t primaryStackSize_ = 0;
186     // The primary id stack is protection for restoreInstanceIdStack_.
187     // This can avoid crashing when the pointer in vector is corrupted.
188     std::array<int32_t, PRIMARY_ID_STACK_SIZE> primaryIdStack_{};
189     bool isStatic_ = false;
190     std::function<void()> notifyRenderDone_;
191 };
192 
193 class JSViewFullUpdate : public JSView {
194     DECLARE_ACE_TYPE(JSViewFullUpdate, JSView)
195 
196 public:
197     JSViewFullUpdate(const std::string& viewId, JSRef<JSObject> jsObject, JSRef<JSFunc> jsRenderFunction);
198     ~JSViewFullUpdate() override;
199 
200     void Destroy(JSView* parentCustomView) override;
201 
202     // TODO: delete this after the toolchain for partial update is ready.
203     RefPtr<AceType> InternalRender();
204 
205     RefPtr<AceType> CreateViewNode(bool isTitleNode = false) override;
206 
207     void MarkNeedUpdate() override;
208 
209     /**
210      * During render function execution, the child custom view with same id will
211      * be recycled if they exist already in our child map. The ones which are not
212      * recycled needs to be cleaned. So After render function execution, clean the
213      * abandoned child custom view.
214      */
215     void CleanUpAbandonedChild();
216 
217     /**
218      * Retries the custom view child for recycling
219      * always use FindChildById to be certain before calling this method
220      */
221     JSRef<JSObject> GetChildById(const std::string& viewId);
222 
223     void FindChildById(const JSCallbackInfo& info);
224     void FindChildByIdForPreview(const JSCallbackInfo& info);
225     bool GetChildByViewId(const std::string& viewId, JSRef<JSObject>& childView, JSRef<JSObject>& targetView);
226 
ExecuteUpdateWithValueParams(const std::string & jsonData)227     void ExecuteUpdateWithValueParams(const std::string& jsonData) override
228     {
229         jsViewFunction_->ExecuteUpdateWithValueParams(jsonData);
230     }
231 
MarkLazyForEachProcess(const std::string & groudId)232     void MarkLazyForEachProcess(const std::string& groudId) override
233     {
234         isLazyForEachProcessed_ = true;
235         lazyItemGroupId_ = groudId;
236     }
237 
ResetLazyForEachProcess()238     void ResetLazyForEachProcess() override
239     {
240         isLazyForEachProcessed_ = false;
241         lazyItemGroupId_ = "";
242     }
243 
244     /**
245      * New CustomView child will be added to the map.
246      * and it can be retrieved for recycling in next render function
247      * In next render call if this child is not recycled, it will be destroyed.
248      */
249     std::string AddChildById(const std::string& viewId, const JSRef<JSObject>& obj);
250 
251     void RemoveChildGroupById(const std::string& viewId) override;
252 
253     static void Create(const JSCallbackInfo& info);
254     static void JSBind(BindingTarget globalObj);
255 
256     static void ConstructorCallback(const JSCallbackInfo& args);
257     static void DestructorCallback(JSViewFullUpdate* instance);
258 
259 private:
260     void DestroyChild(JSView* parentCustomView);
261 
262     /**
263      * Takes care of the viewId wrt to foreach
264      */
265     std::string ProcessViewId(const std::string& viewId);
266     /**
267      * creates a set of valid view ids on a render function execution
268      * its cleared after cleaning up the abandoned child.
269      */
270     void ChildAccessedById(const std::string& viewId);
271 
272     bool isLazyForEachProcessed_ = false;
273     std::string lazyItemGroupId_;
274 
275     // unique view id for custom view to recycle.
276     std::string id_;
277     // hold handle to the native and javascript object to keep them alive
278     // until they are abandoned
279     std::unordered_map<std::string, JSRef<JSObject>> customViewChildren_;
280 
281     // hold handle to the native and javascript object to keep them alive
282     // until they are abandoned used by lazyForEach
283     std::unordered_map<std::string, JSRef<JSObject>> customViewChildrenWithLazy_;
284 
285     // hold js view ids by lazy item ground.
286     // until they are abandoned used by lazyForEach
287     std::unordered_map<std::string, std::list<std::string>> lazyItemGroups_;
288 
289     // a set of valid view ids on a render function execution
290     // its cleared after cleaning up the abandoned child.
291     std::unordered_set<std::string> lastAccessedViewIds_;
292 
293     // The C++ JSView object owns a reference to the JS Object
294     // AssignNewView assigns the JS View
295     // Destroy deleted the ref, and thereby triggers the deletion
296     // GC -> JS View Object -> JSView C++ Object
297     JSRef<JSObject> jsViewObject_;
298 };
299 
300 class JSViewPartialUpdate : public JSView {
301     DECLARE_ACE_TYPE(JSViewPartialUpdate, JSView)
302 
303 public:
304     explicit JSViewPartialUpdate(JSRef<JSObject> jsObject);
305     ~JSViewPartialUpdate() override;
306 
307     void Destroy(JSView* parentCustomView) override;
308 
309     RefPtr<AceType> InitialRender();
310 
311     RefPtr<AceType> CreateViewNode(bool isTitleNode = false) override;
312 
313     static void Create(const JSCallbackInfo& info);
314     static void CreateRecycle(const JSCallbackInfo& info);
315     static void JSBind(BindingTarget globalObj);
316 
317     static void ConstructorCallback(const JSCallbackInfo& args);
318     static void DestructorCallback(JSViewPartialUpdate* instance);
319 
320     // public functions added by partial update added below ==================
321 
322     /**
323      * Last step of executing an partial update function
324      * get the result component from ViewStackProcessor
325      * add it to the queue to [elmtId, Component] to
326      * execute an local update on in the UI thread
327      * parameters
328      * elmtId of the Component/Element that's updated
329      * removedElementId : Array<number>  ids of Elements that were removed while updating
330      * caused by if condition toggle or ForEach array deleted / replaced items
331      * return boolean - true means success
332      */
333     void JsFinishUpdateFunc(int32_t elmtId);
334 
335     /**
336     JS exposed function to check from ElementRegister if given elmtId is (still) in use
337     */
338     bool JsElementIdExists(int32_t elmtId);
339 
340     void JSGetProxiedItemRenderState(const JSCallbackInfo& info);
341 
342     void JSGetNavDestinationInfo(const JSCallbackInfo& info);
343 
344     void JSGetRouterPageInfo(const JSCallbackInfo& info);
345 
346     void JSGetNavigationInfo(const JSCallbackInfo& info);
347 
348     void JSGetUIContext(const JSCallbackInfo& info);
349 
350     void JSGetUniqueId(const JSCallbackInfo& info);
351 
352     // Release the UINode hold on the JS object and trigger the delete phase.
JSResetRecycleCustomNode(const JSCallbackInfo & info)353     void JSResetRecycleCustomNode(const JSCallbackInfo& info)
354     {
355         recycleCustomNode_.Reset();
356     }
357 
358     void JSSendStateInfo(const std::string& stateInfo);
359 
isFullUpdate()360     bool isFullUpdate() const override
361     {
362         return false;
363     }
364 
365     void IsFirstRender(const JSCallbackInfo& info);
366     void FindChildByIdForPreview(const JSCallbackInfo& info);
367 
SetJSViewName(const std::string & name)368     void SetJSViewName(const std::string& name)
369     {
370         jsViewName_ = name;
371     }
GetJSViewName()372     const std::string& GetJSViewName() const
373     {
374         return jsViewName_;
375     }
376 
ExecuteInitiallyProvidedValue(const std::string & jsonData)377     void ExecuteInitiallyProvidedValue(const std::string& jsonData) override
378     {
379         jsViewFunction_->ExecuteInitiallyProvidedValue(jsonData);
380     }
SetRecycleCustomNode(const RefPtr<NG::CustomNodeBase> & recycleNode)381     void SetRecycleCustomNode(const RefPtr<NG::CustomNodeBase>& recycleNode)
382     {
383         recycleCustomNode_ = recycleNode;
384     }
385 
GetCachedRecycleNode()386     RefPtr<NG::CustomNodeBase> GetCachedRecycleNode()
387     {
388         auto node = RefPtr<NG::CustomNodeBase>(recycleCustomNode_);
389         recycleCustomNode_.Reset();
390         return node;
391     }
392 
GetRecycleCustomNodeName()393     const std::string& GetRecycleCustomNodeName()
394     {
395         return recycleCustomNodeName_;
396     }
397 
SetRecycleCustomNodeName(const std::string & recycleCustomNodeName)398     void SetRecycleCustomNodeName(const std::string& recycleCustomNodeName)
399     {
400         recycleCustomNodeName_ = recycleCustomNodeName;
401     }
402 
SetIsRecycleRerender(bool isRecycleRerender)403     void SetIsRecycleRerender(bool isRecycleRerender)
404     {
405         isRecycleRerender_ = isRecycleRerender;
406     }
407 
GetIsRecycleRerender()408     bool GetIsRecycleRerender()
409     {
410         return isRecycleRerender_;
411     }
412 
413     void JSSetIsV2(const bool isV2);
414 
GetJSIsV2()415     bool GetJSIsV2() const
416     {
417         return isV2_;
418     }
419 
420     void OnDumpInfo(const std::vector<std::string>& params) override;
421 
422 private:
423     void MarkNeedUpdate() override;
424 
425     // indicates if the JSView has ever completed initial render
426     // used for code branching in lambda given to ComposedComponent
427     // render callback
428     bool isFirstRender_ = true;
429 
430     /* list of update function result is a triple (tuple with three entries)
431     <0> elmtId
432     <1> outmost wrapping Component
433     <2> main Component
434     */
435     std::list<UpdateTask> pendingUpdateTasks_;
436 
437     // The C++ JSView object owns a reference to the JS Object
438     // AssignNewView assigns the JS View
439     // Destroy deleted the ref, and thereby triggers the deletion
440     // GC -> JS View Object -> JSView C++ Object
441     JSRef<JSObject> jsViewObject_;
442 
443     // Restore the custom node related to the JSView object
444     // If the JSView object is GC by engine, this CustomNode will abe deleted
445     // If the JSView object is hold by RecycleManager, this CustomNode will be reused
446     RefPtr<NG::CustomNodeBase> recycleCustomNode_;
447 
448     // Store the recycle nodes name as key
449     std::string recycleCustomNodeName_;
450     std::string jsViewName_;
451 
452     bool isRecycleRerender_ = false;
453     bool isV2_ = false;
454 };
455 
456 } // namespace OHOS::Ace::Framework
457 #endif // FOUNDATION_ACE_FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_VIEW_H
458