• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param);
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