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 #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
22 #include "base/geometry/dimension.h"
23 #include "base/memory/ace_type.h"
24 #include "base/memory/referenced.h"
25 #include "base/utils/utils.h"
26 #include "bridge/common/utils/engine_helper.h"
27 #include "bridge/declarative_frontend/engine/functions/js_function.h"
28 #include "bridge/declarative_frontend/engine/js_converter.h"
29 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
30 #include "bridge/declarative_frontend/engine/js_types.h"
31 #include "bridge/declarative_frontend/jsview/js_utils.h"
32 #include "bridge/js_frontend/engine/jsi/js_value.h"
33 #include "core/components/common/properties/color.h"
34 #include "core/components_ng/base/frame_node.h"
35 #include "core/components_ng/base/modifier.h"
36 #include "core/components_ng/base/view_stack_processor.h"
37 #include "core/components_ng/pattern/render_node/render_node_pattern.h"
38 #include "core/components_ng/pattern/stack/stack_pattern.h"
39 #include "core/components_ng/render/drawing_forward.h"
40 #include "core/event/touch_event.h"
41 #include "core/pipeline/pipeline_base.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43
44 namespace OHOS::Ace::Framework {
45 namespace {
46 const std::unordered_set<std::string> EXPORT_TEXTURE_SUPPORT_TYPES = { V2::JS_VIEW_ETS_TAG, V2::COMMON_VIEW_ETS_TAG };
47 } // namespace
48
BuildNode(const JSCallbackInfo & info)49 void JSBaseNode::BuildNode(const JSCallbackInfo& info)
50 {
51 auto builder = info[0];
52 auto buildFunc = AceType::MakeRefPtr<JsFunction>(info.This(), JSRef<JSFunc>::Cast(builder));
53 NG::ScopedViewStackProcessor builderViewStackProcessor;
54 NG::ViewStackProcessor::GetInstance()->SetIsBuilderNode(true);
55 NG::ViewStackProcessor::GetInstance()->SetIsExportTexture(renderType_ == NodeRenderType::RENDER_TYPE_TEXTURE);
56 if (info.Length() >= 2 && info[1]->IsObject()) {
57 JSRef<JSVal> param = info[1];
58 buildFunc->ExecuteJS(1, ¶m);
59 } else {
60 buildFunc->ExecuteJS();
61 }
62 auto parent = viewNode_ ? viewNode_->GetParent() : nullptr;
63 auto newNode = NG::ViewStackProcessor::GetInstance()->Finish();
64 if (parent) {
65 parent->ReplaceChild(viewNode_, newNode);
66 newNode->MarkNeedFrameFlushDirty(NG::PROPERTY_UPDATE_MEASURE);
67 }
68 viewNode_ = newNode;
69 CHECK_NULL_VOID(viewNode_);
70 if (EXPORT_TEXTURE_SUPPORT_TYPES.count(viewNode_->GetTag()) > 0) {
71 viewNode_->CreateExportTextureInfoIfNeeded();
72 auto exportTextureInfo = viewNode_->GetExportTextureInfo();
73 CHECK_NULL_VOID(exportTextureInfo);
74 exportTextureInfo->SetSurfaceId(surfaceId_);
75 exportTextureInfo->SetCurrentRenderType(renderType_);
76 }
77 viewNode_->Build(nullptr);
78 if (size_.IsValid()) {
79 viewNode_->SetParentLayoutConstraint(size_.ConvertToSizeT());
80 }
81 }
82
Create(const JSCallbackInfo & info)83 void JSBaseNode::Create(const JSCallbackInfo& info)
84 {
85 if (info.Length() >= 1 && !info[0]->IsFunction()) {
86 return;
87 }
88 if ((info.Length() >= 2 && !(info[1]->IsObject() || info[1]->IsUndefined() || info[1]->IsNull()))) {
89 return;
90 }
91 BuildNode(info);
92 EcmaVM* vm = info.GetVm();
93 info.SetReturnValue(JSRef<JSVal>::Make(panda::NativePointerRef::New(vm, AceType::RawPtr(viewNode_))));
94 }
95
CreateRenderNode(const JSCallbackInfo & info)96 void JSBaseNode::CreateRenderNode(const JSCallbackInfo& info)
97 {
98 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
99 std::string nodeTag = "RenderNode";
100 auto frameNode = NG::FrameNode::GetOrCreateFrameNode(
101 nodeTag, nodeId, []() { return AceType::MakeRefPtr<NG::RenderNodePattern>(); });
102 viewNode_ = frameNode;
103 void* ptr = AceType::RawPtr(viewNode_);
104
105 EcmaVM* vm = info.GetVm();
106 auto object = info[0];
107 auto renderNode = JSRef<JSObject>::Cast(info[0]);
108 JSRef<JSVal> jsDrawFunc = renderNode->GetProperty("draw");
109 if (jsDrawFunc->IsFunction()) {
110 auto jsFunc = JSRef<JSFunc>::Cast(jsDrawFunc);
111 RefPtr<JsFunction> jsDraw = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(renderNode), jsFunc);
112 auto pattern = frameNode->GetPattern<NG::RenderNodePattern>();
113 pattern->SetDrawCallback(
114 [func = std::move(jsDraw), execCtx = info.GetExecutionContext(), vm](NG::DrawingContext& context) -> void {
115 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
116
117 JSRef<JSObjTemplate> objectTemplate = JSRef<JSObjTemplate>::New();
118 objectTemplate->SetInternalFieldCount(1);
119 JSRef<JSObject> contextObj = objectTemplate->NewInstance();
120 JSRef<JSObject> sizeObj = objectTemplate->NewInstance();
121 sizeObj->SetProperty<float>("height", PipelineBase::Px2VpWithCurrentDensity(context.height));
122 sizeObj->SetProperty<float>("width", PipelineBase::Px2VpWithCurrentDensity(context.width));
123 contextObj->SetPropertyObject("size", sizeObj);
124
125 auto engine = EngineHelper::GetCurrentEngine();
126 CHECK_NULL_VOID(engine);
127 NativeEngine* nativeEngine = engine->GetNativeEngine();
128 napi_env env = reinterpret_cast<napi_env>(nativeEngine);
129 ScopeRAII scope(env);
130
131 auto jsCanvas =
132 OHOS::Rosen::Drawing::JsCanvas::CreateJsCanvas(env, &context.canvas, context.width, context.height);
133 JsiRef<JsiValue> jsCanvasVal = JsConverter::ConvertNapiValueToJsVal(jsCanvas);
134 contextObj->SetPropertyObject("canvas", jsCanvasVal);
135
136 auto jsVal = JSRef<JSVal>::Cast(contextObj);
137 panda::Local<JsiValue> value = jsVal.Get().GetLocalHandle();
138 JSValueWrapper valueWrapper = value;
139 napi_value nativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
140
141 napi_wrap(
142 env, nativeValue, &context.canvas, [](napi_env, void*, void*) {}, nullptr, nullptr);
143
144 JSRef<JSVal> result = func->ExecuteJS(1, &jsVal);
145 OHOS::Rosen::Drawing::JsCanvas* unwrapCanvas = nullptr;
146 napi_unwrap(env, jsCanvas, reinterpret_cast<void**>(&unwrapCanvas));
147 if (unwrapCanvas) {
148 unwrapCanvas->ResetCanvas();
149 }
150 });
151 }
152 info.SetReturnValue(JSRef<JSVal>::Make(panda::NativePointerRef::New(vm, ptr)));
153 }
154
ConstructorCallback(const JSCallbackInfo & info)155 void JSBaseNode::ConstructorCallback(const JSCallbackInfo& info)
156 {
157 std::string surfaceId;
158 NodeRenderType renderType = NodeRenderType::RENDER_TYPE_DISPLAY;
159 NG::OptionalSizeF selfIdealSize;
160 if (info.Length() > 0 && info[0]->IsObject()) {
161 auto renderOption = JSRef<JSObject>::Cast(info[0]);
162 auto size = renderOption->GetProperty("selfIdealSize");
163 if (size->IsObject()) {
164 auto sizeObj = JSRef<JSObject>::Cast(size);
165 auto width = sizeObj->GetProperty("width");
166 auto widthValue = width->IsNumber() ? width->ToNumber<float>() : 0.0f;
167 widthValue = LessNotEqual(widthValue, 0.0f) ? 0.0f : widthValue;
168 auto height = sizeObj->GetProperty("height");
169 auto heightValue = height->IsNumber() ? height->ToNumber<float>() : 0.0f;
170 heightValue = LessNotEqual(heightValue, 0.0f) ? 0.0f : heightValue;
171 selfIdealSize.SetWidth(PipelineBase::Vp2PxWithCurrentDensity(widthValue));
172 selfIdealSize.SetHeight(PipelineBase::Vp2PxWithCurrentDensity(heightValue));
173 }
174 auto type = renderOption->GetProperty("type");
175 if (type->IsNumber()) {
176 renderType = static_cast<NodeRenderType>(type->ToNumber<uint32_t>());
177 }
178 auto id = renderOption->GetProperty("surfaceId");
179 if (id->IsString()) {
180 surfaceId = id->ToString();
181 }
182 }
183 auto instance = AceType::MakeRefPtr<JSBaseNode>(selfIdealSize, renderType, surfaceId);
184 instance->IncRefCount();
185 info.SetReturnValue(AceType::RawPtr(instance));
186 }
187
DestructorCallback(JSBaseNode * node)188 void JSBaseNode::DestructorCallback(JSBaseNode* node)
189 {
190 if (node != nullptr) {
191 node->DecRefCount();
192 }
193 }
194
FinishUpdateFunc(const JSCallbackInfo & info)195 void JSBaseNode::FinishUpdateFunc(const JSCallbackInfo& info)
196 {
197 NG::ViewStackProcessor::GetInstance()->FlushRerenderTask();
198 }
199
PostTouchEvent(const JSCallbackInfo & info)200 void JSBaseNode::PostTouchEvent(const JSCallbackInfo& info)
201 {
202 if (!viewNode_ || info.Length() < 1 || !info[0]->IsObject()) {
203 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
204 return;
205 }
206 TouchEvent touchEvent;
207 auto obj = JSRef<JSObject>::Cast(info[0]);
208 auto typeJsVal = obj->GetProperty("type");
209 if (typeJsVal->IsNumber()) {
210 touchEvent.type = static_cast<TouchType>(typeJsVal->ToNumber<int32_t>());
211 }
212 auto sourceJsVal = obj->GetProperty("source");
213 if (sourceJsVal->IsNumber()) {
214 touchEvent.sourceType = static_cast<SourceType>((sourceJsVal->ToNumber<int32_t>()));
215 }
216 auto sourceToolJsVal = obj->GetProperty("sourceTool");
217 if (sourceToolJsVal->IsNumber()) {
218 touchEvent.sourceTool = static_cast<SourceTool>((sourceToolJsVal->ToNumber<int32_t>()));
219 }
220 auto pressureJsVal = obj->GetProperty("pressure");
221 if (pressureJsVal->IsNumber()) {
222 touchEvent.force = sourceToolJsVal->ToNumber<float>();
223 }
224 auto timestampJsVal = obj->GetProperty("timestamp");
225 if (timestampJsVal->IsNumber()) {
226 std::chrono::nanoseconds nanoseconds(static_cast<int64_t>(timestampJsVal->ToNumber<double>()));
227 TimeStamp time(nanoseconds);
228 touchEvent.time = time;
229 }
230 auto deviceIdJsVal = obj->GetProperty("deviceId");
231 if (deviceIdJsVal->IsNumber()) {
232 touchEvent.deviceId = deviceIdJsVal->ToNumber<int32_t>();
233 }
234 auto targetDisplayIdJsVal = obj->GetProperty("targetDisplayId");
235 if (targetDisplayIdJsVal->IsNumber()) {
236 touchEvent.targetDisplayId = targetDisplayIdJsVal->ToNumber<int32_t>();
237 }
238 auto touchesJsVal = obj->GetProperty("touches");
239 if (touchesJsVal->IsArray()) {
240 JSRef<JSArray> touchesArray = JSRef<JSArray>::Cast(touchesJsVal);
241 for (auto index = 0; index < static_cast<int32_t>(touchesArray->Length()); index++) {
242 JSRef<JSVal> item = touchesArray->GetValueAt(index);
243 if (!item->IsObject()) {
244 continue;
245 }
246 JSRef<JSObject> itemObj = JSRef<JSObject>::Cast(item);
247 TouchPoint point;
248 point.id = itemObj->GetPropertyValue<int32_t>("id", 0);
249 point.x = itemObj->GetPropertyValue<float>("x", 0.0f);
250 point.y = itemObj->GetPropertyValue<float>("y", 0.0f);
251 point.screenX = itemObj->GetPropertyValue<float>("screenX", 0.0f);
252 point.screenY = itemObj->GetPropertyValue<float>("screenY", 0.0f);
253 touchEvent.pointers.emplace_back(point);
254 }
255 }
256 auto titleXJsVal = obj->GetProperty("tiltX");
257 if (titleXJsVal->IsNumber()) {
258 touchEvent.tiltX = titleXJsVal->ToNumber<float>();
259 }
260 auto titleYJsVal = obj->GetProperty("tiltY");
261 if (titleYJsVal->IsNumber()) {
262 touchEvent.tiltY = titleYJsVal->ToNumber<float>();
263 }
264 auto changedTouchesJsVal = obj->GetProperty("changedTouches");
265 if (changedTouchesJsVal->IsArray()) {
266 JSRef<JSArray> changedTouchesArray = JSRef<JSArray>::Cast(changedTouchesJsVal);
267 if (static_cast<int32_t>(changedTouchesArray->Length()) <= 0) {
268 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
269 return;
270 }
271 JSRef<JSVal> item = changedTouchesArray->GetValueAt(0);
272 if (!item->IsObject()) {
273 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
274 return;
275 }
276 JSRef<JSObject> itemObj = JSRef<JSObject>::Cast(item);
277 touchEvent.id = itemObj->GetPropertyValue<int32_t>("id", 0);
278 touchEvent.x = itemObj->GetPropertyValue<float>("x", 0.0f);
279 touchEvent.y = itemObj->GetPropertyValue<float>("y", 0.0f);
280 touchEvent.screenX = itemObj->GetPropertyValue<float>("screenX", 0.0f);
281 touchEvent.screenY = itemObj->GetPropertyValue<float>("screenY", 0.0f);
282 }
283 auto pipelineContext = NG::PipelineContext::GetCurrentContext();
284 if (!pipelineContext) {
285 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
286 return;
287 }
288 auto postEventManager = pipelineContext->GetPostEventManager();
289 if (!postEventManager) {
290 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
291 return;
292 }
293 auto result = postEventManager->PostEvent(viewNode_, touchEvent);
294 info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(result)));
295 }
296
UpdateStart(const JSCallbackInfo & info)297 void JSBaseNode::UpdateStart(const JSCallbackInfo& info)
298 {
299 scopedViewStackProcessor_ = std::make_unique<NG::ScopedViewStackProcessor>();
300 }
301
UpdateEnd(const JSCallbackInfo & info)302 void JSBaseNode::UpdateEnd(const JSCallbackInfo& info)
303 {
304 if (viewNode_ && size_.IsValid()) {
305 viewNode_->SetParentLayoutConstraint(size_.ConvertToSizeT());
306 }
307 scopedViewStackProcessor_ = nullptr;
308 }
309
JSBind(BindingTarget globalObj)310 void JSBaseNode::JSBind(BindingTarget globalObj)
311 {
312 JSClass<JSBaseNode>::Declare("__JSBaseNode__");
313
314 JSClass<JSBaseNode>::CustomMethod("create", &JSBaseNode::Create);
315 JSClass<JSBaseNode>::CustomMethod("createRenderNode", &JSBaseNode::CreateRenderNode);
316 JSClass<JSBaseNode>::CustomMethod("finishUpdateFunc", &JSBaseNode::FinishUpdateFunc);
317 JSClass<JSBaseNode>::CustomMethod("postTouchEvent", &JSBaseNode::PostTouchEvent);
318 JSClass<JSBaseNode>::CustomMethod("dispose", &JSBaseNode::Dispose);
319 JSClass<JSBaseNode>::CustomMethod("updateStart", &JSBaseNode::UpdateStart);
320 JSClass<JSBaseNode>::CustomMethod("updateEnd", &JSBaseNode::UpdateEnd);
321
322 JSClass<JSBaseNode>::Bind(globalObj, JSBaseNode::ConstructorCallback, JSBaseNode::DestructorCallback);
323 }
324 } // namespace OHOS::Ace::Framework
325