• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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