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
16 #include "js_panel.h"
17
18 #include "event_checker.h"
19 #include "input_method_ability.h"
20 #include "js_util.h"
21 #include "js_utils.h"
22 #include "napi/native_common.h"
23 #include "panel_listener_impl.h"
24
25 namespace OHOS {
26 namespace MiscServices {
27 const std::string JsPanel::CLASS_NAME = "Panel";
28 thread_local napi_ref JsPanel::panelConstructorRef_ = nullptr;
29 std::mutex JsPanel::panelConstructorMutex_;
30
Init(napi_env env)31 napi_value JsPanel::Init(napi_env env)
32 {
33 IMSA_HILOGI("JsPanel in.");
34 napi_value constructor = nullptr;
35 std::lock_guard<std::mutex> lock(panelConstructorMutex_);
36 if (panelConstructorRef_ != nullptr) {
37 napi_status status = napi_get_reference_value(env, panelConstructorRef_, &constructor);
38 CHECK_RETURN(status == napi_ok, "Failed to get jsPanel constructor.", nullptr);
39 return constructor;
40 }
41 const napi_property_descriptor properties[] = {
42 DECLARE_NAPI_FUNCTION("setUiContent", SetUiContent),
43 DECLARE_NAPI_FUNCTION("resize", Resize),
44 DECLARE_NAPI_FUNCTION("moveTo", MoveTo),
45 DECLARE_NAPI_FUNCTION("show", Show),
46 DECLARE_NAPI_FUNCTION("hide", Hide),
47 DECLARE_NAPI_FUNCTION("changeFlag", ChangeFlag),
48 DECLARE_NAPI_FUNCTION("on", Subscribe),
49 DECLARE_NAPI_FUNCTION("off", UnSubscribe),
50 };
51 NAPI_CALL(env, napi_define_class(env, CLASS_NAME.c_str(), CLASS_NAME.size(), JsNew, nullptr,
52 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor));
53 CHECK_RETURN(constructor != nullptr, "napi_define_class failed!", nullptr);
54 NAPI_CALL(env, napi_create_reference(env, constructor, 1, &panelConstructorRef_));
55 return constructor;
56 }
57
JsNew(napi_env env,napi_callback_info info)58 napi_value JsPanel::JsNew(napi_env env, napi_callback_info info)
59 {
60 IMSA_HILOGD("JsPanel, create panel instance in.");
61 JsPanel *panel = new (std::nothrow) JsPanel();
62 CHECK_RETURN(panel != nullptr, "no memory for JsPanel", nullptr);
63 auto finalize = [](napi_env env, void *data, void *hint) {
64 IMSA_HILOGI("jsPanel finalize.");
65 auto *jsPanel = reinterpret_cast<JsPanel *>(data);
66 CHECK_RETURN_VOID(jsPanel != nullptr, "finalize null!");
67 jsPanel->GetNative() = nullptr;
68 delete jsPanel;
69 };
70 napi_value thisVar = nullptr;
71 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
72 napi_status status = napi_wrap(env, thisVar, panel, finalize, nullptr, nullptr);
73 if (status != napi_ok) {
74 IMSA_HILOGE("JsPanel napi_wrap failed: %{public}d", status);
75 delete panel;
76 return nullptr;
77 }
78 return thisVar;
79 }
80
~JsPanel()81 JsPanel::~JsPanel()
82 {
83 inputMethodPanel_ = nullptr;
84 }
85
SetNative(const std::shared_ptr<InputMethodPanel> & panel)86 void JsPanel::SetNative(const std::shared_ptr<InputMethodPanel> &panel)
87 {
88 inputMethodPanel_ = panel;
89 }
90
GetNative()91 std::shared_ptr<InputMethodPanel> JsPanel::GetNative()
92 {
93 return inputMethodPanel_;
94 }
95
SetUiContent(napi_env env,napi_callback_info info)96 napi_value JsPanel::SetUiContent(napi_env env, napi_callback_info info)
97 {
98 IMSA_HILOGI("JsPanel in.");
99 auto ctxt = std::make_shared<PanelContentContext>(env, info);
100 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
101 napi_status status = napi_generic_failure;
102 PARAM_CHECK_RETURN(env, argc >= 1, "should 1 or 2 parameters!", TYPE_NONE, status);
103 // 0 means the first param path<std::string>
104 status = JsUtils::GetValue(env, argv[0], ctxt->path);
105 CHECK_RETURN(status == napi_ok, "get path failed!", status);
106 // if type of argv[1] is object, we will get value of 'storage' from it.
107 if (argc >= 2) {
108 napi_valuetype valueType = napi_undefined;
109 status = napi_typeof(env, argv[1], &valueType);
110 CHECK_RETURN(status == napi_ok, "get valueType failed!", status);
111 if (valueType == napi_object) {
112 NativeValue *storage = nullptr;
113 storage = reinterpret_cast<NativeValue *>(argv[1]);
114 auto contentStorage = (storage == nullptr)
115 ? nullptr
116 : std::shared_ptr<NativeReference>(
117 reinterpret_cast<NativeEngine *>(env)->CreateReference(storage, 1));
118 ctxt->contentStorage = contentStorage;
119 }
120 }
121 return napi_ok;
122 };
123
124 auto exec = [ctxt](AsyncCall::Context *ctx) { ctxt->SetState(napi_ok); };
125 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
126 CHECK_RETURN(ctxt->inputMethodPanel != nullptr, "inputMethodPanel is nullptr!", napi_generic_failure);
127 auto code = ctxt->inputMethodPanel->SetUiContent(
128 ctxt->path, *(reinterpret_cast<NativeEngine *>(env)), ctxt->contentStorage);
129 CHECK_RETURN(code == ErrorCode::NO_ERROR, "SetUiContent failed!", napi_generic_failure);
130 return napi_ok;
131 };
132 ctxt->SetAction(std::move(input), std::move(output));
133 // 3 means JsAPI:setUiContent has 3 params at most.
134 AsyncCall asyncCall(env, info, ctxt, 3);
135 return asyncCall.Call(env, exec, "setUiContent");
136 }
137
Resize(napi_env env,napi_callback_info info)138 napi_value JsPanel::Resize(napi_env env, napi_callback_info info)
139 {
140 auto ctxt = std::make_shared<PanelContentContext>(env, info);
141 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
142 napi_status status = napi_generic_failure;
143 PARAM_CHECK_RETURN(env, argc > 1, "should 2 or 3 parameters!", TYPE_NONE, status);
144 // 0 means the first param width<uint32_t>
145 status = JsUtils::GetValue(env, argv[0], ctxt->width);
146 CHECK_RETURN(status == napi_ok, "get width failed!", status);
147 // 1 means the second param height<uint32_t>
148 status = JsUtils::GetValue(env, argv[1], ctxt->height);
149 CHECK_RETURN(status == napi_ok, "get height failed!", status);
150 return napi_ok;
151 };
152
153 auto exec = [ctxt](AsyncCall::Context *ctx) {
154 CHECK_RETURN_VOID(ctxt->inputMethodPanel != nullptr, "inputMethodPanel_ is nullptr.");
155 auto code = ctxt->inputMethodPanel->Resize(ctxt->width, ctxt->height);
156 if (code == ErrorCode::NO_ERROR) {
157 ctxt->SetState(napi_ok);
158 return;
159 }
160 ctxt->SetErrorCode(code);
161 };
162 ctxt->SetAction(std::move(input));
163 // 3 means JsAPI:resize has 3 params at most.
164 AsyncCall asyncCall(env, info, ctxt, 3);
165 return asyncCall.Call(env, exec, "resize");
166 }
167
MoveTo(napi_env env,napi_callback_info info)168 napi_value JsPanel::MoveTo(napi_env env, napi_callback_info info)
169 {
170 auto ctxt = std::make_shared<PanelContentContext>(env, info);
171 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
172 napi_status status = napi_generic_failure;
173 PARAM_CHECK_RETURN(env, argc > 1, " should 2 or 3 parameters! ", TYPE_NONE, status);
174 // 0 means the first param x<int32_t>
175 status = JsUtils::GetValue(env, argv[0], ctxt->x);
176 CHECK_RETURN(status == napi_ok, "get x failed!", status);
177 // 1 means the second param y<int32_t>
178 status = JsUtils::GetValue(env, argv[1], ctxt->y);
179 CHECK_RETURN(status == napi_ok, "get y failed!", status);
180 return napi_ok;
181 };
182
183 auto exec = [ctxt](AsyncCall::Context *ctx) {
184 CHECK_RETURN_VOID(ctxt->inputMethodPanel != nullptr, "inputMethodPanel_ is nullptr.");
185 auto code = ctxt->inputMethodPanel->MoveTo(ctxt->x, ctxt->y);
186 if (code == ErrorCode::NO_ERROR) {
187 ctxt->SetState(napi_ok);
188 return;
189 }
190 ctxt->SetErrorCode(code);
191 };
192 ctxt->SetAction(std::move(input));
193 // 3 means JsAPI:moveTo has 3 params at most.
194 AsyncCall asyncCall(env, info, ctxt, 3);
195 return asyncCall.Call(env, exec, "moveTo");
196 }
197
Show(napi_env env,napi_callback_info info)198 napi_value JsPanel::Show(napi_env env, napi_callback_info info)
199 {
200 auto ctxt = std::make_shared<PanelContentContext>(env, info);
201 auto exec = [ctxt](AsyncCall::Context *ctx) {
202 CHECK_RETURN_VOID(ctxt->inputMethodPanel != nullptr, "inputMethodPanel_ is nullptr.");
203 auto code = ctxt->inputMethodPanel->ShowPanel();
204 if (code == ErrorCode::NO_ERROR) {
205 ctxt->SetState(napi_ok);
206 return;
207 }
208 ctxt->SetErrorCode(code);
209 };
210 // 1 means JsAPI:show has 1 params at most.
211 AsyncCall asyncCall(env, info, ctxt, 1);
212 return asyncCall.Call(env, exec, "show");
213 }
214
Hide(napi_env env,napi_callback_info info)215 napi_value JsPanel::Hide(napi_env env, napi_callback_info info)
216 {
217 auto ctxt = std::make_shared<PanelContentContext>(env, info);
218 auto exec = [ctxt](AsyncCall::Context *ctx) {
219 CHECK_RETURN_VOID(ctxt->inputMethodPanel != nullptr, "inputMethodPanel_ is nullptr.");
220 auto code = ctxt->inputMethodPanel->HidePanel();
221 if (code == ErrorCode::NO_ERROR) {
222 ctxt->SetState(napi_ok);
223 return;
224 }
225 ctxt->SetErrorCode(code);
226 };
227 // 1 means JsAPI:hide has 1 params at most.
228 AsyncCall asyncCall(env, info, ctxt, 1);
229 return asyncCall.Call(env, exec, "panel.hide");
230 }
231
ChangeFlag(napi_env env,napi_callback_info info)232 napi_value JsPanel::ChangeFlag(napi_env env, napi_callback_info info)
233 {
234 size_t argc = ARGC_MAX;
235 napi_value argv[ARGC_MAX] = { nullptr };
236 napi_value thisVar = nullptr;
237 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
238 PARAM_CHECK_RETURN(env, argc > 0, " should 1 parameter! ", TYPE_NONE, nullptr);
239 int32_t panelFlag = 0;
240 // 0 means the first param flag<PanelFlag>
241 napi_status status = JsUtils::GetValue(env, argv[0], panelFlag);
242 CHECK_RETURN(status == napi_ok, "get panelFlag failed!", nullptr);
243 auto inputMethodPanel = UnwrapPanel(env, thisVar);
244 auto ret = inputMethodPanel->ChangePanelFlag(PanelFlag(panelFlag));
245 CHECK_RETURN(ret == ErrorCode::NO_ERROR, "ChangePanelFlag failed!", nullptr);
246 return nullptr;
247 }
248
Subscribe(napi_env env,napi_callback_info info)249 napi_value JsPanel::Subscribe(napi_env env, napi_callback_info info)
250 {
251 IMSA_HILOGD("JsPanel in");
252 size_t argc = ARGC_MAX;
253 napi_value argv[ARGC_MAX] = { nullptr };
254 napi_value thisVar = nullptr;
255 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
256 std::string type;
257 // 2 means least param num.
258 if (argc < 2 || !JsUtil::GetValue(env, argv[0], type)
259 || !EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type)
260 || JsUtil::GetType(env, argv[1]) != napi_function) {
261 IMSA_HILOGE("Subscribe failed, type:%{public}s", type.c_str());
262 return nullptr;
263 }
264 IMSA_HILOGD("Subscribe type:%{public}s", type.c_str());
265 std::shared_ptr<PanelListenerImpl> observer = PanelListenerImpl::GetInstance();
266 auto inputMethodPanel = UnwrapPanel(env, thisVar);
267 // 1 means the second param callback.
268 observer->SaveInfo(env, type, argv[1], inputMethodPanel->windowId_);
269 inputMethodPanel->SetPanelStatusListener(observer, type);
270 napi_value result = nullptr;
271 napi_get_undefined(env, &result);
272 return result;
273 }
274
UnSubscribe(napi_env env,napi_callback_info info)275 napi_value JsPanel::UnSubscribe(napi_env env, napi_callback_info info)
276 {
277 size_t argc = ARGC_MAX;
278 napi_value argv[ARGC_MAX] = { nullptr };
279 napi_value thisVar = nullptr;
280 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
281 std::string type;
282 // 1 means least param num.
283 if (argc < 1 || !JsUtil::GetValue(env, argv[0], type)
284 || !EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type)) {
285 IMSA_HILOGE("UnSubscribe failed, type:%{public}s", type.c_str());
286 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
287 return nullptr;
288 }
289 // If the type of optional parameter is wrong, make it nullptr
290 if (JsUtil::GetType(env, argv[1]) != napi_function) {
291 argv[1] = nullptr;
292 }
293 IMSA_HILOGD("UnSubscribe type:%{public}s", type.c_str());
294 std::shared_ptr<PanelListenerImpl> observer = PanelListenerImpl::GetInstance();
295 auto inputMethodPanel = UnwrapPanel(env, thisVar);
296 observer->RemoveInfo(type, inputMethodPanel->windowId_);
297 inputMethodPanel->ClearPanelListener(type);
298 napi_value result = nullptr;
299 napi_get_null(env, &result);
300 return result;
301 }
302
UnwrapPanel(napi_env env,napi_value thisVar)303 std::shared_ptr<InputMethodPanel> JsPanel::UnwrapPanel(napi_env env, napi_value thisVar)
304 {
305 void *native = nullptr;
306 napi_status status = napi_unwrap(env, thisVar, &native);
307 CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
308 auto jsPanel = reinterpret_cast<JsPanel *>(native);
309 if (jsPanel == nullptr) {
310 return nullptr;
311 }
312 auto inputMethodPanel = jsPanel->GetNative();
313 CHECK_RETURN(inputMethodPanel != nullptr, "inputMethodPanel is nullptr", nullptr);
314 return inputMethodPanel;
315 }
316 } // namespace MiscServices
317 } // namespace OHOS