1 /*
2 * Copyright (c) 2022-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
16 #include "frameworks/bridge/declarative_frontend/jsview/js_xcomponent_controller.h"
17
18 #include "interfaces/inner_api/ace/ai/image_analyzer.h"
19 #include "js_utils.h"
20
21 #include "base/error/error_code.h"
22 #include "base/memory/referenced.h"
23 #include "base/utils/linear_map.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/engine_helper.h"
26 #include "bridge/declarative_frontend/engine/js_converter.h"
27 #include "core/components/xcomponent/xcomponent_controller_impl.h"
28 #include "core/components_ng/pattern/xcomponent/xcomponent_controller_ng.h"
29 #include "frameworks/bridge/declarative_frontend/jsview/js_view_common_def.h"
30
31 namespace OHOS::Ace::Framework {
32 struct XComponentAsyncCxt {
33 napi_env env = nullptr;
34 napi_deferred deferred = nullptr;
35 };
36 namespace {
ParseSurfaceRectParam(const JSRef<JSVal> & jsValue,CalcDimension & result)37 bool ParseSurfaceRectParam(const JSRef<JSVal>& jsValue, CalcDimension& result)
38 {
39 if (!jsValue->IsNumber()) {
40 return false;
41 }
42 result = CalcDimension(jsValue->ToNumber<double>(), DimensionUnit::PX);
43 return true;
44 }
45
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")46 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
47 {
48 napi_value code = nullptr;
49 std::string codeStr = std::to_string(errCode);
50 napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
51 napi_value msg = nullptr;
52 napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
53 napi_value error = nullptr;
54 napi_create_error(env, code, msg, &error);
55 return error;
56 }
57
HandleDeferred(const shared_ptr<XComponentAsyncCxt> & asyncCtx,ImageAnalyzerState state)58 void HandleDeferred(const shared_ptr<XComponentAsyncCxt>& asyncCtx, ImageAnalyzerState state)
59 {
60 auto env = asyncCtx->env;
61 CHECK_NULL_VOID(env);
62 auto deferred = asyncCtx->deferred;
63 CHECK_NULL_VOID(deferred);
64
65 napi_handle_scope scope = nullptr;
66 auto status = napi_open_handle_scope(env, &scope);
67 if (status != napi_ok) {
68 return;
69 }
70
71 napi_value result = nullptr;
72 switch (state) {
73 case ImageAnalyzerState::UNSUPPORTED:
74 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_UNSUPPORTED);
75 napi_reject_deferred(env, deferred, result);
76 break;
77 case ImageAnalyzerState::ONGOING:
78 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
79 napi_reject_deferred(env, deferred, result);
80 break;
81 case ImageAnalyzerState::STOPPED:
82 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_STOPPED);
83 napi_reject_deferred(env, deferred, result);
84 break;
85 case ImageAnalyzerState::FINISHED:
86 napi_get_null(env, &result);
87 napi_resolve_deferred(env, deferred, result);
88 break;
89 default:
90 break;
91 }
92 napi_close_handle_scope(env, scope);
93 }
94
ReturnPromise(const JSCallbackInfo & info,napi_value result)95 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
96 {
97 CHECK_NULL_VOID(result);
98 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
99 if (!jsPromise->IsObject()) {
100 return;
101 }
102 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
103 }
104 } // namespace
JSBind(BindingTarget globalObj)105 void JSXComponentController::JSBind(BindingTarget globalObj)
106 {
107 JSClass<JSXComponentController>::Declare("XComponentController");
108 JSClass<JSXComponentController>::CustomMethod("getXComponentSurfaceId", &JSXComponentController::GetSurfaceId);
109 JSClass<JSXComponentController>::CustomMethod(
110 "getXComponentContext", &JSXComponentController::GetXComponentContext);
111 JSClass<JSXComponentController>::CustomMethod(
112 "setXComponentSurfaceSize", &JSXComponentController::SetSurfaceConfig);
113 JSClass<JSXComponentController>::CustomMethod(
114 "getXComponentSurfaceRect", &JSXComponentController::GetXComponentSurfaceRect);
115 JSClass<JSXComponentController>::CustomMethod(
116 "setXComponentSurfaceRect", &JSXComponentController::SetXComponentSurfaceRect);
117 JSClass<JSXComponentController>::CustomMethod("startImageAnalyzer", &JSXComponentController::StartImageAnalyzer);
118 JSClass<JSXComponentController>::CustomMethod("stopImageAnalyzer", &JSXComponentController::StopImageAnalyzer);
119 JSClass<JSXComponentController>::CustomMethod(
120 "setXComponentSurfaceRotation", &JSXComponentController::SetXComponentSurfaceRotation);
121 JSClass<JSXComponentController>::CustomMethod(
122 "getXComponentSurfaceRotation", &JSXComponentController::GetXComponentSurfaceRotation);
123 JSClass<JSXComponentController>::Bind(
124 globalObj, JSXComponentController::Constructor, JSXComponentController::Destructor);
125 }
126
Constructor(const JSCallbackInfo & args)127 void JSXComponentController::Constructor(const JSCallbackInfo& args)
128 {
129 auto xcomponentController = Referenced::MakeRefPtr<JSXComponentController>();
130 xcomponentController->IncRefCount();
131 std::shared_ptr<InnerXComponentController> controller;
132 #ifdef NG_BUILD
133 controller = std::make_shared<NG::XComponentControllerNG>();
134 #else
135 if (Container::IsCurrentUseNewPipeline()) {
136 controller = std::make_shared<NG::XComponentControllerNG>();
137 } else {
138 controller = std::make_shared<XComponentControllerImpl>();
139 }
140 #endif
141 xcomponentController->SetController(controller);
142 args.SetReturnValue(Referenced::RawPtr(xcomponentController));
143 }
144
Destructor(JSXComponentController * xcomponentController)145 void JSXComponentController::Destructor(JSXComponentController* xcomponentController)
146 {
147 if (xcomponentController) {
148 xcomponentController->DecRefCount();
149 }
150 }
151
GetSurfaceId(const JSCallbackInfo & args)152 void JSXComponentController::GetSurfaceId(const JSCallbackInfo& args)
153 {
154 if (xcomponentController_) {
155 auto surfaceId = xcomponentController_->GetSurfaceId();
156 auto returnValue = JSVal(ToJSValue(surfaceId));
157 auto returnPtr = JSRef<JSVal>::Make(returnValue);
158 TAG_LOGI(AceLogTag::ACE_XCOMPONENT, "Controller GetSurfaceId:%{public}s", surfaceId.c_str());
159 args.SetReturnValue(returnPtr);
160 }
161 }
162
SetSurfaceConfig(const JSCallbackInfo & args)163 void JSXComponentController::SetSurfaceConfig(const JSCallbackInfo& args)
164 {
165 if (args.Length() < 1 || !args[0]->IsObject()) {
166 LOGW("Invalid params");
167 return;
168 }
169
170 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
171 uint32_t surfaceWidth;
172 uint32_t surfaceHeight;
173 if (!ConvertFromJSValue(obj->GetProperty("surfaceWidth"), surfaceWidth) ||
174 !ConvertFromJSValue(obj->GetProperty("surfaceHeight"), surfaceHeight)) {
175 LOGW("Failed to parse param 'surfaceWidth' or 'surfaceHeight'");
176 return;
177 }
178
179 if (xcomponentController_) {
180 xcomponentController_->ConfigSurface(surfaceWidth, surfaceHeight);
181 }
182 }
183
GetXComponentSurfaceRect(const JSCallbackInfo & args)184 void JSXComponentController::GetXComponentSurfaceRect(const JSCallbackInfo& args)
185 {
186 if (!xcomponentController_) {
187 return;
188 }
189 auto retObj = JSRef<JSObject>::New();
190 float offsetX = 0.0f;
191 float offsetY = 0.0f;
192 float width = 0.0f;
193 float height = 0.0f;
194 xcomponentController_->GetSurfaceOffset(offsetX, offsetY);
195 xcomponentController_->GetSurfaceSize(width, height);
196 retObj->SetProperty("offsetX", offsetX);
197 retObj->SetProperty("offsetY", offsetY);
198 retObj->SetProperty("surfaceWidth", width);
199 retObj->SetProperty("surfaceHeight", height);
200 args.SetReturnValue(retObj);
201 }
202
SetXComponentSurfaceRect(const JSCallbackInfo & args)203 void JSXComponentController::SetXComponentSurfaceRect(const JSCallbackInfo& args)
204 {
205 if (args.Length() < 1 || !args[0]->IsObject()) {
206 return;
207 }
208 if (!xcomponentController_) {
209 return;
210 }
211
212 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
213 auto jsSurfaceWidth = obj->GetProperty("surfaceWidth");
214 CalcDimension surfaceWidth;
215 if (!ParseSurfaceRectParam(jsSurfaceWidth, surfaceWidth) || !surfaceWidth.IsValid()) {
216 return;
217 }
218 auto jsSurfaceHeight = obj->GetProperty("surfaceHeight");
219 CalcDimension surfaceHeight;
220 if (!ParseSurfaceRectParam(jsSurfaceHeight, surfaceHeight) || !surfaceHeight.IsValid()) {
221 return;
222 }
223 xcomponentController_->SetIdealSurfaceWidth(static_cast<float>(surfaceWidth.ConvertToPx()));
224 xcomponentController_->SetIdealSurfaceHeight(static_cast<float>(surfaceHeight.ConvertToPx()));
225
226 auto jsOffsetX = obj->GetProperty("offsetX");
227 CalcDimension offsetX;
228 if (ParseSurfaceRectParam(jsOffsetX, offsetX)) {
229 xcomponentController_->SetIdealSurfaceOffsetX(static_cast<float>(offsetX.ConvertToPx()));
230 } else {
231 xcomponentController_->ClearIdealSurfaceOffset(true);
232 }
233 auto jsOffsetY = obj->GetProperty("offsetY");
234 CalcDimension offsetY;
235 if (ParseSurfaceRectParam(jsOffsetY, offsetY)) {
236 xcomponentController_->SetIdealSurfaceOffsetY(static_cast<float>(offsetY.ConvertToPx()));
237 } else {
238 xcomponentController_->ClearIdealSurfaceOffset(false);
239 }
240
241 xcomponentController_->UpdateSurfaceBounds();
242 }
243
StartImageAnalyzer(const JSCallbackInfo & args)244 void JSXComponentController::StartImageAnalyzer(const JSCallbackInfo& args)
245 {
246 ContainerScope scope(instanceId_);
247 auto engine = EngineHelper::GetCurrentEngine();
248 CHECK_NULL_VOID(engine);
249 NativeEngine* nativeEngine = engine->GetNativeEngine();
250 auto env = reinterpret_cast<napi_env>(nativeEngine);
251
252 auto asyncCtx = std::make_shared<XComponentAsyncCxt>();
253 asyncCtx->env = env;
254 napi_value promise = nullptr;
255 napi_create_promise(env, &asyncCtx->deferred, &promise);
256 if (args.Length() < 1 || !args[0]->IsObject()) {
257 ReturnPromise(args, promise);
258 return;
259 }
260
261 ScopeRAII scopeRaii(env);
262 panda::Local<JsiValue> value = args[0].Get().GetLocalHandle();
263 JSValueWrapper valueWrapper = value;
264 napi_value configNativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
265 if (isImageAnalyzing_) {
266 napi_value result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
267 napi_reject_deferred(env, asyncCtx->deferred, result);
268 ReturnPromise(args, promise);
269 return;
270 }
271
272 OnAnalyzedCallback onAnalyzed_ = [asyncCtx, weakCtx = WeakClaim(this)](ImageAnalyzerState state) {
273 CHECK_NULL_VOID(asyncCtx);
274 HandleDeferred(asyncCtx, state);
275 auto ctx = weakCtx.Upgrade();
276 CHECK_NULL_VOID(ctx);
277 ctx->isImageAnalyzing_ = false;
278 };
279 isImageAnalyzing_ = true;
280 CHECK_NULL_VOID(xcomponentController_);
281 xcomponentController_->StartImageAnalyzer(configNativeValue, onAnalyzed_);
282 ReturnPromise(args, promise);
283 }
284
StopImageAnalyzer(const JSCallbackInfo & args)285 void JSXComponentController::StopImageAnalyzer(const JSCallbackInfo& args)
286 {
287 ContainerScope scope(instanceId_);
288 CHECK_NULL_VOID(xcomponentController_);
289 xcomponentController_->StopImageAnalyzer();
290 }
291
SetXComponentSurfaceRotation(const JSCallbackInfo & args)292 void JSXComponentController::SetXComponentSurfaceRotation(const JSCallbackInfo& args)
293 {
294 if (!args[0]->IsObject()) {
295 return;
296 }
297
298 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
299 bool lock = false;
300 ConvertFromJSValue(obj->GetProperty("lock"), lock);
301 CHECK_NULL_VOID(xcomponentController_);
302 xcomponentController_->SetSurfaceRotation(lock);
303 }
304
GetXComponentSurfaceRotation(const JSCallbackInfo & args)305 void JSXComponentController::GetXComponentSurfaceRotation(const JSCallbackInfo& args)
306 {
307 auto retObj = JSRef<JSObject>::New();
308 CHECK_NULL_VOID(xcomponentController_);
309 bool lock = xcomponentController_->GetSurfaceRotation();
310 retObj->SetProperty("lock", lock);
311 args.SetReturnValue(retObj);
312 }
313 } // namespace OHOS::Ace::Framework
314