1 /*
2 * Copyright (c) 2023-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 #include "bridge/declarative_frontend/jsview/js_base_node.h"
16
17 #include <memory>
18 #include <string>
19
20 #include "canvas_napi/js_canvas.h"
21 #include "jsnapi_expo.h"
22
23 #include "base/geometry/dimension.h"
24 #include "base/memory/ace_type.h"
25 #include "base/memory/referenced.h"
26 #include "base/utils/utils.h"
27 #include "bridge/common/utils/engine_helper.h"
28 #include "bridge/declarative_frontend/engine/jsi/nativeModule/ui_context_helper.h"
29 #include "bridge/declarative_frontend/engine/functions/js_function.h"
30 #include "bridge/declarative_frontend/engine/js_converter.h"
31 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
32 #include "bridge/declarative_frontend/engine/js_types.h"
33 #include "bridge/declarative_frontend/jsview/js_utils.h"
34 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
35 #include "bridge/js_frontend/engine/jsi/js_value.h"
36 #include "core/components_ng/base/frame_node.h"
37 #include "core/components_ng/base/modifier.h"
38 #include "core/components_ng/base/ui_node.h"
39 #include "core/components_ng/base/view_stack_processor.h"
40 #include "core/components_ng/pattern/custom_frame_node/custom_frame_node.h"
41 #include "core/components_ng/pattern/render_node/render_node_pattern.h"
42 #include "core/components_ng/pattern/stack/stack_pattern.h"
43 #include "core/components_ng/render/drawing_forward.h"
44 #include "core/event/touch_event.h"
45 #include "core/pipeline/pipeline_base.h"
46 #include "core/pipeline_ng/pipeline_context.h"
47
48 namespace OHOS::Ace::Framework {
49 namespace {
50 const std::unordered_set<std::string> EXPORT_TEXTURE_SUPPORT_TYPES = { V2::JS_VIEW_ETS_TAG, V2::COMMON_VIEW_ETS_TAG };
51 constexpr int32_t INFO_LENGTH_LIMIT = 2;
52 constexpr int32_t BUILD_PARAM_INDEX_TWO = 2;
53 constexpr int32_t BUILD_PARAM_INDEX_THREE = 3;
54 constexpr int32_t BUILD_PARAM_INDEX_FOUR = 4;
55 } // namespace
56
BuildNode(const JSCallbackInfo & info)57 void JSBaseNode::BuildNode(const JSCallbackInfo& info)
58 {
59 auto builder = info[0];
60 CHECK_NULL_VOID(builder->IsFunction());
61 auto buildFunc = AceType::MakeRefPtr<JsFunction>(info.This(), JSRef<JSFunc>::Cast(builder));
62
63 auto infoLen = info.Length();
64 JSRef<JSVal> param;
65 if (infoLen >= INFO_LENGTH_LIMIT && (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
66 || (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE) && info[1]->IsObject()))) {
67 param = info[1];
68 }
69
70 auto lazyBuilderFunc = [buildFunc, param, renderType = renderType_]() mutable {
71 NG::ViewStackProcessor::GetInstance()->SetIsBuilderNode(true);
72 NG::ViewStackProcessor::GetInstance()->SetIsExportTexture(renderType == NodeRenderType::RENDER_TYPE_TEXTURE);
73 if (!param->IsEmpty()) {
74 buildFunc->ExecuteJS(1, ¶m);
75 } else {
76 buildFunc->ExecuteJS();
77 }
78 };
79
80 NG::ScopedViewStackProcessor builderViewStackProcessor;
81 lazyBuilderFunc();
82 auto parent = viewNode_ ? viewNode_->GetParent() : nullptr;
83 auto newNode = NG::ViewStackProcessor::GetInstance()->Finish();
84 if (newNode) {
85 newNode->SetBuilderFunc(std::move(lazyBuilderFunc));
86 }
87
88 if (newNode && (infoLen >= BUILD_PARAM_INDEX_TWO + 1)) {
89 auto updateTsNodeBuilder = info[BUILD_PARAM_INDEX_TWO];
90 EcmaVM* vm = info.GetVm();
91 auto updateTsFunc = AceType::MakeRefPtr<JsFunction>(info.This(), JSRef<JSFunc>::Cast(updateTsNodeBuilder));
92 auto updateNodeFunc = [updateTsFunc, vm](int32_t instanceId, RefPtr<NG::UINode>& node) mutable {
93 JSRef<JSVal> param[2];
94 param[0] = JSRef<JSVal>::Make(ToJSValue(instanceId));
95 param[1] = JSRef<JSVal>::Make(panda::NativePointerRef::New(vm, AceType::RawPtr(node)));
96 updateTsFunc->ExecuteJS(2, param);
97 };
98 newNode->SetUpdateNodeFunc(std::move(updateNodeFunc));
99 }
100
101 if (newNode && (infoLen >= BUILD_PARAM_INDEX_THREE + 1)) {
102 auto updateTsNodeConfig = info[BUILD_PARAM_INDEX_THREE];
103 EcmaVM* vm = info.GetVm();
104 auto updateTsConfig = AceType::MakeRefPtr<JsFunction>(info.This(), JSRef<JSFunc>::Cast(updateTsNodeConfig));
105 auto updateNodeConfig = [updateTsConfig, vm]() mutable {
106 updateTsConfig->ExecuteJS();
107 };
108 newNode->SetUpdateNodeConfig(std::move(updateNodeConfig));
109 }
110
111 bool isSupportLazyBuild = false;
112 if (infoLen >= BUILD_PARAM_INDEX_FOUR + 1) {
113 auto jsLazyBuildSupported = info[BUILD_PARAM_INDEX_FOUR];
114 if (jsLazyBuildSupported->IsBoolean()) {
115 isSupportLazyBuild = jsLazyBuildSupported->ToBoolean();
116 }
117 }
118
119 // If the node is a UINode, amount it to a BuilderProxyNode if needProxy.
120 auto flag = AceType::InstanceOf<NG::FrameNode>(newNode);
121 auto isSupportExportTexture = newNode ? EXPORT_TEXTURE_SUPPORT_TYPES.count(newNode->GetTag()) > 0 : false;
122 if (!flag && newNode) {
123 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
124 auto proxyNode = NG::FrameNode::GetOrCreateFrameNode(
125 "BuilderProxyNode", nodeId, []() { return AceType::MakeRefPtr<NG::StackPattern>(); });
126 auto stackLayoutAlgorithm = proxyNode->GetLayoutProperty<NG::LayoutProperty>();
127 stackLayoutAlgorithm->UpdateAlignment(Alignment::TOP_LEFT);
128 proxyNode->AddChild(newNode);
129 newNode = proxyNode;
130 }
131 if (parent) {
132 if (newNode) {
133 parent->ReplaceChild(viewNode_, newNode);
134 newNode->MarkNeedFrameFlushDirty(NG::PROPERTY_UPDATE_MEASURE);
135 } else {
136 parent->RemoveChild(viewNode_);
137 parent->MarkNeedFrameFlushDirty(NG::PROPERTY_UPDATE_MEASURE);
138 }
139 }
140 viewNode_ = newNode ? AceType::DynamicCast<NG::FrameNode>(newNode) : nullptr;
141 CHECK_NULL_VOID(viewNode_);
142 ProccessNode(isSupportExportTexture, isSupportLazyBuild);
143 UpdateEnd(info);
144 CHECK_NULL_VOID(viewNode_);
145 JSRef<JSObject> thisObj = info.This();
146 JSWeak<JSObject> jsObject(thisObj);
147 viewNode_->RegisterUpdateJSInstanceCallback([jsObject, vm = info.GetVm()](int32_t id) {
148 JSRef<JSObject> jsThis = jsObject.Lock();
149 JSRef<JSVal> jsUpdateFunc = jsThis->GetProperty("updateInstance");
150 if (jsUpdateFunc->IsFunction()) {
151 auto jsFunc = JSRef<JSFunc>::Cast(jsUpdateFunc);
152 auto uiContext = NG::UIContextHelper::GetUIContext(vm, id);
153 auto jsVal = JSRef<JSVal>::Make(uiContext);
154 jsFunc->Call(jsThis, 1, &jsVal);
155 }
156 });
157 }
158
ProccessNode(bool isSupportExportTexture,bool isSupportLazyBuild)159 void JSBaseNode::ProccessNode(bool isSupportExportTexture, bool isSupportLazyBuild)
160 {
161 CHECK_NULL_VOID(viewNode_);
162 viewNode_->SetIsRootBuilderNode(true);
163 if (isSupportExportTexture) {
164 viewNode_->CreateExportTextureInfoIfNeeded();
165 auto exportTextureInfo = viewNode_->GetExportTextureInfo();
166 CHECK_NULL_VOID(exportTextureInfo);
167 exportTextureInfo->SetSurfaceId(surfaceId_);
168 exportTextureInfo->SetCurrentRenderType(renderType_);
169 }
170 if (!isSupportLazyBuild) {
171 viewNode_->Build(nullptr);
172 }
173 }
174
Create(const JSCallbackInfo & info)175 void JSBaseNode::Create(const JSCallbackInfo& info)
176 {
177 if (info.Length() >= 1 && !info[0]->IsFunction()) {
178 return;
179 }
180 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE) && info.Length() >= INFO_LENGTH_LIMIT
181 && !(info[1]->IsObject() || info[1]->IsUndefined() || info[1]->IsNull())) {
182 return;
183 }
184 BuildNode(info);
185 EcmaVM* vm = info.GetVm();
186 info.SetReturnValue(JSRef<JSVal>::Make(panda::NativePointerRef::New(vm, AceType::RawPtr(viewNode_))));
187 }
188
ConstructorCallback(const JSCallbackInfo & info)189 void JSBaseNode::ConstructorCallback(const JSCallbackInfo& info)
190 {
191 std::string surfaceId;
192 NodeRenderType renderType = NodeRenderType::RENDER_TYPE_DISPLAY;
193 NG::OptionalSizeF selfIdealSize;
194 if (info.Length() > 0 && info[0]->IsObject()) {
195 auto renderOption = JSRef<JSObject>::Cast(info[0]);
196 auto size = renderOption->GetProperty("selfIdealSize");
197 if (size->IsObject()) {
198 auto sizeObj = JSRef<JSObject>::Cast(size);
199 auto width = sizeObj->GetProperty("width");
200 auto widthValue = width->IsNumber() ? width->ToNumber<float>() : 0.0f;
201 widthValue = LessNotEqual(widthValue, 0.0f) ? 0.0f : widthValue;
202 auto height = sizeObj->GetProperty("height");
203 auto heightValue = height->IsNumber() ? height->ToNumber<float>() : 0.0f;
204 heightValue = LessNotEqual(heightValue, 0.0f) ? 0.0f : heightValue;
205 selfIdealSize.SetWidth(PipelineBase::Vp2PxWithCurrentDensity(widthValue));
206 selfIdealSize.SetHeight(PipelineBase::Vp2PxWithCurrentDensity(heightValue));
207 }
208 auto type = renderOption->GetProperty("type");
209 if (type->IsNumber()) {
210 renderType = static_cast<NodeRenderType>(type->ToNumber<uint32_t>());
211 }
212 auto id = renderOption->GetProperty("surfaceId");
213 if (id->IsString()) {
214 surfaceId = id->ToString();
215 }
216 }
217 auto instance = AceType::MakeRefPtr<JSBaseNode>(selfIdealSize, renderType, surfaceId);
218 instance->IncRefCount();
219 info.SetReturnValue(AceType::RawPtr(instance));
220 }
221
DestructorCallback(JSBaseNode * node)222 void JSBaseNode::DestructorCallback(JSBaseNode* node)
223 {
224 if (node != nullptr) {
225 node->DecRefCount();
226 }
227 }
228
FinishUpdateFunc(const JSCallbackInfo & info)229 void JSBaseNode::FinishUpdateFunc(const JSCallbackInfo& info)
230 {
231 NG::ViewStackProcessor::GetInstance()->FlushRerenderTask();
232 }
233
PostTouchEvent(const JSCallbackInfo & info)234 void JSBaseNode::PostTouchEvent(const JSCallbackInfo& info)
235 {
236 if (!viewNode_ || info.Length() < 1 || !info[0]->IsObject()) {
237 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
238 return;
239 }
240 TouchEvent touchEvent;
241 auto obj = JSRef<JSObject>::Cast(info[0]);
242 auto typeJsVal = obj->GetProperty("type");
243 if (typeJsVal->IsNumber()) {
244 touchEvent.type = static_cast<TouchType>(typeJsVal->ToNumber<int32_t>());
245 }
246 auto sourceJsVal = obj->GetProperty("source");
247 if (sourceJsVal->IsNumber()) {
248 touchEvent.sourceType = static_cast<SourceType>((sourceJsVal->ToNumber<int32_t>()));
249 }
250 auto sourceToolJsVal = obj->GetProperty("sourceTool");
251 if (sourceToolJsVal->IsNumber()) {
252 touchEvent.sourceTool = static_cast<SourceTool>((sourceToolJsVal->ToNumber<int32_t>()));
253 }
254 auto pressureJsVal = obj->GetProperty("pressure");
255 if (pressureJsVal->IsNumber()) {
256 touchEvent.force = sourceToolJsVal->ToNumber<float>();
257 }
258 auto timestampJsVal = obj->GetProperty("timestamp");
259 if (timestampJsVal->IsNumber()) {
260 std::chrono::nanoseconds nanoseconds(static_cast<int64_t>(timestampJsVal->ToNumber<double>()));
261 TimeStamp time(nanoseconds);
262 touchEvent.time = time;
263 }
264 auto deviceIdJsVal = obj->GetProperty("deviceId");
265 if (deviceIdJsVal->IsNumber()) {
266 touchEvent.deviceId = deviceIdJsVal->ToNumber<int32_t>();
267 }
268 auto targetDisplayIdJsVal = obj->GetProperty("targetDisplayId");
269 if (targetDisplayIdJsVal->IsNumber()) {
270 touchEvent.targetDisplayId = targetDisplayIdJsVal->ToNumber<int32_t>();
271 }
272 auto touchesJsVal = obj->GetProperty("touches");
273 if (touchesJsVal->IsArray()) {
274 JSRef<JSArray> touchesArray = JSRef<JSArray>::Cast(touchesJsVal);
275 for (auto index = 0; index < static_cast<int32_t>(touchesArray->Length()); index++) {
276 JSRef<JSVal> item = touchesArray->GetValueAt(index);
277 if (!item->IsObject()) {
278 continue;
279 }
280 JSRef<JSObject> itemObj = JSRef<JSObject>::Cast(item);
281 TouchPoint point;
282 point.id = itemObj->GetPropertyValue<int32_t>("id", 0);
283 point.x = itemObj->GetPropertyValue<float>("x", 0.0f);
284 point.y = itemObj->GetPropertyValue<float>("y", 0.0f);
285 point.screenX = itemObj->GetPropertyValue<float>("screenX", 0.0f);
286 point.screenY = itemObj->GetPropertyValue<float>("screenY", 0.0f);
287 point.originalId = itemObj->GetPropertyValue<int32_t>("id", 0);
288 touchEvent.pointers.emplace_back(point);
289 }
290 }
291 auto titleXJsVal = obj->GetProperty("tiltX");
292 if (titleXJsVal->IsNumber()) {
293 touchEvent.tiltX = titleXJsVal->ToNumber<float>();
294 }
295 auto titleYJsVal = obj->GetProperty("tiltY");
296 if (titleYJsVal->IsNumber()) {
297 touchEvent.tiltY = titleYJsVal->ToNumber<float>();
298 }
299 auto changedTouchesJsVal = obj->GetProperty("changedTouches");
300 if (changedTouchesJsVal->IsArray()) {
301 JSRef<JSArray> changedTouchesArray = JSRef<JSArray>::Cast(changedTouchesJsVal);
302 if (static_cast<int32_t>(changedTouchesArray->Length()) <= 0) {
303 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
304 return;
305 }
306 JSRef<JSVal> item = changedTouchesArray->GetValueAt(0);
307 if (!item->IsObject()) {
308 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
309 return;
310 }
311 JSRef<JSObject> itemObj = JSRef<JSObject>::Cast(item);
312 touchEvent.id = itemObj->GetPropertyValue<int32_t>("id", 0);
313 touchEvent.x = itemObj->GetPropertyValue<float>("x", 0.0f);
314 touchEvent.y = itemObj->GetPropertyValue<float>("y", 0.0f);
315 touchEvent.screenX = itemObj->GetPropertyValue<float>("screenX", 0.0f);
316 touchEvent.screenY = itemObj->GetPropertyValue<float>("screenY", 0.0f);
317 touchEvent.originalId = itemObj->GetPropertyValue<int32_t>("id", 0);
318 }
319 auto pipelineContext = NG::PipelineContext::GetCurrentContext();
320 if (!pipelineContext) {
321 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
322 return;
323 }
324 auto postEventManager = pipelineContext->GetPostEventManager();
325 if (!postEventManager) {
326 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
327 return;
328 }
329 auto result = postEventManager->PostEvent(viewNode_, touchEvent);
330 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(result)));
331 }
332
UpdateStart(const JSCallbackInfo & info)333 void JSBaseNode::UpdateStart(const JSCallbackInfo& info)
334 {
335 scopedViewStackProcessor_ = std::make_unique<NG::ScopedViewStackProcessor>();
336 }
337
UpdateEnd(const JSCallbackInfo & info)338 void JSBaseNode::UpdateEnd(const JSCallbackInfo& info)
339 {
340 scopedViewStackProcessor_ = nullptr;
341 CHECK_NULL_VOID(viewNode_);
342 if (size_.IsValid()) {
343 viewNode_->SetParentLayoutConstraint(size_.ConvertToSizeT());
344 }
345 }
346
JSBind(BindingTarget globalObj)347 void JSBaseNode::JSBind(BindingTarget globalObj)
348 {
349 JSClass<JSBaseNode>::Declare("__JSBaseNode__");
350
351 JSClass<JSBaseNode>::CustomMethod("create", &JSBaseNode::Create);
352 JSClass<JSBaseNode>::CustomMethod("finishUpdateFunc", &JSBaseNode::FinishUpdateFunc);
353 JSClass<JSBaseNode>::CustomMethod("postTouchEvent", &JSBaseNode::PostTouchEvent);
354 JSClass<JSBaseNode>::CustomMethod("disposeNode", &JSBaseNode::Dispose);
355 JSClass<JSBaseNode>::CustomMethod("updateStart", &JSBaseNode::UpdateStart);
356 JSClass<JSBaseNode>::CustomMethod("updateEnd", &JSBaseNode::UpdateEnd);
357
358 JSClass<JSBaseNode>::Bind(globalObj, JSBaseNode::ConstructorCallback, JSBaseNode::DestructorCallback);
359 }
360 } // namespace OHOS::Ace::Framework
361