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 "imf_adapter_impl.h"
17 
18 #include "hisysevent_adapter.h"
19 #include "nweb_log.h"
20 #include "ohos_adapter_helper.h"
21 #include "third_party/cJSON/cJSON.h"
22 
23 namespace OHOS::NWeb {
24 constexpr char INPUT_METHOD[] = "INPUT_METHOD";
25 constexpr char ATTACH_CODE[] = "ATTACH_CODE";
26 constexpr char IS_SHOW_KEY_BOARD[] = "IS_SHOW_KEY_BOARD";
27 constexpr int32_t IMF_LISTENER_NULL_POINT = 1;
28 constexpr int32_t IMF_TEXT_CONFIG_NULL_POINT = 2;
29 const std::string AUTO_FILL_CANCEL_PRIVATE_COMMAND = "autofill.cancel";
30 
IMFTextListenerAdapterImpl(const std::shared_ptr<IMFTextListenerAdapter> & listener)31 IMFTextListenerAdapterImpl::IMFTextListenerAdapterImpl(const std::shared_ptr<IMFTextListenerAdapter>& listener)
32     : listener_(listener) {};
33 
~IMFTextListenerAdapterImpl()34 IMFTextListenerAdapterImpl::~IMFTextListenerAdapterImpl()
35 {
36     listener_ = nullptr;
37 }
38 
InsertText(const std::u16string & text)39 void IMFTextListenerAdapterImpl::InsertText(const std::u16string& text)
40 {
41     if (listener_) {
42         listener_->InsertText(text);
43     }
44 }
45 
DeleteForward(int32_t length)46 void IMFTextListenerAdapterImpl::DeleteForward(int32_t length)
47 {
48     if (listener_) {
49         listener_->DeleteForward(length);
50     }
51 }
52 
DeleteBackward(int32_t length)53 void IMFTextListenerAdapterImpl::DeleteBackward(int32_t length)
54 {
55     if (listener_) {
56         listener_->DeleteBackward(length);
57     }
58 }
59 
SendKeyEventFromInputMethod(const MiscServices::KeyEvent & event)60 void IMFTextListenerAdapterImpl::SendKeyEventFromInputMethod(const MiscServices::KeyEvent& event)
61 {
62     (void)event;
63     if (listener_) {
64         listener_->SendKeyEventFromInputMethod();
65     }
66 }
67 
SendKeyboardStatus(const MiscServices::KeyboardStatus & keyboardStatus)68 void IMFTextListenerAdapterImpl::SendKeyboardStatus(const MiscServices::KeyboardStatus& keyboardStatus)
69 {
70     if (listener_) {
71         auto status = IMFAdapterKeyboardStatus::NONE;
72         if (keyboardStatus == MiscServices::KeyboardStatus::SHOW) {
73             status = IMFAdapterKeyboardStatus::SHOW;
74         } else if (keyboardStatus == MiscServices::KeyboardStatus::HIDE) {
75             status = IMFAdapterKeyboardStatus::HIDE;
76         }
77         listener_->SendKeyboardStatus(status);
78     }
79 }
80 
SendFunctionKey(const MiscServices::FunctionKey & functionKey)81 void IMFTextListenerAdapterImpl::SendFunctionKey(const MiscServices::FunctionKey& functionKey)
82 {
83     if (listener_) {
84         std::shared_ptr<IMFAdapterFunctionKeyAdapterImpl> adapterFunction =
85             std::make_shared<IMFAdapterFunctionKeyAdapterImpl>();
86         switch (functionKey.GetEnterKeyType()) {
87             case MiscServices::EnterKeyType::UNSPECIFIED:
88                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::UNSPECIFIED);
89                 break;
90             case MiscServices::EnterKeyType::NONE:
91                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NONE);
92                 break;
93             case MiscServices::EnterKeyType::GO:
94                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::GO);
95                 break;
96             case MiscServices::EnterKeyType::SEARCH:
97                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::SEARCH);
98                 break;
99             case MiscServices::EnterKeyType::SEND:
100                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::SEND);
101                 break;
102             case MiscServices::EnterKeyType::NEXT:
103                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NEXT);
104                 break;
105             case MiscServices::EnterKeyType::DONE:
106                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::DONE);
107                 break;
108             case MiscServices::EnterKeyType::PREVIOUS:
109                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::PREVIOUS);
110                 break;
111             case MiscServices::EnterKeyType::NEW_LINE:
112                 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NEW_LINE);
113                 break;
114             default:
115                 WVLOG_E("unknown functionKey");
116                 break;
117         }
118         listener_->SendFunctionKey(adapterFunction);
119     }
120 }
121 
SetKeyboardStatus(bool status)122 void IMFTextListenerAdapterImpl::SetKeyboardStatus(bool status)
123 {
124     if (listener_) {
125         listener_->SetKeyboardStatus(status);
126     }
127 }
128 
MoveCursor(const MiscServices::Direction direction)129 void IMFTextListenerAdapterImpl::MoveCursor(const MiscServices::Direction direction)
130 {
131     if (listener_) {
132         IMFAdapterDirection adapterDirection;
133         switch (direction) {
134             case MiscServices::Direction::UP: {
135                 adapterDirection = IMFAdapterDirection::UP;
136                 break;
137             }
138             case MiscServices::Direction::DOWN: {
139                 adapterDirection = IMFAdapterDirection::DOWN;
140                 break;
141             }
142             case MiscServices::Direction::LEFT: {
143                 adapterDirection = IMFAdapterDirection::LEFT;
144                 break;
145             }
146             case MiscServices::Direction::RIGHT: {
147                 adapterDirection = IMFAdapterDirection::RIGHT;
148                 break;
149             }
150             default: {
151                 adapterDirection = IMFAdapterDirection::NONE;
152             }
153         }
154         listener_->MoveCursor(adapterDirection);
155     }
156 }
157 
HandleSetSelection(int32_t start,int32_t end)158 void IMFTextListenerAdapterImpl::HandleSetSelection(int32_t start, int32_t end)
159 {
160     if (listener_) {
161         listener_->HandleSetSelection(start, end);
162     }
163 }
164 
HandleExtendAction(int32_t action)165 void IMFTextListenerAdapterImpl::HandleExtendAction(int32_t action)
166 {
167     if (listener_) {
168         listener_->HandleExtendAction(action);
169     }
170 }
171 
HandleSelect(int32_t keyCode,int32_t cursorMoveSkip)172 void IMFTextListenerAdapterImpl::HandleSelect(int32_t keyCode, int32_t cursorMoveSkip)
173 {
174     if (listener_) {
175         listener_->HandleSelect(keyCode, cursorMoveSkip);
176     }
177 }
178 
GetTextIndexAtCursor()179 int32_t IMFTextListenerAdapterImpl::GetTextIndexAtCursor()
180 {
181     if (listener_) {
182         return listener_->GetTextIndexAtCursor();
183     }
184     return -1;
185 }
186 
GetLeftTextOfCursor(int32_t number)187 std::u16string IMFTextListenerAdapterImpl::GetLeftTextOfCursor(int32_t number)
188 {
189     if (listener_) {
190         return listener_->GetLeftTextOfCursor(number);
191     }
192     return u"";
193 }
194 
GetRightTextOfCursor(int32_t number)195 std::u16string IMFTextListenerAdapterImpl::GetRightTextOfCursor(int32_t number)
196 {
197     if (listener_) {
198         return listener_->GetRightTextOfCursor(number);
199     }
200     return u"";
201 }
202 
SetPreviewText(const std::u16string & text,const MiscServices::Range & range)203 int32_t IMFTextListenerAdapterImpl::SetPreviewText(const std::u16string& text, const MiscServices::Range& range)
204 {
205     if (listener_) {
206         return listener_->SetPreviewText(text, range.start, range.end);
207     }
208     return -1;
209 }
210 
FinishTextPreview()211 void IMFTextListenerAdapterImpl::FinishTextPreview()
212 {
213     if (listener_) {
214         listener_->FinishTextPreview();
215     }
216 }
217 
ReceivePrivateCommand(const std::unordered_map<std::string,MiscServices::PrivateDataValue> & privateCommand)218 int32_t IMFTextListenerAdapterImpl::ReceivePrivateCommand(
219     const std::unordered_map<std::string, MiscServices::PrivateDataValue>& privateCommand)
220 {
221     WVLOG_I("ReceivePrivateCommand");
222     auto item = privateCommand.find(PREVIEW_TEXT_STYLE_KEY);
223     if (item != privateCommand.end()) {
224         bool isNeedUnderline = false;
225         MiscServices::PrivateDataValue data = item->second;
226         std::string previewStyle = std::get<std::string>(data);
227         if (previewStyle == PREVIEW_TEXT_STYLE_UNDERLINE) {
228             isNeedUnderline = true;
229         }
230 
231         if (listener_) {
232             listener_->SetNeedUnderLine(isNeedUnderline);
233         }
234     }
235 
236     item = privateCommand.find(AUTO_FILL_PARAMS_USERNAME);
237     if (item != privateCommand.end()) {
238         if (listener_) {
239             std::string content = std::get<std::string>(item->second);
240             listener_->AutoFillWithIMFEvent(true, false, false, content);
241         }
242     }
243 
244     item = privateCommand.find(AUTO_FILL_PARAMS_OTHERACCOUNT);
245     if (item != privateCommand.end()) {
246         if (listener_) {
247             std::string content = std::string("");
248             listener_->AutoFillWithIMFEvent(false, true, false, content);
249         }
250     }
251 
252     return 0;
253 }
254 
Attach(std::shared_ptr<IMFTextListenerAdapter> listener,bool isShowKeyboard)255 bool IMFAdapterImpl::Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard)
256 {
257     if (!listener) {
258         WVLOG_E("the listener is nullptr");
259         return false;
260     }
261     if (!textListener_) {
262         textListener_ = new (std::nothrow) IMFTextListenerAdapterImpl(listener);
263         if (!textListener_) {
264             WVLOG_E("new textListener failed");
265             return false;
266         }
267     }
268     int32_t ret = MiscServices::InputMethodController::GetInstance()->Attach(textListener_, isShowKeyboard);
269     if (ret != 0) {
270         WVLOG_E("inputmethod attach failed, errcode=%{public}d", ret);
271         return false;
272     }
273     return true;
274 }
275 
ReportImfErrorEvent(int32_t ret,bool isShowKeyboard)276 void ReportImfErrorEvent(int32_t ret, bool isShowKeyboard)
277 {
278     std::string isShowKeyboardStr = isShowKeyboard ? "true" : "false";
279     OhosAdapterHelper::GetInstance().GetHiSysEventAdapterInstance().Write(INPUT_METHOD,
280         HiSysEventAdapter::EventType::FAULT,
281         { ATTACH_CODE, std::to_string(ret), IS_SHOW_KEY_BOARD, isShowKeyboardStr });
282 }
283 
Attach(std::shared_ptr<IMFTextListenerAdapter> listener,bool isShowKeyboard,const std::shared_ptr<IMFTextConfigAdapter> config,bool isResetListener)284 bool IMFAdapterImpl::Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard,
285     const std::shared_ptr<IMFTextConfigAdapter> config, bool isResetListener)
286 {
287     if (!listener) {
288         WVLOG_E("the listener is nullptr");
289         ReportImfErrorEvent(IMF_LISTENER_NULL_POINT, isShowKeyboard);
290         return false;
291     }
292     if (!config || !(config->GetInputAttribute()) || !(config->GetCursorInfo())) {
293         WVLOG_E("the config is nullptr");
294         ReportImfErrorEvent(IMF_TEXT_CONFIG_NULL_POINT, isShowKeyboard);
295         return false;
296     }
297 
298     if ((textListener_ != nullptr) && isResetListener) {
299         textListener_ = nullptr;
300         WVLOG_I("attach node is changed, need reset listener");
301     }
302 
303     if (!textListener_) {
304         textListener_ = new (std::nothrow) IMFTextListenerAdapterImpl(listener);
305         if (!textListener_) {
306             WVLOG_E("new textListener failed");
307             ReportImfErrorEvent(IMF_LISTENER_NULL_POINT, isShowKeyboard);
308             return false;
309         }
310     }
311 
312     MiscServices::InputAttribute inputAttribute = { .inputPattern = config->GetInputAttribute()->GetInputPattern(),
313         .enterKeyType = config->GetInputAttribute()->GetEnterKeyType(),
314         .isTextPreviewSupported = true };
315 
316     MiscServices::CursorInfo imfInfo = { .left = config->GetCursorInfo()->GetLeft(),
317         .top = config->GetCursorInfo()->GetTop(),
318         .width = config->GetCursorInfo()->GetWidth(),
319         .height = config->GetCursorInfo()->GetHeight() };
320 
321     MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
322         .cursorInfo = imfInfo,
323         .windowId = config->GetWindowId(),
324         .positionY = config->GetPositionY(),
325         .height = config->GetHeight() };
326     WVLOG_I("web inputmethod attach, isShowKeyboard=%{public}d, textConfig=%{public}s", isShowKeyboard,
327         textConfig.ToString().c_str());
328     int32_t ret = MiscServices::InputMethodController::GetInstance()->Attach(textListener_, isShowKeyboard, textConfig);
329     if (ret != 0) {
330         WVLOG_E("inputmethod attach failed, errcode=%{public}d", ret);
331         ReportImfErrorEvent(ret, isShowKeyboard);
332         return false;
333     }
334     return true;
335 }
336 
ShowCurrentInput(const IMFAdapterTextInputType & inputType)337 void IMFAdapterImpl::ShowCurrentInput(const IMFAdapterTextInputType& inputType)
338 {
339     MiscServices::Configuration config;
340     if (inputType == IMFAdapterTextInputType::NUMBER) {
341         config.SetTextInputType(MiscServices::TextInputType::NUMBER);
342     } else {
343         config.SetTextInputType(MiscServices::TextInputType::TEXT);
344     }
345     MiscServices::InputMethodController::GetInstance()->OnConfigurationChange(config);
346     MiscServices::InputMethodController::GetInstance()->ShowCurrentInput();
347 }
348 
HideTextInput()349 void IMFAdapterImpl::HideTextInput()
350 {
351     MiscServices::InputMethodController::GetInstance()->HideTextInput();
352 }
353 
Close()354 void IMFAdapterImpl::Close()
355 {
356     MiscServices::InputMethodController::GetInstance()->Close();
357 }
358 
OnCursorUpdate(const std::shared_ptr<IMFCursorInfoAdapter> cursorInfo)359 void IMFAdapterImpl::OnCursorUpdate(const std::shared_ptr<IMFCursorInfoAdapter> cursorInfo)
360 {
361     if (!cursorInfo) {
362         WVLOG_E("inputmethod OnCursorUpdate cursorInfo is null");
363         return;
364     }
365 
366     MiscServices::CursorInfo imfInfo = { .left = cursorInfo->GetLeft(),
367         .top = cursorInfo->GetTop(),
368         .width = cursorInfo->GetWidth(),
369         .height = cursorInfo->GetHeight() };
370     WVLOG_D("imfInfo left = %{public}f, top = %{public}f, width = %{public}f, height = %{public}f", imfInfo.left,
371         imfInfo.top, imfInfo.width, imfInfo.height);
372     MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(imfInfo);
373 }
374 
OnSelectionChange(std::u16string text,int start,int end)375 void IMFAdapterImpl::OnSelectionChange(std::u16string text, int start, int end)
376 {
377     MiscServices::InputMethodController::GetInstance()->OnSelectionChange(text, start, end);
378 }
379 
SendPrivateCommand(const std::string & commandKey,const std::string & commandValue)380 bool IMFAdapterImpl::SendPrivateCommand(const std::string& commandKey, const std::string& commandValue)
381 {
382     if (commandKey == AUTO_FILL_CANCEL_PRIVATE_COMMAND) {
383         std::unordered_map<std::string, MiscServices::PrivateDataValue> privateCommand;
384         ParseFillContentJsonValue(commandValue, privateCommand);
385         int32_t ret = MiscServices::InputMethodController::GetInstance()->SendPrivateCommand(privateCommand);
386         if (ret != 0) {
387             WVLOG_E("inputmethod SendPrivateCommand failed, errcode=%{public}d", ret);
388             return false;
389         }
390         WVLOG_I("inputmethod  SendPrivateCommand success");
391         return true;
392     }
393     return false;
394 }
395 
ParseFillContentJsonValue(const std::string & commandValue,std::unordered_map<std::string,std::variant<std::string,bool,int32_t>> & map)396 bool IMFAdapterImpl::ParseFillContentJsonValue(const std::string& commandValue,
397     std::unordered_map<std::string, std::variant<std::string, bool, int32_t>>& map)
398 {
399     cJSON* sourceJson = cJSON_Parse(commandValue.c_str());
400     if (sourceJson == nullptr || cJSON_IsNull(sourceJson)) {
401         cJSON_Delete(sourceJson);
402         return false;
403     }
404     if (cJSON_HasObjectItem(sourceJson, "userName")) {
405         cJSON* userName = cJSON_GetObjectItem(sourceJson, "userName");
406         if (userName != nullptr && cJSON_IsString(userName) && userName->valuestring != nullptr) {
407             map.insert(std::make_pair("userName", userName->valuestring));
408         }
409     }
410     if (cJSON_HasObjectItem(sourceJson, "hasAccount")) {
411         cJSON* hasAccount = cJSON_GetObjectItem(sourceJson, "hasAccount");
412         if (hasAccount != nullptr && cJSON_IsString(hasAccount) && hasAccount->valuestring != nullptr) {
413             map.insert(std::make_pair("hasAccount", hasAccount->valuestring));
414         }
415     }
416     cJSON_Delete(sourceJson);
417     return true;
418 }
419 
NotifyPanelStatusInfo(const MiscServices::PanelStatusInfo & info)420 void IMFTextListenerAdapterImpl::NotifyPanelStatusInfo(const MiscServices::PanelStatusInfo& info)
421 {
422     MiscServices::Trigger triggerFrom = info.trigger;
423     if (listener_ && (triggerFrom == MiscServices::Trigger::IME_APP)) {
424         WVLOG_I("IMFTextListenerAdapterImpl::NotifyPanelStatusInfo, info.IME_APP");
425         listener_->KeyboardUpperRightCornerHide();
426     }
427 }
428 } // namespace OHOS::NWeb
429