1 /*
2 * Copyright (c) 2025-2025 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 "js_fb_window_controller.h"
16
17 #include <memory>
18
19 #include "color_parser.h"
20 #include "floating_ball_manager.h"
21 #include "js_err_utils.h"
22 #include "js_fb_utils.h"
23 #include "napi_common_want.h"
24 #include "pixel_map_napi.h"
25 #include "permission.h"
26 #include "window_manager_hilog.h"
27
28 namespace OHOS {
29 namespace Rosen {
30 using namespace AbilityRuntime;
31 namespace {
32 const std::string STATE_CHANGE_CB = "stateChange";
33 const std::string CLICK_EVENT = "click";
34 const std::string FLOATING_BALL_PERMISSION = "ohos.permission.USE_FLOAT_BALL";
35 constexpr uint32_t TITLE_MIN_LEN = 1;
36 constexpr uint32_t TITLE_MAX_LEN = 64;
37 constexpr uint32_t CONTENT_MAX_LEN = 64;
38 constexpr int32_t PIXEL_MAP_MAX_SIZE = 192 * 1024;
39 constexpr int32_t NUMBER_TWO = 2;
40 }
41
BindFunctions(napi_env env,napi_value object,const char * moduleName)42 void BindFunctions(napi_env env, napi_value object, const char* moduleName)
43 {
44 BindNativeFunction(env, object, "startFloatingBall", moduleName, JsFbController::StartFloatingBall);
45 BindNativeFunction(env, object, "updateFloatingBall", moduleName, JsFbController::UpdateFloatingBall);
46 BindNativeFunction(env, object, "stopFloatingBall", moduleName, JsFbController::StopFloatingBall);
47 BindNativeFunction(env, object, "restoreMainWindow", moduleName, JsFbController::RestoreMainWindow);
48 BindNativeFunction(env, object, "on", moduleName, JsFbController::RegisterCallback);
49 BindNativeFunction(env, object, "off", moduleName, JsFbController::UnregisterCallback);
50 BindNativeFunction(env, object, "getFloatingBallWindowInfo", moduleName, JsFbController::GetFloatingBallWindowInfo);
51 }
52
CreateJsFbControllerObject(napi_env env,const sptr<FloatingBallController> & fbController)53 napi_value CreateJsFbControllerObject(napi_env env, const sptr<FloatingBallController>& fbController)
54 {
55 napi_value objValue = nullptr;
56 napi_status status = napi_create_object(env, &objValue);
57 if (status != napi_ok || objValue == nullptr) {
58 TLOGE(WmsLogTag::WMS_SYSTEM, "failed to create js obj, error:%{public}d", status);
59 return NapiGetUndefined(env);
60 }
61
62 TLOGI(WmsLogTag::WMS_SYSTEM, "CreateJsFbController");
63 std::unique_ptr<JsFbController> jsFbController = std::make_unique<JsFbController>(fbController);
64 napi_wrap(env, objValue, jsFbController.release(), JsFbController::Finalizer, nullptr, nullptr);
65
66 BindFunctions(env, objValue, "JsFbController");
67 return objValue;
68 }
69
JsFbController(const sptr<FloatingBallController> & fbController)70 JsFbController::JsFbController(const sptr<FloatingBallController>& fbController)
71 : fbController_(fbController)
72 {
73 }
74
~JsFbController()75 JsFbController::~JsFbController()
76 {
77 TLOGI(WmsLogTag::WMS_SYSTEM, "JsFbController release");
78 }
79
Finalizer(napi_env env,void * data,void * hint)80 void JsFbController::Finalizer(napi_env env, void* data, void* hint)
81 {
82 TLOGD(WmsLogTag::WMS_SYSTEM, "Finalizer is called");
83 std::unique_ptr<JsFbController>(static_cast<JsFbController*>(data));
84 }
85
StartFloatingBall(napi_env env,napi_callback_info info)86 napi_value JsFbController::StartFloatingBall(napi_env env, napi_callback_info info)
87 {
88 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
89 return (me != nullptr) ? me->OnStartFloatingBall(env, info) : nullptr;
90 }
91
OnStartFloatingBall(napi_env env,napi_callback_info info)92 napi_value JsFbController::OnStartFloatingBall(napi_env env, napi_callback_info info)
93 {
94 TLOGI(WmsLogTag::WMS_SYSTEM, "OnStartFloatingBall is called");
95 size_t argc = 1;
96 napi_value argv[1] = {nullptr};
97 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
98 if (argc < 1) {
99 return NapiThrowInvalidParam(env, "Missing args when start floating ball");
100 }
101
102 napi_value config = argv[0];
103 if (config == nullptr) {
104 TLOGE(WmsLogTag::WMS_SYSTEM, "config is null");
105 return NapiThrowInvalidParam(env,
106 "Failed to convert object to FloatingBallOption or FloatingBallOption is null");
107 }
108
109 FbOption option;
110 if (GetFloatingBallOptionFromJs(env, config, option) == nullptr) {
111 return NapiGetUndefined(env);
112 }
113 return StartFloatingBallTask(env, option);
114 }
115
StartFloatingBallTask(napi_env env,const FbOption & option)116 napi_value JsFbController::StartFloatingBallTask(napi_env env, const FbOption& option)
117 {
118 wptr<FloatingBallController> weakController(fbController_);
119 std::shared_ptr<WmErrorCode> errCodePtr = std::make_shared<WmErrorCode>(WmErrorCode::WM_OK);
120 NapiAsyncTask::ExecuteCallback execute = [weakController, option, errCodePtr] {
121 if (errCodePtr == nullptr) {
122 return;
123 }
124 if (!Permission::CheckCallingPermission(FLOATING_BALL_PERMISSION)) {
125 *errCodePtr = WmErrorCode::WM_ERROR_NO_PERMISSION;
126 return;
127 }
128 auto fbController = weakController.promote();
129 if (fbController == nullptr) {
130 *errCodePtr = WmErrorCode::WM_ERROR_FB_STATE_ABNORMALLY;
131 return;
132 }
133 sptr<FbOption> optionPtr = sptr<FbOption>::MakeSptr(option);
134 *errCodePtr = ConvertErrorToCode(fbController->StartFloatingBall(optionPtr));
135 };
136 NapiAsyncTask::CompleteCallback complete =
137 [errCodePtr](napi_env env, NapiAsyncTask& task, int32_t status) {
138 if (errCodePtr == nullptr) {
139 task.Reject(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR));
140 return;
141 }
142 if (*errCodePtr == WmErrorCode::WM_OK) {
143 task.Resolve(env, NapiGetUndefined(env));
144 } else {
145 task.Reject(env, JsErrUtils::CreateJsError(env, *errCodePtr,
146 "JsFbController::OnStartFloatingBall failed."));
147 }
148 };
149 napi_value result = nullptr;
150 NapiAsyncTask::Schedule("JsFbController::OnStartFloatingBall",
151 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
152 return result;
153 }
154
UpdateFloatingBall(napi_env env,napi_callback_info info)155 napi_value JsFbController::UpdateFloatingBall(napi_env env, napi_callback_info info)
156 {
157 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
158 return (me != nullptr) ? me->OnUpdateFloatingBall(env, info) : nullptr;
159 }
160
OnUpdateFloatingBall(napi_env env,napi_callback_info info)161 napi_value JsFbController::OnUpdateFloatingBall(napi_env env, napi_callback_info info)
162 {
163 TLOGI(WmsLogTag::WMS_SYSTEM, "OnUpdateFloatingBall is called");
164 size_t argc = 1;
165 napi_value argv[1] = {nullptr};
166 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
167 if (argc < 1) {
168 return NapiThrowInvalidParam(env, "Missing args when update floating ball");
169 }
170
171 napi_value config = argv[0];
172 if (config == nullptr) {
173 TLOGE(WmsLogTag::WMS_SYSTEM, "config is null");
174 return NapiThrowInvalidParam(env,
175 "Failed to convert object to FloatingBallOption or FloatingBallOption is null");
176 }
177
178 FbOption option;
179 if (GetFloatingBallOptionFromJs(env, config, option) == nullptr) {
180 return NapiGetUndefined(env);
181 }
182
183 wptr<FloatingBallController> weakController(fbController_);
184 std::shared_ptr<WmErrorCode> errCodePtr = std::make_shared<WmErrorCode>(WmErrorCode::WM_OK);
185 NapiAsyncTask::ExecuteCallback execute = [weakController, option, errCodePtr] {
186 if (errCodePtr == nullptr) {
187 return;
188 }
189 auto fbController = weakController.promote();
190 if (fbController == nullptr) {
191 *errCodePtr = WmErrorCode::WM_ERROR_FB_STATE_ABNORMALLY;
192 return;
193 }
194 sptr<FbOption> optionPtr = sptr<FbOption>::MakeSptr(option);
195 *errCodePtr = ConvertErrorToCode(fbController->UpdateFloatingBall(optionPtr));
196 };
197 NapiAsyncTask::CompleteCallback complete =
198 [errCodePtr](napi_env env, NapiAsyncTask& task, int32_t status) {
199 if (errCodePtr == nullptr) {
200 task.Reject(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR));
201 return;
202 }
203 if (*errCodePtr == WmErrorCode::WM_OK) {
204 task.Resolve(env, NapiGetUndefined(env));
205 } else {
206 task.Reject(env, JsErrUtils::CreateJsError(env, *errCodePtr,
207 "JsFbController::OnUpdateFloatingBall failed."));
208 }
209 };
210 napi_value result = nullptr;
211 NapiAsyncTask::Schedule("JsFbController::OnUpdateFloatingBall",
212 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
213 return result;
214 }
215
StopFloatingBall(napi_env env,napi_callback_info info)216 napi_value JsFbController::StopFloatingBall(napi_env env, napi_callback_info info)
217 {
218 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
219 return (me != nullptr) ? me->OnStopFloatingBall(env, info) : nullptr;
220 }
221
OnStopFloatingBall(napi_env env,napi_callback_info info)222 napi_value JsFbController::OnStopFloatingBall(napi_env env, napi_callback_info info)
223 {
224 TLOGI(WmsLogTag::WMS_SYSTEM, "OnStopFloatingBall");
225 wptr<FloatingBallController> weakController(fbController_);
226 std::shared_ptr<WmErrorCode> errCodePtr = std::make_shared<WmErrorCode>(WmErrorCode::WM_OK);
227 NapiAsyncTask::ExecuteCallback execute = [weakController, errCodePtr] {
228 if (errCodePtr == nullptr) {
229 return;
230 }
231 auto fbController = weakController.promote();
232 if (fbController == nullptr) {
233 *errCodePtr = WmErrorCode::WM_ERROR_FB_STATE_ABNORMALLY;
234 return;
235 }
236 *errCodePtr = ConvertErrorToCode(fbController->StopFloatingBallFromClient());
237 };
238 NapiAsyncTask::CompleteCallback complete =
239 [errCodePtr](napi_env env, NapiAsyncTask& task, int32_t status) {
240 if (errCodePtr == nullptr) {
241 task.Reject(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR));
242 return;
243 }
244 if (*errCodePtr == WmErrorCode::WM_OK) {
245 task.Resolve(env, NapiGetUndefined(env));
246 } else {
247 task.Reject(env, JsErrUtils::CreateJsError(env, *errCodePtr,
248 "JsFbController::OnStopFloatingBall failed."));
249 }
250 };
251 napi_value result = nullptr;
252 NapiAsyncTask::Schedule("JsFbController::OnStopFloatingBall",
253 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
254 return result;
255 }
256
RestoreMainWindow(napi_env env,napi_callback_info info)257 napi_value JsFbController::RestoreMainWindow(napi_env env, napi_callback_info info)
258 {
259 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
260 return (me != nullptr) ? me->OnRestoreMainWindow(env, info) : nullptr;
261 }
262
OnRestoreMainWindow(napi_env env,napi_callback_info info)263 napi_value JsFbController::OnRestoreMainWindow(napi_env env, napi_callback_info info)
264 {
265 TLOGI(WmsLogTag::WMS_SYSTEM, "OnRestoreMainWindow");
266 size_t argc = 1;
267 napi_value argv[1] = {nullptr};
268 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
269 if (argc < 1) {
270 return NapiThrowInvalidParam(env, "Missing args when restore main window");
271 }
272
273 napi_value wantValue = argv[0];
274 if (wantValue == nullptr) {
275 TLOGE(WmsLogTag::WMS_SYSTEM, "want is null");
276 return NapiThrowInvalidParam(env, "want is null");
277 }
278
279 AAFwk::Want want;
280 if (!AppExecFwk::UnwrapWant(env, wantValue, want)) {
281 TLOGE(WmsLogTag::WMS_SYSTEM, "unWrap want failed.");
282 return NapiThrowInvalidParam(env, "Incorrect parameter, parameter must be want.");
283 }
284
285 std::shared_ptr<AAFwk::Want> abilityWant = std::make_shared<AAFwk::Want>(want);
286 wptr<FloatingBallController> weakController(fbController_);
287 std::shared_ptr<WmErrorCode> errCodePtr = std::make_shared<WmErrorCode>(WmErrorCode::WM_OK);
288 NapiAsyncTask::ExecuteCallback execute = [weakController, abilityWant, errCodePtr] {
289 if (errCodePtr == nullptr) {
290 return;
291 }
292 auto fbController = weakController.promote();
293 if (fbController == nullptr) {
294 *errCodePtr = WmErrorCode::WM_ERROR_FB_STATE_ABNORMALLY;
295 return;
296 }
297 *errCodePtr = ConvertErrorToCode(fbController->RestoreMainWindow(abilityWant));
298 };
299 NapiAsyncTask::CompleteCallback complete =
300 [errCodePtr](napi_env env, NapiAsyncTask& task, int32_t status) {
301 if (errCodePtr == nullptr) {
302 task.Reject(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR));
303 return;
304 }
305 if (*errCodePtr == WmErrorCode::WM_OK) {
306 task.Resolve(env, NapiGetUndefined(env));
307 } else {
308 task.Reject(env, JsErrUtils::CreateJsError(env, *errCodePtr,
309 "JsFbController::OnRestoreMainWindow failed."));
310 }
311 };
312 napi_value result = nullptr;
313 NapiAsyncTask::Schedule("JsFbController::OnRestoreMainWindow",
314 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
315 return result;
316 }
317
GetFloatingBallOptionFromJs(napi_env env,napi_value optionObject,FbOption & option)318 napi_value JsFbController::GetFloatingBallOptionFromJs(napi_env env, napi_value optionObject, FbOption& option)
319 {
320 napi_value templateValue = nullptr;
321 napi_value titleValue = nullptr;
322 napi_value contentValue = nullptr;
323 napi_value colorValue = nullptr;
324
325 uint32_t templateType = 0;
326 std::string title = "";
327 std::string content = "";
328 std::string color = "";
329 bool hasProperty = false;
330 napi_has_named_property(env, optionObject, "template", &hasProperty);
331 if (hasProperty) {
332 napi_get_named_property(env, optionObject, "template", &templateValue);
333 ConvertFromJsValue(env, templateValue, templateType);
334 option.SetTemplate(templateType);
335 }
336 napi_has_named_property(env, optionObject, "title", &hasProperty);
337 if (hasProperty) {
338 napi_get_named_property(env, optionObject, "title", &titleValue);
339 ConvertFromJsValue(env, titleValue, title);
340 option.SetTitle(title);
341 }
342 napi_has_named_property(env, optionObject, "content", &hasProperty);
343 if (hasProperty) {
344 napi_get_named_property(env, optionObject, "content", &contentValue);
345 ConvertFromJsValue(env, contentValue, content);
346 option.SetContent(content);
347 }
348 napi_has_named_property(env, optionObject, "backgroundColor", &hasProperty);
349 if (hasProperty) {
350 napi_get_named_property(env, optionObject, "backgroundColor", &colorValue);
351 ConvertFromJsValue(env, colorValue, color);
352 option.SetBackgroundColor(color);
353 }
354 if (GetIcon(env, optionObject, option) == nullptr) {
355 napi_throw(env, AbilityRuntime::CreateJsError(env,
356 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "Invalid icon object"));
357 return nullptr;
358 }
359 return CheckParams(env, option);
360 }
361
CheckParams(napi_env env,const FbOption & option)362 napi_value JsFbController::CheckParams(napi_env env, const FbOption& option)
363 {
364 if (option.GetTemplate() < static_cast<uint32_t>(FloatingBallTemplate::STATIC) ||
365 option.GetTemplate() >= static_cast<uint32_t>(FloatingBallTemplate::END)) {
366 TLOGE(WmsLogTag::WMS_SYSTEM, "template %{public}d is invalid.", option.GetTemplate());
367 napi_throw(env, AbilityRuntime::CreateJsError(env,
368 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "template is invalid."));
369 return nullptr;
370 }
371 if (option.GetTitle().length() < TITLE_MIN_LEN || option.GetTitle().length() > TITLE_MAX_LEN) {
372 TLOGE(WmsLogTag::WMS_SYSTEM, "title length Exceed the limit %{public}zu.", option.GetTitle().length());
373 napi_throw(env, AbilityRuntime::CreateJsError(env,
374 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "title length Exceed the limit"));
375 return nullptr;
376 }
377 if (option.GetContent().length() > CONTENT_MAX_LEN) {
378 TLOGE(WmsLogTag::WMS_SYSTEM, "content length Exceed the limit %{public}zu.", option.GetContent().length());
379 napi_throw(env, AbilityRuntime::CreateJsError(env,
380 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "content length Exceed the limit"));
381 return nullptr;
382 }
383 if (option.GetIcon() != nullptr && option.GetIcon()->GetByteCount() > PIXEL_MAP_MAX_SIZE) {
384 TLOGE(WmsLogTag::WMS_SYSTEM, "icon size Exceed the limit %{public}d.", option.GetIcon()->GetByteCount());
385 napi_throw(env, AbilityRuntime::CreateJsError(env,
386 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "icon size Exceed the limit"));
387 return nullptr;
388 }
389 if (!option.GetBackgroundColor().empty() && !ColorParser::IsValidColorNoAlpha(option.GetBackgroundColor())) {
390 TLOGE(WmsLogTag::WMS_SYSTEM, "backgroundColor is invalid %{public}s.", option.GetBackgroundColor().c_str());
391 napi_throw(env, AbilityRuntime::CreateJsError(env,
392 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "backgroundColor is invalid"));
393 return nullptr;
394 }
395 if (option.GetTemplate() == static_cast<uint32_t>(FloatingBallTemplate::STATIC) &&
396 option.GetIcon() == nullptr) {
397 TLOGE(WmsLogTag::WMS_SYSTEM, "template %{public}u need icon.", option.GetTemplate());
398 napi_throw(env, AbilityRuntime::CreateJsError(env,
399 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_PARAM_INVALID), "current template need icon"));
400 return nullptr;
401 }
402 return NapiGetUndefined(env);
403 }
404
GetIcon(napi_env env,const napi_value value,FbOption & option)405 napi_value JsFbController::GetIcon(napi_env env, const napi_value value, FbOption& option)
406 {
407 napi_valuetype valuetype = napi_undefined;
408 napi_value result = nullptr;
409 bool hasProperty = false;
410
411 napi_has_named_property(env, value, "icon", &hasProperty);
412 if (hasProperty) {
413 napi_get_named_property(env, value, "icon", &result);
414 napi_typeof(env, result, &valuetype);
415 if (valuetype != napi_object) {
416 TLOGE(WmsLogTag::WMS_SYSTEM, "Wrong argument type. Object expected.");
417 return nullptr;
418 }
419 std::shared_ptr<Media::PixelMap> pixelMap = nullptr;
420 pixelMap = Media::PixelMapNapi::GetPixelMap(env, result);
421 if (pixelMap == nullptr) {
422 TLOGE(WmsLogTag::WMS_SYSTEM, "Invalid object pixelMap");
423 return nullptr;
424 }
425 option.SetIcon(pixelMap);
426 }
427 return NapiGetUndefined(env);
428 }
429
RegisterCallback(napi_env env,napi_callback_info info)430 napi_value JsFbController::RegisterCallback(napi_env env, napi_callback_info info)
431 {
432 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
433 return (me != nullptr) ? me->OnRegisterCallback(env, info) : nullptr;
434 }
435
OnRegisterCallback(napi_env env,napi_callback_info info)436 napi_value JsFbController::OnRegisterCallback(napi_env env, napi_callback_info info)
437 {
438 TLOGI(WmsLogTag::WMS_SYSTEM, "OnRegisterCallback is called");
439 size_t argc = NUMBER_TWO;
440 napi_value argv[NUMBER_TWO] = {nullptr};
441 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
442 if (argc < NUMBER_TWO) {
443 TLOGE(WmsLogTag::WMS_SYSTEM, "OnRegisterCallback Params not match: %{public}zu", argc);
444 return NapiThrowInvalidParam(env, "OnRegisterCallback Params not match");
445 }
446 std::string cbType = "";
447 if (!ConvertFromJsValue(env, argv[0], cbType)) {
448 TLOGE(WmsLogTag::WMS_SYSTEM, "Failed to convert parameter to callbackType");
449 return NapiThrowInvalidParam(env, "Failed to convert parameter to callbackType");
450 }
451 napi_value value = argv[1];
452 if (value == nullptr || !NapiIsCallable(env, value)) {
453 TLOGE(WmsLogTag::WMS_SYSTEM, "Callback is nullptr or not callable");
454 return NapiThrowInvalidParam(env, "Callback is nullptr or not callable");
455 }
456 return RegisterListenerWithType(env, cbType, value);
457 }
458
RegisterListenerWithType(napi_env env,const std::string & type,napi_value value)459 napi_value JsFbController::RegisterListenerWithType(napi_env env, const std::string& type, napi_value value)
460 {
461 std::lock_guard<std::mutex> lock(callbBackMutex_);
462 if (IsCallbackRegistered(env, type, value)) {
463 TLOGE(WmsLogTag::WMS_SYSTEM, "Callback already registered");
464 napi_throw(env, AbilityRuntime::CreateJsError(env,
465 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_REPEAT_OPERATION), "Callback already registered"));
466 return NapiGetUndefined(env);
467 }
468 std::shared_ptr<NativeReference> callbackRef;
469 napi_ref result = nullptr;
470 napi_create_reference(env, value, 1, &result);
471 callbackRef.reset(reinterpret_cast<NativeReference*>(result));
472 auto fbWindowListener = sptr<JsFbWindowListener>::MakeSptr(env, callbackRef);
473 if (fbWindowListener == nullptr) {
474 TLOGE(WmsLogTag::WMS_SYSTEM, "New JsFbWindowListener failed");
475 napi_throw(env, AbilityRuntime::CreateJsError(env,
476 static_cast<int32_t>(WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR), "New JsFbWindowListener failed"));
477 return NapiGetUndefined(env);
478 }
479 WMError ret = WMError::WM_OK;
480 if (type == STATE_CHANGE_CB) {
481 ret = ProcessStateChangeRegister(fbWindowListener);
482 }
483 if (type == CLICK_EVENT) {
484 ret = ProcessClickEventRegister(fbWindowListener);
485 }
486 if (ret != WMError::WM_OK) {
487 TLOGE(WmsLogTag::WMS_SYSTEM, "register failed");
488 napi_throw(env, AbilityRuntime::CreateJsError(env,
489 static_cast<int32_t>(ConvertErrorToCode(ret)), "register failed"));
490 return NapiGetUndefined(env);
491 }
492 jsCbMap_[type].insert(fbWindowListener);
493 TLOGI(WmsLogTag::WMS_SYSTEM, "Register type %{public}s success! callback map size: %{public}zu",
494 type.c_str(), jsCbMap_[type].size());
495 return NapiGetUndefined(env);
496 }
497
IsCallbackRegistered(napi_env env,const std::string & type,napi_value jsListenerObject)498 bool JsFbController::IsCallbackRegistered(napi_env env, const std::string& type, napi_value jsListenerObject)
499 {
500 if (jsCbMap_.empty()) {
501 TLOGI(WmsLogTag::WMS_SYSTEM, "callback empty, methodName %{public}s not registered", type.c_str());
502 return false;
503 }
504 auto callback = jsCbMap_.find(type);
505 if (callback == jsCbMap_.end()) {
506 TLOGI(WmsLogTag::WMS_SYSTEM, "methodName %{public}s not registered", type.c_str());
507 return false;
508 }
509 for (auto& listener : callback->second) {
510 if (listener == nullptr || listener->GetCallbackRef() == nullptr) {
511 TLOGI(WmsLogTag::WMS_SYSTEM, "listener or listener callback is null");
512 continue;
513 }
514 bool isEquals = false;
515 napi_strict_equals(env, jsListenerObject, listener->GetCallbackRef()->GetNapiValue(), &isEquals);
516 if (isEquals) {
517 TLOGE(WmsLogTag::WMS_SYSTEM, "Callback already registered");
518 return true;
519 }
520 }
521 return false;
522 }
523
ProcessStateChangeRegister(const sptr<JsFbWindowListener> & listener)524 WMError JsFbController::ProcessStateChangeRegister(const sptr<JsFbWindowListener>& listener)
525 {
526 if (fbController_ == nullptr) {
527 TLOGE(WmsLogTag::WMS_SYSTEM, "controller is nullptr");
528 return WMError::WM_ERROR_FB_STATE_ABNORMALLY;
529 }
530 return fbController_->RegisterFbLifecycle(listener);
531 }
532
ProcessClickEventRegister(const sptr<JsFbWindowListener> & listener)533 WMError JsFbController::ProcessClickEventRegister(const sptr<JsFbWindowListener>& listener)
534 {
535 if (fbController_ == nullptr) {
536 TLOGE(WmsLogTag::WMS_SYSTEM, "controller is nullptr");
537 return WMError::WM_ERROR_FB_STATE_ABNORMALLY;
538 }
539 return fbController_->RegisterFbClickObserver(listener);
540 }
541
UnregisterCallback(napi_env env,napi_callback_info info)542 napi_value JsFbController::UnregisterCallback(napi_env env, napi_callback_info info)
543 {
544 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
545 return (me != nullptr) ? me->OnUnregisterCallback(env, info) : nullptr;
546 }
547
OnUnregisterCallback(napi_env env,napi_callback_info info)548 napi_value JsFbController::OnUnregisterCallback(napi_env env, napi_callback_info info)
549 {
550 size_t argc = NUMBER_TWO;
551 napi_value argv[NUMBER_TWO] = {nullptr};
552 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
553 if (argc <= 0) {
554 TLOGE(WmsLogTag::WMS_SYSTEM, "JsFbController Params not match: %{public}zu", argc);
555 return NapiThrowInvalidParam(env, "Params num not match");
556 }
557 std::string cbType = "";
558 if (!ConvertFromJsValue(env, argv[0], cbType)) {
559 TLOGE(WmsLogTag::WMS_SYSTEM, "Failed to convert parameter to string");
560 return NapiThrowInvalidParam(env, "Failed to convert parameter to string");
561 }
562 if (argc == 1) {
563 return UnRegisterListenerWithType(env, cbType, nullptr);
564 }
565 napi_value value = argv[1];
566 if (value == nullptr || !NapiIsCallable(env, value)) {
567 return NapiThrowInvalidParam(env, "callBack is invalid");
568 }
569 return UnRegisterListenerWithType(env, cbType, value);
570 }
571
UnRegisterListenerWithType(napi_env env,const std::string & type,napi_value value)572 napi_value JsFbController::UnRegisterListenerWithType(napi_env env, const std::string& type, napi_value value)
573 {
574 std::lock_guard<std::mutex> lock(callbBackMutex_);
575 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
576 TLOGI(WmsLogTag::WMS_SYSTEM, "methodName %{public}s not registered", type.c_str());
577 return NapiGetUndefined(env);
578 }
579
580 if (value == nullptr) {
581 TLOGI(WmsLogTag::WMS_SYSTEM, "methodName %{public}s start unregister all callback", type.c_str());
582 for (auto& listener : jsCbMap_[type]) {
583 WmErrorCode ret = UnRegisterListener(type, listener);
584 if (ret != WmErrorCode::WM_OK) {
585 TLOGE(WmsLogTag::WMS_SYSTEM, "Unregister type %{public}s failed, no value", type.c_str());
586 napi_throw(env, AbilityRuntime::CreateJsError(env,
587 static_cast<int32_t>(ret), "unRegister failed"));
588 return NapiGetUndefined(env);
589 }
590 }
591 jsCbMap_.erase(type);
592 } else {
593 TLOGI(WmsLogTag::WMS_SYSTEM, "methodName %{public}s start unregister one callback", type.c_str());
594 bool foundCallbackValue = false;
595 for (auto& listener : jsCbMap_[type]) {
596 bool isEquals = false;
597 napi_strict_equals(env, value, listener->GetCallbackRef()->GetNapiValue(), &isEquals);
598 if (!isEquals) {
599 continue;
600 }
601 foundCallbackValue = true;
602 WmErrorCode ret = UnRegisterListener(type, listener);
603 if (ret != WmErrorCode::WM_OK) {
604 TLOGE(WmsLogTag::WMS_SYSTEM, "Unregister type %{public}s failed", type.c_str());
605 napi_throw(env, AbilityRuntime::CreateJsError(env, static_cast<int32_t>(ret), "unRegister failed"));
606 return NapiGetUndefined(env);
607 }
608 jsCbMap_[type].erase(listener);
609 break;
610 }
611 if (!foundCallbackValue) {
612 TLOGW(WmsLogTag::WMS_SYSTEM, "Unregister type %{public}s failed because not found callback", type.c_str());
613 return NapiGetUndefined(env);
614 }
615 if (jsCbMap_[type].empty()) {
616 jsCbMap_.erase(type);
617 }
618 }
619 TLOGI(WmsLogTag::WMS_SYSTEM, "Unregister type %{public}s success", type.c_str());
620 return NapiGetUndefined(env);
621 }
622
UnRegisterListener(const std::string & type,const sptr<JsFbWindowListener> & fbWindowListener)623 WmErrorCode JsFbController::UnRegisterListener(const std::string& type,
624 const sptr<JsFbWindowListener>& fbWindowListener)
625 {
626 WMError ret = WMError::WM_OK;
627 if (type == STATE_CHANGE_CB) {
628 ret = ProcessStateChangeUnRegister(fbWindowListener);
629 }
630 if (type == CLICK_EVENT) {
631 ret = ProcessClickEventUnRegister(fbWindowListener);
632 }
633 return ConvertErrorToCode(ret);
634 }
635
ProcessStateChangeUnRegister(const sptr<JsFbWindowListener> & listener)636 WMError JsFbController::ProcessStateChangeUnRegister(const sptr<JsFbWindowListener>& listener)
637 {
638 if (fbController_ == nullptr) {
639 TLOGE(WmsLogTag::WMS_SYSTEM, "controller is nullptr");
640 return WMError::WM_ERROR_FB_STATE_ABNORMALLY;
641 }
642 return fbController_->UnRegisterFbLifecycle(listener);
643 }
644
ProcessClickEventUnRegister(const sptr<JsFbWindowListener> & listener)645 WMError JsFbController::ProcessClickEventUnRegister(const sptr<JsFbWindowListener>& listener)
646 {
647 if (fbController_ == nullptr) {
648 TLOGE(WmsLogTag::WMS_SYSTEM, "controller is nullptr");
649 return WMError::WM_ERROR_FB_STATE_ABNORMALLY;
650 }
651 return fbController_->UnRegisterFbClickObserver(listener);
652 }
653
GetFloatingBallWindowInfo(napi_env env,napi_callback_info info)654 napi_value JsFbController::GetFloatingBallWindowInfo(napi_env env, napi_callback_info info)
655 {
656 TLOGI(WmsLogTag::WMS_SYSTEM, "GetFloatingBallWindowInfo start");
657 JsFbController* me = CheckParamsAndGetThis<JsFbController>(env, info);
658 return (me != nullptr) ? me->OnGetFloatingBallWindowInfo(env, info) : nullptr;
659 }
660
CreateJsFbWindowInfoObject(napi_env env,const std::shared_ptr<uint32_t> & windowIdPtr)661 napi_value CreateJsFbWindowInfoObject(napi_env env, const std::shared_ptr<uint32_t>& windowIdPtr)
662 {
663 napi_value objValue = nullptr;
664 napi_status status = napi_create_object(env, &objValue);
665 if (status != napi_ok || objValue == nullptr) {
666 TLOGE(WmsLogTag::WMS_SYSTEM, "failed to create js obj, error:%{public}d", status);
667 return NapiGetUndefined(env);
668 }
669 TLOGI(WmsLogTag::WMS_SYSTEM, "CreateJsFbWindowInfo");
670 napi_set_named_property(env, objValue, "windowId", CreateJsValue(env, *windowIdPtr));
671 return objValue;
672 }
673
OnGetFloatingBallWindowInfo(napi_env env,napi_callback_info info)674 napi_value JsFbController::OnGetFloatingBallWindowInfo(napi_env env, napi_callback_info info)
675 {
676 TLOGD(WmsLogTag::WMS_SYSTEM, "NAPI OnGetFloatingBallWindowInfo");
677 wptr<FloatingBallController> weakController(fbController_);
678 std::shared_ptr<WmErrorCode> errCodePtr = std::make_shared<WmErrorCode>(WmErrorCode::WM_OK);
679 std::shared_ptr<uint32_t> windowIdPtr = std::make_shared<uint32_t>(0);
680 NapiAsyncTask::ExecuteCallback execute = [weakController, windowIdPtr, errCodePtr] {
681 if (errCodePtr == nullptr || windowIdPtr == nullptr) {
682 return;
683 }
684 auto fbController = weakController.promote();
685 if (fbController == nullptr) {
686 *errCodePtr = WmErrorCode::WM_ERROR_FB_STATE_ABNORMALLY;
687 return;
688 }
689 uint32_t windowId = 0;
690 *errCodePtr = ConvertErrorToCode(fbController->GetFloatingBallWindowInfo(windowId));
691 *windowIdPtr = windowId;
692 };
693 NapiAsyncTask::CompleteCallback complete =
694 [errCodePtr, windowIdPtr](napi_env env, NapiAsyncTask& task, int32_t status) {
695 if (errCodePtr == nullptr || windowIdPtr == nullptr) {
696 task.Reject(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_FB_INTERNAL_ERROR));
697 return;
698 }
699 if (*errCodePtr == WmErrorCode::WM_OK) {
700 task.Resolve(env, CreateJsFbWindowInfoObject(env, windowIdPtr));
701 } else {
702 task.Reject(env, JsErrUtils::CreateJsError(env, *errCodePtr,
703 "JsFbController::OnGetFloatingBallWindowInfo failed."));
704 }
705 };
706 napi_value result = nullptr;
707 NapiAsyncTask::Schedule("JsFbController::OnGetFloatingBallWindowInfo",
708 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
709 return result;
710 }
711 } // namespace Rosen
712 } // namespace OHOS