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 #include "bridge/declarative_frontend/jsview/js_node_container.h"
17 
18 #include <functional>
19 #include <mutex>
20 #include <unistd.h>
21 
22 #include "base/geometry/ng/size_t.h"
23 #include "base/utils/utils.h"
24 #include "bridge/common/utils/engine_helper.h"
25 #include "bridge/declarative_frontend/engine/functions/js_function.h"
26 #include "bridge/declarative_frontend/engine/js_converter.h"
27 #include "bridge/declarative_frontend/engine/jsi/jsi_types.h"
28 #include "bridge/declarative_frontend/engine/jsi/nativeModule/ui_context_helper.h"
29 #include "bridge/declarative_frontend/jsview/js_base_node.h"
30 #include "core/common/container_scope.h"
31 #include "core/components_ng/base/view_abstract_model.h"
32 #include "core/components_ng/base/view_stack_processor.h"
33 #include "core/components_ng/pattern/node_container/node_container_model_ng.h"
34 #include "core/components_ng/pattern/node_container/node_container_pattern.h"
35 
36 namespace OHOS::Ace {
37 namespace {
38 const char* NODE_CONTAINER_ID = "_nodeContainerId";
39 const char* INTERNAL_FIELD_VALUE = "_value";
40 const char* NODEPTR_OF_UINODE = "nodePtr_";
41 constexpr int32_t INVALID_NODE_CONTAINER_ID = -1;
42 
BindFunc(const Framework::JSCallbackInfo & info,const RefPtr<NG::FrameNode> & frameNode)43 void BindFunc(const Framework::JSCallbackInfo& info, const RefPtr<NG::FrameNode>& frameNode)
44 {
45     CHECK_NULL_VOID(frameNode);
46     CHECK_NULL_VOID(!frameNode->HasOnNodeDestroyCallback());
47     EcmaVM* vm = info.GetVm();
48     auto global = JSNApi::GetGlobalObject(vm);
49     auto funcName = panda::StringRef::NewFromUtf8(vm, "__RemoveFromNodeControllerMap__");
50     auto obj = global->Get(vm, funcName);
51     if (obj->IsFunction(vm)) {
52         panda::Local<panda::FunctionRef> detachFunc = obj;
53         frameNode->SetOnNodeDestroyCallback([vm, func = panda::CopyableGlobal(vm, detachFunc)](int32_t nodeId) {
54             panda::Local<panda::JSValueRef> params[] = { panda::NumberRef::New(vm, nodeId) };
55             func->Call(vm, func.ToLocal(), params, ArraySize(params));
56         });
57     }
58 }
59 
AddToNodeControllerMap(EcmaVM * vm,int32_t nodeId,const Framework::JSRef<Framework::JSObject> & object)60 void AddToNodeControllerMap(EcmaVM* vm, int32_t nodeId, const Framework::JSRef<Framework::JSObject>& object)
61 {
62     auto global = JSNApi::GetGlobalObject(vm);
63     auto funcName = panda::StringRef::NewFromUtf8(vm, "__AddToNodeControllerMap__");
64     auto obj = global->Get(vm, funcName);
65     if (obj->IsFunction(vm)) {
66         panda::Local<panda::FunctionRef> attachFunc = obj;
67         panda::Local<panda::JSValueRef> params[] = { panda::NumberRef::New(vm, nodeId),
68             panda::CopyableGlobal(vm, object->GetLocalHandle()).ToLocal() };
69         attachFunc->Call(vm, attachFunc, params, ArraySize(params));
70     }
71 }
72 } // namespace
73 
74 std::unique_ptr<NodeContainerModel> NodeContainerModel::instance_;
75 std::mutex NodeContainerModel::mutex_;
76 
GetInstance()77 NodeContainerModel* NodeContainerModel::GetInstance()
78 {
79     if (!instance_) {
80         std::lock_guard<std::mutex> lock(mutex_);
81         if (!instance_) {
82 #ifdef NG_BUILD
83             instance_.reset(new NG::NodeContainerModelNG());
84 #else
85             if (Container::IsCurrentUseNewPipeline()) {
86                 instance_.reset(new NG::NodeContainerModelNG());
87             } else {
88                 LOGE("NodeContainerModelNG is not supported in OldPipeline.");
89                 return nullptr;
90             }
91 #endif
92         }
93     }
94     return instance_.get();
95 }
96 } // namespace OHOS::Ace
97 
98 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo & info)99 void JSNodeContainer::Create(const JSCallbackInfo& info)
100 {
101     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
102     CHECK_NULL_VOID(nodeContainerModelInstance);
103     nodeContainerModelInstance->Create();
104     auto frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
105     CHECK_NULL_VOID(frameNode);
106     if (info.Length() < 1 || !info[0]->IsObject() || info[0]->IsNull()) {
107         frameNode->RemoveChildAtIndex(0);
108         frameNode->MarkNeedFrameFlushDirty(NG::PROPERTY_UPDATE_MEASURE);
109         ResetNodeController();
110         return;
111     }
112     auto object = JSRef<JSObject>::Cast(info[0]);
113 
114     JSObject firstArg = JSRef<JSObject>::Cast(info[0]).Get();
115     auto nodeContainerId = frameNode->GetId();
116     // check if it's the same object, and if it is, return it;
117     auto internalField = firstArg->GetProperty(NODE_CONTAINER_ID);
118     if (internalField->IsObject()) {
119         auto obj = JSRef<JSObject>::Cast(internalField);
120         auto insideId = obj->GetProperty(INTERNAL_FIELD_VALUE);
121         if (insideId->IsNumber()) {
122             auto id = insideId->ToNumber<int32_t>();
123             if (id == nodeContainerId) {
124                 return;
125             }
126         }
127     }
128     // clear the _nodeContainerId in pre controller;
129     nodeContainerModelInstance->ResetController();
130 
131     BindFunc(info, AceType::Claim(frameNode));
132     AddToNodeControllerMap(info.GetVm(), frameNode->GetId(), object);
133     // set a function to reset the _nodeContainerId in controller;
134     auto resetFunc = [firstArg = JSWeak<JSObject>(object)]() {
135         CHECK_NULL_VOID(!firstArg.IsEmpty());
136         JSObject args = firstArg.Lock().Get();
137         auto internalField = args->GetProperty(NODE_CONTAINER_ID);
138         CHECK_NULL_VOID(internalField->IsObject());
139         auto obj = JSRef<JSObject>::Cast(internalField);
140         obj->SetProperty(INTERNAL_FIELD_VALUE, INVALID_NODE_CONTAINER_ID);
141     };
142     nodeContainerModelInstance->BindController(std::move(resetFunc));
143     auto execCtx = info.GetExecutionContext();
144 
145     SetNodeController(object, execCtx);
146     // set the _nodeContainerId to nodeController
147     if (internalField->IsObject()) {
148         auto obj = JSRef<JSObject>::Cast(internalField);
149         obj->SetProperty(INTERNAL_FIELD_VALUE, nodeContainerId);
150     }
151     nodeContainerModelInstance->FireMakeNode();
152 }
153 
SetNodeController(const JSRef<JSObject> & object,JsiExecutionContext execCtx)154 void JSNodeContainer::SetNodeController(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
155 {
156     // get the function to makeNode
157     JSRef<JSVal> jsMakeNodeFunc = object->GetProperty("__makeNode__");
158     if (!jsMakeNodeFunc->IsFunction()) {
159         ResetNodeController();
160         return;
161     }
162     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
163     CHECK_NULL_VOID(nodeContainerModelInstance);
164 
165     auto jsFunc = JSRef<JSFunc>::Cast(jsMakeNodeFunc);
166     auto containerId = Container::CurrentId();
167     RefPtr<JsFunction> jsMake = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), jsFunc);
168     nodeContainerModelInstance->SetMakeFunction(
169         [func = std::move(jsMake), containerId, execCtx]() -> RefPtr<NG::UINode> {
170             JAVASCRIPT_EXECUTION_SCOPE(execCtx);
171             ContainerScope scope(containerId);
172             panda::Local<panda::JSValueRef> uiContext = NG::UIContextHelper::GetUIContext(execCtx.vm_, containerId);
173             auto jsVal = JSRef<JSVal>::Make(uiContext);
174             JSRef<JSVal> result = func->ExecuteJS(1, &jsVal);
175             if (result.IsEmpty() || !result->IsObject()) {
176                 return nullptr;
177             }
178             JSRef<JSObject> obj = JSRef<JSObject>::Cast(result);
179             JSRef<JSVal> nodeptr = obj->GetProperty(NODEPTR_OF_UINODE);
180             if (nodeptr.IsEmpty()) {
181                 return nullptr;
182             }
183             const auto* vm = nodeptr->GetEcmaVM();
184             auto* node = nodeptr->GetLocalHandle()->ToNativePointer(vm)->Value();
185             auto* uiNode = reinterpret_cast<NG::UINode*>(node);
186             CHECK_NULL_RETURN(uiNode, nullptr);
187             return AceType::Claim(uiNode);
188         });
189 
190     SetOnAppearFunc(object, execCtx);
191     SetOnDisappearFunc(object, execCtx);
192     SetOnAttachFunc(object, execCtx);
193     SetOnDetachFunc(object, execCtx);
194     SetOnResizeFunc(object, execCtx);
195     SetOnTouchEventFunc(object, execCtx);
196 }
197 
ResetNodeController()198 void JSNodeContainer::ResetNodeController()
199 {
200     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
201     CHECK_NULL_VOID(nodeContainerModelInstance);
202     nodeContainerModelInstance->ResetController();
203     nodeContainerModelInstance->SetMakeFunction(nullptr);
204     nodeContainerModelInstance->SetOnTouchEvent(nullptr);
205     nodeContainerModelInstance->SetOnResize(nullptr);
206     nodeContainerModelInstance->SetOnAppear(nullptr);
207     nodeContainerModelInstance->SetOnDisAppear(nullptr);
208     ViewAbstractModel::GetInstance()->SetOnAttach(nullptr);
209     ViewAbstractModel::GetInstance()->SetOnDetach(nullptr);
210 }
211 
SetOnAppearFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)212 void JSNodeContainer::SetOnAppearFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
213 {
214     auto showCallback = object->GetProperty("aboutToAppear");
215     CHECK_NULL_VOID(showCallback->IsFunction());
216     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
217     CHECK_NULL_VOID(nodeContainerModelInstance);
218     RefPtr<JsFunction> jsAppearFunc =
219         AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(showCallback));
220     auto onAppear = [func = std::move(jsAppearFunc), execCtx]() {
221         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
222         func->Execute();
223     };
224     nodeContainerModelInstance->SetOnAppear(onAppear);
225 }
226 
SetOnDisappearFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)227 void JSNodeContainer::SetOnDisappearFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
228 {
229     auto dismissCallback = object->GetProperty("aboutToDisappear");
230     CHECK_NULL_VOID(dismissCallback->IsFunction());
231     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
232     CHECK_NULL_VOID(nodeContainerModelInstance);
233     RefPtr<JsFunction> jsDisappearFunc =
234         AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(dismissCallback));
235     auto onDisappear = [func = std::move(jsDisappearFunc), execCtx]() {
236         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
237         func->Execute();
238     };
239     nodeContainerModelInstance->SetOnDisAppear(onDisappear);
240 }
241 
SetOnAttachFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)242 void JSNodeContainer::SetOnAttachFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
243 {
244     auto showCallback = object->GetProperty("aboutToAttach");
245     CHECK_NULL_VOID(showCallback->IsFunction());
246     RefPtr<JsFunction> jsAppearFunc =
247         AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(showCallback));
248     auto onAttach = [func = std::move(jsAppearFunc), execCtx]() {
249         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
250         func->Execute();
251     };
252     ViewAbstractModel::GetInstance()->SetOnAttach(onAttach);
253 }
254 
SetOnDetachFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)255 void JSNodeContainer::SetOnDetachFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
256 {
257     auto dismissCallback = object->GetProperty("aboutToDetach");
258     CHECK_NULL_VOID(dismissCallback->IsFunction());
259     RefPtr<JsFunction> jsDisappearFunc =
260         AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(dismissCallback));
261     auto onDetach = [func = std::move(jsDisappearFunc), execCtx]() {
262         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
263         func->Execute();
264     };
265     ViewAbstractModel::GetInstance()->SetOnDetach(onDetach);
266 }
267 
SetOnTouchEventFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)268 void JSNodeContainer::SetOnTouchEventFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
269 {
270     auto onTouchEventCallback = object->GetProperty("onTouchEvent");
271     CHECK_NULL_VOID(onTouchEventCallback->IsFunction());
272     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
273     CHECK_NULL_VOID(nodeContainerModelInstance);
274     RefPtr<JsTouchFunction> jsOnTouchFunc =
275         AceType::MakeRefPtr<JsTouchFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(onTouchEventCallback));
276     WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
277     auto onTouch = [execCtx, func = std::move(jsOnTouchFunc), node = frameNode](TouchEventInfo& info) {
278         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
279         PipelineContext::SetCallBackNode(node);
280         func->Execute(info);
281     };
282     nodeContainerModelInstance->SetOnTouchEvent(std::move(onTouch));
283 }
284 
SetOnResizeFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)285 void JSNodeContainer::SetOnResizeFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
286 {
287     auto aboutToResize = object->GetProperty("aboutToResize");
288     CHECK_NULL_VOID(aboutToResize->IsFunction());
289     NodeContainerModel* nodeContainerModelInstance = NodeContainerModel::GetInstance();
290     CHECK_NULL_VOID(nodeContainerModelInstance);
291     RefPtr<JsFunction> jsAboutToResizeFunc =
292         AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(aboutToResize));
293     auto onResize = [func = std::move(jsAboutToResizeFunc), execCtx](const NG::SizeF& size) {
294         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
295         JSRef<JSObjTemplate> objectTemplate = JSRef<JSObjTemplate>::New();
296         objectTemplate->SetInternalFieldCount(1);
297         JSRef<JSObject> obj = objectTemplate->NewInstance();
298         obj->SetProperty<double>("width", PipelineBase::Px2VpWithCurrentDensity(size.Width()));
299         obj->SetProperty<double>("height", PipelineBase::Px2VpWithCurrentDensity(size.Height()));
300         JSRef<JSVal> param = JSRef<JSVal>::Cast(obj);
301         func->ExecuteJS(1, &param);
302     };
303     nodeContainerModelInstance->SetOnResize(onResize);
304 }
305 
GetCurrentContext()306 JSRef<JSVal> JSNodeContainer::GetCurrentContext()
307 {
308     auto container = Container::Current();
309     CHECK_NULL_RETURN(container, JSRef<JSVal>());
310     auto frontend = container->GetFrontend();
311     CHECK_NULL_RETURN(frontend, JSRef<JSVal>());
312     auto context = frontend->GetContextValue();
313     auto jsVal = JsConverter::ConvertNapiValueToJsVal(context);
314     return jsVal;
315 }
316 
JSBind(BindingTarget globalObj)317 void JSNodeContainer::JSBind(BindingTarget globalObj)
318 {
319     JSClass<JSNodeContainer>::Declare("NodeContainer");
320 
321     MethodOptions opt = MethodOptions::NONE;
322     JSClass<JSNodeContainer>::StaticMethod("create", &JSNodeContainer::Create, opt);
323     JSClass<JSNodeContainer>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
324     JSClass<JSNodeContainer>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
325     JSClass<JSNodeContainer>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
326     JSClass<JSNodeContainer>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
327     JSClass<JSNodeContainer>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
328     JSClass<JSNodeContainer>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
329     JSClass<JSNodeContainer>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
330     JSClass<JSNodeContainer>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
331 
332     JSClass<JSNodeContainer>::InheritAndBind<JSViewAbstract>(globalObj);
333 }
334 } // namespace OHOS::Ace::Framework
335