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#include "js_window_register_manager.h"
16#include "window_manager_hilog.h"
17
18namespace OHOS {
19namespace Rosen {
20namespace {
21constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "JsRegisterManager"};
22
23const std::map<std::string, ListenerFunctionType> WindowListenerFunctionMap {
24    {SYSTEM_AVOID_AREA_CHANGE_CB, ListenerFunctionType::SYSTEM_AVOID_AREA_CHANGE_CB},
25    {AVOID_AREA_CHANGE_CB, ListenerFunctionType::AVOID_AREA_CHANGE_CB},
26};
27
28const std::map<CaseType, std::map<std::string, ListenerFunctionType>> ListenerFunctionMap {
29    {CaseType::CASE_WINDOW, WindowListenerFunctionMap},
30};
31}
32
33JsWindowRegisterManager::JsWindowRegisterManager()
34{
35}
36
37JsWindowRegisterManager::~JsWindowRegisterManager()
38{
39}
40
41WmErrorCode JsWindowRegisterManager::ProcessSystemAvoidAreaChangeRegister(sptr<JsWindowListener> listener,
42    sptr<Window> window, bool isRegister, napi_env env, napi_value parameter)
43{
44    if (window == nullptr) {
45        WLOGFE("[NAPI]Window is nullptr");
46        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
47    }
48    if (listener == nullptr) {
49        WLOGFE("[NAPI]listener is nullptr");
50        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
51    }
52    listener->SetIsDeprecatedInterface(true);
53    sptr<IAvoidAreaChangedListener> thisListener(listener);
54    WmErrorCode ret;
55    if (isRegister) {
56        ret = WM_JS_TO_ERROR_CODE_MAP.at(window->RegisterAvoidAreaChangeListener(thisListener));
57    } else {
58        ret = WM_JS_TO_ERROR_CODE_MAP.at(window->UnregisterAvoidAreaChangeListener(thisListener));
59    }
60    return ret;
61}
62
63WmErrorCode JsWindowRegisterManager::ProcessAvoidAreaChangeRegister(sptr<JsWindowListener> listener,
64    sptr<Window> window, bool isRegister, napi_env env, napi_value parameter)
65{
66    if (window == nullptr) {
67        WLOGFE("[NAPI]Window is nullptr");
68        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
69    }
70    sptr<IAvoidAreaChangedListener> thisListener(listener);
71    WmErrorCode ret;
72    if (isRegister) {
73        ret = WM_JS_TO_ERROR_CODE_MAP.at(window->RegisterAvoidAreaChangeListener(thisListener));
74    } else {
75        ret = WM_JS_TO_ERROR_CODE_MAP.at(window->UnregisterAvoidAreaChangeListener(thisListener));
76    }
77    return ret;
78}
79
80WmErrorCode JsWindowRegisterManager::RegisterListener(sptr<Window> window, std::string type,
81    CaseType caseType, napi_env env, napi_value callback, napi_value parameter)
82{
83    std::lock_guard<std::mutex> lock(mtx_);
84    if (IsCallbackRegistered(env, type, callback)) {
85        return WmErrorCode::WM_OK;
86    }
87    auto iterCaseType = ListenerFunctionMap.find(caseType);
88    if (iterCaseType == ListenerFunctionMap.end()) {
89        WLOGFE("[NAPI]CaseType %{public}u is not supported", static_cast<uint32_t>(caseType));
90        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
91    }
92    auto iterCallbackType = iterCaseType->second.find(type);
93    if (iterCallbackType == iterCaseType->second.end()) {
94        WLOGFE("[NAPI]Type %{public}s is not supported", type.c_str());
95        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
96    }
97    ListenerFunctionType listenerFunctionType = iterCallbackType->second;
98    napi_ref result = nullptr;
99    napi_create_reference(env, callback, 1, &result);
100    std::shared_ptr<NativeReference> callbackRef(reinterpret_cast<NativeReference*>(result));
101    sptr<JsWindowListener> windowManagerListener = new(std::nothrow) JsWindowListener(env, callbackRef);
102    if (windowManagerListener == nullptr) {
103        WLOGFE("[NAPI]New JsWindowListener failed");
104        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
105    }
106    windowManagerListener->SetMainEventHandler();
107    WmErrorCode ret = ProcessRegisterCallback(listenerFunctionType, caseType, windowManagerListener, window,
108        true, env, parameter);
109    if (ret != WmErrorCode::WM_OK) {
110        WLOGFE("[NAPI]Register type %{public}s failed", type.c_str());
111        return ret;
112    }
113    jsCbMap_[type][callbackRef] = windowManagerListener;
114    WLOGI("[NAPI]Register type %{public}s success! callback map size: %{public}zu",
115        type.c_str(), jsCbMap_[type].size());
116    return WmErrorCode::WM_OK;
117}
118
119WmErrorCode JsWindowRegisterManager::ProcessRegisterCallback(ListenerFunctionType listenerFunctionType,
120    CaseType caseType, const sptr<JsWindowListener>& listener, const sptr<Window>& window, bool isRegister,
121    napi_env env, napi_value parameter)
122{
123    if (caseType == CaseType::CASE_WINDOW) {
124        switch (listenerFunctionType) {
125            case ListenerFunctionType::SYSTEM_AVOID_AREA_CHANGE_CB:
126                return ProcessSystemAvoidAreaChangeRegister(listener, window, isRegister, env, parameter);
127            case ListenerFunctionType::AVOID_AREA_CHANGE_CB:
128                return ProcessAvoidAreaChangeRegister(listener, window, isRegister, env, parameter);
129            default:
130                return WmErrorCode::WM_ERROR_INVALID_PARAM;
131        }
132    }
133    return WmErrorCode::WM_ERROR_INVALID_PARAM;
134}
135
136WmErrorCode JsWindowRegisterManager::UnregisterListener(sptr<Window> window, std::string type,
137    CaseType caseType, napi_env env, napi_value value)
138{
139    std::lock_guard<std::mutex> lock(mtx_);
140    if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
141        WLOGFE("[NAPI]Type %{public}s was not registerted", type.c_str());
142        return WmErrorCode::WM_OK;
143    }
144    auto iterCaseType = ListenerFunctionMap.find(caseType);
145    if (iterCaseType == ListenerFunctionMap.end()) {
146        WLOGFE("[NAPI]CaseType %{public}u is not supported", static_cast<uint32_t>(caseType));
147        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
148    }
149    auto iterCallbackType = iterCaseType->second.find(type);
150    if (iterCallbackType == iterCaseType->second.end()) {
151        WLOGFE("[NAPI]Type %{public}s is not supported", type.c_str());
152        return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
153    }
154    ListenerFunctionType listenerFunctionType = iterCallbackType->second;
155    if (value == nullptr) {
156        for (auto it = jsCbMap_[type].begin(); it != jsCbMap_[type].end();) {
157            WmErrorCode ret = ProcessRegisterCallback(listenerFunctionType, caseType, it->second, window, false,
158                env, nullptr);
159            if (ret != WmErrorCode::WM_OK) {
160                WLOGFE("[NAPI]Unregister type %{public}s failed, no value", type.c_str());
161                return ret;
162            }
163            jsCbMap_[type].erase(it++);
164        }
165    } else {
166        bool findFlag = false;
167        for (auto it = jsCbMap_[type].begin(); it != jsCbMap_[type].end(); ++it) {
168            bool isEquals = false;
169            napi_strict_equals(env, value, it->first->GetNapiValue(), &isEquals);
170            if (!isEquals) {
171                continue;
172            }
173            findFlag = true;
174            WmErrorCode ret = ProcessRegisterCallback(listenerFunctionType, caseType, it->second, window, false,
175                env, nullptr);
176            if (ret != WmErrorCode::WM_OK) {
177                WLOGFE("[NAPI]Unregister type %{public}s failed", type.c_str());
178                return ret;
179            }
180            jsCbMap_[type].erase(it);
181            break;
182        }
183        if (!findFlag) {
184            WLOGFE("[NAPI]Unregister type %{public}s failed because not found callback!", type.c_str());
185            return WmErrorCode::WM_ERROR_STATE_ABNORMALLY;
186        }
187    }
188    WLOGI("[NAPI]Unregister type %{public}s success! callback map size: %{public}zu",
189        type.c_str(), jsCbMap_[type].size());
190    // erase type when there is no callback in one type
191    if (jsCbMap_[type].empty()) {
192        jsCbMap_.erase(type);
193    }
194    return WmErrorCode::WM_OK;
195}
196
197bool JsWindowRegisterManager::IsCallbackRegistered(napi_env env, std::string& type, napi_value jsListenerObject)
198{
199    if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
200        WLOGI("[NAPI]Method %{public}s has not been registerted", type.c_str());
201        return false;
202    }
203
204    for (auto iter = jsCbMap_[type].begin(); iter != jsCbMap_[type].end(); ++iter) {
205        bool isEquals = false;
206        napi_strict_equals(env, jsListenerObject, iter->first->GetNapiValue(), &isEquals);
207        if (isEquals) {
208            WLOGFE("[NAPI]Method %{public}s has already been registered", type.c_str());
209            return true;
210        }
211    }
212    return false;
213}
214
215} // namespace Rosen
216} // namespace OHOS
217