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