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, &param);
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