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 <unistd.h>
19
20 #include "base/geometry/ng/size_t.h"
21 #include "base/utils/utils.h"
22 #include "bridge/declarative_frontend/jsview/js_base_node.h"
23 #include "core/components_ng/base/view_abstract_model.h"
24 #include "core/components_ng/base/view_stack_processor.h"
25 #include "core/components_ng/pattern/node_container/node_container_pattern.h"
26 #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h"
27 #include "frameworks/bridge/declarative_frontend/engine/js_converter.h"
28 #include "frameworks/core/common/container_scope.h"
29 #include "frameworks/core/components_ng/pattern/node_container/node_container_model_ng.h"
30
31 namespace OHOS::Ace {
32 namespace {
33 const char* NODE_CONTAINER_ID = "nodeContainerId_";
34 const char* NODEPTR_OF_UINODE = "nodePtr_";
35 constexpr int32_t INVALID_NODE_CONTAINER_ID = -1;
36 } // namespace
37
38 std::unique_ptr<NodeContainerModel> NodeContainerModel::instance_;
39 std::mutex NodeContainerModel::mutex_;
40
GetInstance()41 NodeContainerModel* NodeContainerModel::GetInstance()
42 {
43 if (!instance_) {
44 std::lock_guard<std::mutex> lock(mutex_);
45 if (!instance_) {
46 #ifdef NG_BUILD
47 instance_.reset(new NG::NodeContainerModelNG());
48 #else
49 if (Container::IsCurrentUseNewPipeline()) {
50 instance_.reset(new NG::NodeContainerModelNG());
51 } else {
52 return nullptr;
53 }
54 #endif
55 }
56 }
57 return instance_.get();
58 }
59 } // namespace OHOS::Ace
60
61 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo & info)62 void JSNodeContainer::Create(const JSCallbackInfo& info)
63 {
64 NodeContainerModel::GetInstance()->Create();
65 auto frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
66 CHECK_NULL_VOID(frameNode);
67 if (info.Length() < 1 || !info[0]->IsObject() || info[0]->IsNull()) {
68 frameNode->RemoveChildAtIndex(0);
69 frameNode->MarkNeedFrameFlushDirty(NG::PROPERTY_UPDATE_MEASURE);
70 ResetNodeController();
71 return;
72 }
73 auto object = JSRef<JSObject>::Cast(info[0]);
74
75 JSObject firstArg = JSRef<JSObject>::Cast(info[0]).Get();
76 auto nodeContainerId = frameNode->GetId();
77 // check if it's the same object, and if it is, return it;
78 auto insideId = firstArg->GetProperty(NODE_CONTAINER_ID);
79 if (insideId->IsNumber()) {
80 auto id = insideId->ToNumber<int32_t>();
81 if (id == nodeContainerId) {
82 return;
83 }
84 }
85 // clear the nodeContainerId_ in pre controller;
86 NodeContainerModel::GetInstance()->ResetController();
87
88 // set a function to reset the nodeContainerId_ in controller;
89 auto resetFunc = [firstArg = JSWeak<JSObject>(object)]() {
90 CHECK_NULL_VOID(!firstArg.IsEmpty());
91 JSObject args = firstArg.Lock().Get();
92 args->SetProperty(NODE_CONTAINER_ID, INVALID_NODE_CONTAINER_ID);
93 };
94 NodeContainerModel::GetInstance()->BindController(std::move(resetFunc));
95 auto execCtx = info.GetExecutionContext();
96 SetNodeController(object, execCtx);
97 // set the nodeContainerId_ to nodeController
98 firstArg->SetProperty(NODE_CONTAINER_ID, nodeContainerId);
99 NodeContainerModel::GetInstance()->FireMakeNode();
100 }
101
SetNodeController(const JSRef<JSObject> & object,JsiExecutionContext execCtx)102 void JSNodeContainer::SetNodeController(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
103 {
104 // get the function to makeNode
105 JSRef<JSVal> jsMakeNodeFunc = object->GetProperty("makeNode");
106 if (!jsMakeNodeFunc->IsFunction()) {
107 ResetNodeController();
108 return;
109 }
110
111 auto jsFunc = JSRef<JSFunc>::Cast(jsMakeNodeFunc);
112 auto containerId = Container::CurrentId();
113 RefPtr<JsFunction> jsMake = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), jsFunc);
114 NodeContainerModel::GetInstance()->SetMakeFunction(
115 [func = std::move(jsMake), containerId, execCtx]() -> RefPtr<NG::UINode> {
116 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
117 ContainerScope scope(containerId);
118 auto container = Container::Current();
119 CHECK_NULL_RETURN(container, nullptr);
120 auto frontend = container->GetFrontend();
121 CHECK_NULL_RETURN(frontend, nullptr);
122 auto context = frontend->GetContextValue();
123 auto jsVal = JsConverter::ConvertNapiValueToJsVal(context);
124 JSRef<JSVal> result = func->ExecuteJS(1, &jsVal);
125 if (result.IsEmpty() || !result->IsObject()) {
126 return nullptr;
127 }
128 JSRef<JSObject> obj = JSRef<JSObject>::Cast(result);
129 JSRef<JSVal> nodeptr = obj->GetProperty(NODEPTR_OF_UINODE);
130 if (nodeptr.IsEmpty()) {
131 return nullptr;
132 }
133 const auto* vm = nodeptr->GetEcmaVM();
134 auto* node = nodeptr->GetLocalHandle()->ToNativePointer(vm)->Value();
135 auto* uiNode = reinterpret_cast<NG::UINode*>(node);
136 CHECK_NULL_RETURN(uiNode, nullptr);
137 return AceType::Claim(uiNode);
138 });
139
140 SetOnAppearFunc(object, execCtx);
141 SetOnDisappearFunc(object, execCtx);
142 SetOnResizeFunc(object, execCtx);
143 SetOnTouchEventFunc(object, execCtx);
144 }
145
ResetNodeController()146 void JSNodeContainer::ResetNodeController()
147 {
148 NodeContainerModel::GetInstance()->ResetController();
149 NodeContainerModel::GetInstance()->SetMakeFunction(nullptr);
150 NodeContainerModel::GetInstance()->SetOnTouchEvent(nullptr);
151 NodeContainerModel::GetInstance()->SetOnResize(nullptr);
152 ViewAbstractModel::GetInstance()->SetOnAppear(nullptr);
153 ViewAbstractModel::GetInstance()->SetOnDisAppear(nullptr);
154 }
155
SetOnAppearFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)156 void JSNodeContainer::SetOnAppearFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
157 {
158 auto showCallback = object->GetProperty("aboutToAppear");
159 CHECK_NULL_VOID(showCallback->IsFunction());
160 RefPtr<JsFunction> jsAppearFunc =
161 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(showCallback));
162 auto onAppear = [func = std::move(jsAppearFunc), execCtx]() {
163 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
164 func->Execute();
165 };
166 ViewAbstractModel::GetInstance()->SetOnAppear(onAppear);
167 }
168
SetOnDisappearFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)169 void JSNodeContainer::SetOnDisappearFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
170 {
171 auto dismissCallback = object->GetProperty("aboutToDisappear");
172 CHECK_NULL_VOID(dismissCallback->IsFunction());
173 RefPtr<JsFunction> jsDisappearFunc =
174 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(dismissCallback));
175 auto onDisappear = [func = std::move(jsDisappearFunc), execCtx]() {
176 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
177 func->Execute();
178 };
179 ViewAbstractModel::GetInstance()->SetOnDisAppear(onDisappear);
180 }
181
SetOnTouchEventFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)182 void JSNodeContainer::SetOnTouchEventFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
183 {
184 auto onTouchEventCallback = object->GetProperty("onTouchEvent");
185 CHECK_NULL_VOID(onTouchEventCallback->IsFunction());
186 RefPtr<JsTouchFunction> jsOnTouchFunc =
187 AceType::MakeRefPtr<JsTouchFunction>(JSRef<JSFunc>::Cast(onTouchEventCallback));
188 WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
189 auto onTouch = [execCtx, func = std::move(jsOnTouchFunc), node = frameNode](TouchEventInfo& info) {
190 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
191 PipelineContext::SetCallBackNode(node);
192 func->Execute(info);
193 };
194 NodeContainerModel::GetInstance()->SetOnTouchEvent(std::move(onTouch));
195 }
196
SetOnResizeFunc(const JSRef<JSObject> & object,JsiExecutionContext execCtx)197 void JSNodeContainer::SetOnResizeFunc(const JSRef<JSObject>& object, JsiExecutionContext execCtx)
198 {
199 auto aboutToResize = object->GetProperty("aboutToResize");
200 CHECK_NULL_VOID(aboutToResize->IsFunction());
201 RefPtr<JsFunction> jsAboutToResizeFunc =
202 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(object), JSRef<JSFunc>::Cast(aboutToResize));
203 auto onResize = [func = std::move(jsAboutToResizeFunc), execCtx](const NG::SizeF& size) {
204 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
205 JSRef<JSObjTemplate> objectTemplate = JSRef<JSObjTemplate>::New();
206 objectTemplate->SetInternalFieldCount(1);
207 JSRef<JSObject> obj = objectTemplate->NewInstance();
208 obj->SetProperty<double>("width", PipelineBase::Px2VpWithCurrentDensity(size.Width()));
209 obj->SetProperty<double>("height", PipelineBase::Px2VpWithCurrentDensity(size.Height()));
210 JSRef<JSVal> param = JSRef<JSVal>::Cast(obj);
211 func->ExecuteJS(1, ¶m);
212 };
213 NodeContainerModel::GetInstance()->SetOnResize(onResize);
214 }
215
GetCurrentContext()216 JSRef<JSVal> JSNodeContainer::GetCurrentContext()
217 {
218 auto container = Container::Current();
219 CHECK_NULL_RETURN(container, JSRef<JSVal>());
220 auto frontend = container->GetFrontend();
221 CHECK_NULL_RETURN(frontend, JSRef<JSVal>());
222 auto context = frontend->GetContextValue();
223 auto jsVal = JsConverter::ConvertNapiValueToJsVal(context);
224 return jsVal;
225 }
226
JSBind(BindingTarget globalObj)227 void JSNodeContainer::JSBind(BindingTarget globalObj)
228 {
229 JSClass<JSNodeContainer>::Declare("NodeContainer");
230
231 MethodOptions opt = MethodOptions::NONE;
232 JSClass<JSNodeContainer>::StaticMethod("create", &JSNodeContainer::Create, opt);
233 JSClass<JSNodeContainer>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
234 JSClass<JSNodeContainer>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
235 JSClass<JSNodeContainer>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
236 JSClass<JSNodeContainer>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
237 JSClass<JSNodeContainer>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
238 JSClass<JSNodeContainer>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
239
240 JSClass<JSNodeContainer>::InheritAndBind<JSViewAbstract>(globalObj);
241 }
242 } // namespace OHOS::Ace::Framework
243