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