From decafbfe9fd4f9ec0c36fb2ae75bc2b4baaaf2c9 Mon Sep 17 00:00:00 2001 From: wuchengwen Date: Thu, 4 Sep 2025 10:29:53 +0800 Subject: [PATCH 1/4] feature:post Ndk cb to main thread Signed-off-by: wuchengwen --- common/include/atomic_data.h | 49 ++ frameworks/common/block_data.h | 13 + .../ndk/include/native_inputmethod_types.h | 1 + .../include/native_text_changed_listener.h | 8 +- .../src/inputmethod_texteditor_proxy_capi.cpp | 24 + .../ndk/src/native_text_changed_listener.cpp | 644 ++++++++++++------ .../c/inputmethod_text_editor_proxy_capi.h | 24 + test/unittest/cpp_test/BUILD.gn | 8 + .../src/inputmethod_controller_capi_test.cpp | 40 +- .../src/native_text_changed_listener_test.cpp | 226 ++++-- 10 files changed, 752 insertions(+), 285 deletions(-) create mode 100644 common/include/atomic_data.h diff --git a/common/include/atomic_data.h b/common/include/atomic_data.h new file mode 100644 index 000000000..59dd4fb0f --- /dev/null +++ b/common/include/atomic_data.h @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef ATOMIC_DATA_H +#define ATOMIC_DATA_H +#include +#include +#include + +// A generic atomic data wrapper class for types that do not support std::atomic natively +template +class AtomicData { +public: + // Constructor that supports direct initialization of internal data + template + explicit AtomicData(Args &&...args) : data_(std::forward(args)...) + { + } + + // Atomically retrieves a copy of the data + T Load() const + { + std::lock_guard lock(mutex_); + return data_; + } + + // Atomically sets the data (copy version) + void Store(const T &newValue) + { + std::lock_guard lock(mutex_); + data_ = newValue; + } + +private: + mutable std::mutex mutex_; // 'mutable' allows locking in const member functions + T data_; // The wrapped data +}; +#endif // ATOMIC_DAT_ \ No newline at end of file diff --git a/frameworks/common/block_data.h b/frameworks/common/block_data.h index 7c185aff9..07086dbb0 100644 --- a/frameworks/common/block_data.h +++ b/frameworks/common/block_data.h @@ -44,6 +44,9 @@ public: { std::unique_lock lock(mutex_); cv_.wait_for(lock, std::chrono::milliseconds(INTERVAL), [this]() { return isSet_; }); + if (!isSet_) { + isTimeout_ = true; + } T data = data_; return data; } @@ -60,6 +63,9 @@ public: { std::unique_lock lock(mutex_); cv_.wait_for(lock, std::chrono::milliseconds(INTERVAL), [this]() { return isSet_; }); + if (!isSet_) { + isTimeout_ = true; + } data = data_; return isSet_; } @@ -71,8 +77,15 @@ public: data_ = invalid; } + bool IsTimeout() + { + std::lock_guard lock(mutex_); + return isTimeout_; + } + private: bool isSet_ = false; + bool isTimeout_ = false; const uint32_t INTERVAL; T data_; std::mutex mutex_; diff --git a/frameworks/ndk/include/native_inputmethod_types.h b/frameworks/ndk/include/native_inputmethod_types.h index e97ed8973..22c6e95d5 100644 --- a/frameworks/ndk/include/native_inputmethod_types.h +++ b/frameworks/ndk/include/native_inputmethod_types.h @@ -80,6 +80,7 @@ struct InputMethod_TextEditorProxy { OH_TextEditorProxy_ReceivePrivateCommandFunc receivePrivateCommandFunc; OH_TextEditorProxy_SetPreviewTextFunc setPreviewTextFunc; OH_TextEditorProxy_FinishTextPreviewFunc finishTextPreviewFunc; + std::atomic_bool isCallbakcInMainThread = false; }; struct InputMethod_AttachOptions { diff --git a/frameworks/ndk/include/native_text_changed_listener.h b/frameworks/ndk/include/native_text_changed_listener.h index 2c95fcac0..5d142edd9 100644 --- a/frameworks/ndk/include/native_text_changed_listener.h +++ b/frameworks/ndk/include/native_text_changed_listener.h @@ -20,7 +20,7 @@ namespace OHOS { namespace MiscServices { class NativeTextChangedListener : public OHOS::MiscServices::OnTextChangedListener { public: - explicit NativeTextChangedListener(InputMethod_TextEditorProxy *textEditor) : textEditor_(textEditor) {}; + explicit NativeTextChangedListener(InputMethod_TextEditorProxy *textEditor); ~NativeTextChangedListener(); void InsertText(const std::u16string &text) override; void DeleteForward(int32_t length) override; @@ -48,7 +48,13 @@ private: InputMethod_EnterKeyType ConvertToCEnterKeyType(OHOS::MiscServices::EnterKeyType enterKeyType); InputMethod_Direction ConvertToCDirection(OHOS::MiscServices::Direction direction); InputMethod_ExtendAction ConvertToCExtendAction(int32_t action); + int32_t PostToMainThreadSync(std::function func, const std::string &name); + int32_t ExecTask(std::function func, const std::string &name); + bool CreateMainHandler(); + InputMethod_TextEditorProxy *textEditor_; + std::mutex mainHandlerMtx_; + std::shared_ptr mainHandler_ { nullptr }; }; } // namespace MiscServices } // namespace OHOS diff --git a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp index 3e0059780..82a8b3f83 100644 --- a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp +++ b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp @@ -503,4 +503,28 @@ InputMethod_ErrorCode OH_TextEditorProxy_GetFinishTextPreviewFunc( } *finishTextPreviewFunc = proxy->finishTextPreviewFunc; return IME_ERR_OK; +} + +InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( + InputMethod_TextEditorProxy *proxy, bool isCallbackInMainThread) +{ + if (proxy == nullptr) { + IMSA_HILOGE("proxy is nullptr"); + return IME_ERR_NULL_POINTER; + } + IMSA_HILOGI("isCallbackInMainThread: %{public}d", isCallbackInMainThread); + proxy->isCallbakcInMainThread = isCallbackInMainThread; + return IME_ERR_OK; +} + +InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( + InputMethod_TextEditorProxy *proxy, bool *isCallbackInMainThread) +{ + if (proxy == nullptr) { + IMSA_HILOGE("proxy is nullptr"); + return IME_ERR_NULL_POINTER; + } + + *isCallbackInMainThread = proxy->isCallbakcInMainThread; + return IME_ERR_OK; } \ No newline at end of file diff --git a/frameworks/ndk/src/native_text_changed_listener.cpp b/frameworks/ndk/src/native_text_changed_listener.cpp index 4753a48c2..5208b46a0 100644 --- a/frameworks/ndk/src/native_text_changed_listener.cpp +++ b/frameworks/ndk/src/native_text_changed_listener.cpp @@ -13,277 +13,433 @@ * limitations under the License. */ #include "native_text_changed_listener.h" +#include "atomic_data.h" +#include "block_data.h" #include "input_method_utils.h" #include "native_inputmethod_utils.h" namespace OHOS { namespace MiscServices { +namespace { +constexpr int32_t MAX_SYNC_TIMEOUT = 5; // 5ms +} + +NativeTextChangedListener::NativeTextChangedListener(InputMethod_TextEditorProxy *textEditor) + : textEditor_(textEditor) { +} NativeTextChangedListener::~NativeTextChangedListener() { textEditor_ = nullptr; + mainHandler_ = nullptr; } void NativeTextChangedListener::InsertText(const std::u16string &text) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->insertTextFunc == nullptr) { - IMSA_HILOGE("insertTextFunc is nullptr"); - return; - } - - textEditor_->insertTextFunc(textEditor_, text.c_str(), text.length()); + auto weakThis = wptr(this); + auto task = [weakThis, text]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->insertTextFunc == nullptr) { + IMSA_HILOGE("insertTextFunc is nullptr"); + return; + } + + promoteThis->textEditor_->insertTextFunc(promoteThis->textEditor_, text.c_str(), text.length()); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::DeleteForward(int32_t length) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->deleteForwardFunc == nullptr) { - IMSA_HILOGE("deleteForwardFunc is nullptr"); - return; - } - - textEditor_->deleteForwardFunc(textEditor_, length); + auto weakThis = wptr(this); + auto task = [weakThis, length]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->deleteForwardFunc == nullptr) { + IMSA_HILOGE("deleteForwardFunc is nullptr"); + return; + } + promoteThis->textEditor_->deleteForwardFunc(promoteThis->textEditor_, length); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::DeleteBackward(int32_t length) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->deleteBackwardFunc == nullptr) { - IMSA_HILOGE("deleteBackwardFunc is nullptr"); - return; - } - - textEditor_->deleteBackwardFunc(textEditor_, length); + auto weakThis = wptr(this); + auto task = [weakThis, length]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->deleteBackwardFunc == nullptr) { + IMSA_HILOGE("deleteBackwardFunc is nullptr"); + return; + } + + promoteThis->textEditor_->deleteBackwardFunc(promoteThis->textEditor_, length); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::SendKeyboardStatus(const OHOS::MiscServices::KeyboardStatus &status) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->sendKeyboardStatusFunc == nullptr) { - IMSA_HILOGE("sendKeyboardStatusFunc is nullptr"); - return; - } - - textEditor_->sendKeyboardStatusFunc(textEditor_, ConvertToCKeyboardStatus(status)); + auto weakThis = wptr(this); + auto task = [weakThis, status]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->sendKeyboardStatusFunc == nullptr) { + IMSA_HILOGE("sendKeyboardStatusFunc is nullptr"); + return; + } + + promoteThis->textEditor_->sendKeyboardStatusFunc( + promoteThis->textEditor_, promoteThis->ConvertToCKeyboardStatus(status)); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::SendFunctionKey(const OHOS::MiscServices::FunctionKey &functionKey) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->sendEnterKeyFunc == nullptr) { - IMSA_HILOGE("sendEnterKeyFunc is nullptr"); - return; - } - - auto enterKeyType = ConvertToCEnterKeyType(functionKey.GetEnterKeyType()); - - textEditor_->sendEnterKeyFunc(textEditor_, enterKeyType); + auto weakThis = wptr(this); + auto task = [weakThis, functionKey]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->sendEnterKeyFunc == nullptr) { + IMSA_HILOGE("sendEnterKeyFunc is nullptr"); + return; + } + + auto enterKeyType = promoteThis->ConvertToCEnterKeyType(functionKey.GetEnterKeyType()); + + promoteThis->textEditor_->sendEnterKeyFunc(promoteThis->textEditor_, enterKeyType); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::MoveCursor(const OHOS::MiscServices::Direction direction) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->moveCursorFunc == nullptr) { - IMSA_HILOGE("moveCursorFunc is nullptr"); - return; - } - - textEditor_->moveCursorFunc(textEditor_, ConvertToCDirection(direction)); + auto weakThis = wptr(this); + auto task = [weakThis, direction]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->moveCursorFunc == nullptr) { + IMSA_HILOGE("moveCursorFunc is nullptr"); + return; + } + + promoteThis->textEditor_->moveCursorFunc(promoteThis->textEditor_, promoteThis->ConvertToCDirection(direction)); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::HandleSetSelection(int32_t start, int32_t end) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->handleSetSelectionFunc == nullptr) { - IMSA_HILOGE("handleSetSelectionFunc is nullptr"); - return; - } - - textEditor_->handleSetSelectionFunc(textEditor_, start, end); + auto weakThis = wptr(this); + auto task = [weakThis, start, end]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->handleSetSelectionFunc == nullptr) { + IMSA_HILOGE("handleSetSelectionFunc is nullptr"); + return; + } + + promoteThis->textEditor_->handleSetSelectionFunc(promoteThis->textEditor_, start, end); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::HandleExtendAction(int32_t action) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->handleExtendActionFunc == nullptr) { - IMSA_HILOGE("handleExtendActionFunc is nullptr"); - return; - } - - textEditor_->handleExtendActionFunc(textEditor_, ConvertToCExtendAction(action)); + auto weakThis = wptr(this); + auto task = [weakThis, action]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->handleExtendActionFunc == nullptr) { + IMSA_HILOGE("handleExtendActionFunc is nullptr"); + return; + } + + promoteThis->textEditor_->handleExtendActionFunc( + promoteThis->textEditor_, promoteThis->ConvertToCExtendAction(action)); + }; + ExecTask(task, __func__); } std::u16string NativeTextChangedListener::GetLeftTextOfCursor(int32_t number) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return u""; - } - - if (textEditor_->getLeftTextOfCursorFunc == nullptr) { - IMSA_HILOGE("getLeftTextOfCursorFunc is nullptr"); - return u""; - } - - if (number <= 0 || number > MAX_TEXT_LENGTH) { - IMSA_HILOGE("number is invalid"); - return u""; - } - - size_t length = static_cast(number + 1); - char16_t *text = new (std::nothrow) char16_t[length]; - if (text == nullptr) { - IMSA_HILOGE("text is nullptr"); - return u""; - } - - textEditor_->getLeftTextOfCursorFunc(textEditor_, number, text, &length); - - std::u16string textStr(text, length); - delete[] text; - return textStr; + auto weakThis = wptr(this); + auto retText = std::make_shared>(u""); + auto task = [weakThis, number, retText]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->getLeftTextOfCursorFunc == nullptr) { + IMSA_HILOGE("getLeftTextOfCursorFunc is nullptr"); + return; + } + + if (number <= 0 || number > MAX_TEXT_LENGTH) { + IMSA_HILOGE("number is invalid"); + return; + } + + size_t length = static_cast(number + 1); + char16_t *text = new (std::nothrow) char16_t[length]; + if (text == nullptr) { + IMSA_HILOGE("text is nullptr"); + return; + } + + promoteThis->textEditor_->getLeftTextOfCursorFunc(promoteThis->textEditor_, number, text, &length); + + std::u16string textStr(text, length); + delete[] text; + retText->Store(textStr); + }; + ExecTask(task, __func__); + return retText->Load(); } std::u16string NativeTextChangedListener::GetRightTextOfCursor(int32_t number) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return u""; - } - - if (textEditor_->getRightTextOfCursorFunc == nullptr) { - IMSA_HILOGE("getRightTextOfCursorFunc is nullptr"); - return u""; - } - - if (number <= 0 || number > MAX_TEXT_LENGTH) { - IMSA_HILOGE("number is invalid"); - return u""; - } - - size_t length = static_cast(number + 1); - char16_t *text = new (std::nothrow) char16_t[length]; - if (text == nullptr) { - IMSA_HILOGE("text is nullptr"); - return u""; - } - - textEditor_->getRightTextOfCursorFunc(textEditor_, number, text, &length); - std::u16string textStr(text, length); - delete[] text; - return textStr; + auto weakThis = wptr(this); + auto retText = std::make_shared>(u""); + auto task = [weakThis, number, retText]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->getRightTextOfCursorFunc == nullptr) { + IMSA_HILOGE("getRightTextOfCursorFunc is nullptr"); + return; + } + + if (number <= 0 || number > MAX_TEXT_LENGTH) { + IMSA_HILOGE("number is invalid"); + return; + } + + size_t length = static_cast(number + 1); + char16_t *text = new (std::nothrow) char16_t[length]; + if (text == nullptr) { + IMSA_HILOGE("text is nullptr"); + return; + } + + promoteThis->textEditor_->getRightTextOfCursorFunc(promoteThis->textEditor_, number, text, &length); + std::u16string textStr(text, length); + delete[] text; + retText->Store(textStr); + }; + ExecTask(task, __func__); + return retText->Load(); } int32_t NativeTextChangedListener::GetTextIndexAtCursor() { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return 0; - } - - if (textEditor_->getTextIndexAtCursorFunc == nullptr) { - IMSA_HILOGE("getTextIndexAtCursorFunc is nullptr"); - return 0; - } - - return textEditor_->getTextIndexAtCursorFunc(textEditor_); + auto weakThis = wptr(this); + auto retIndex = std::make_shared(0); + auto task = [weakThis, retIndex]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->getTextIndexAtCursorFunc == nullptr) { + IMSA_HILOGE("getTextIndexAtCursorFunc is nullptr"); + return; + } + + retIndex->store(promoteThis->textEditor_->getTextIndexAtCursorFunc(promoteThis->textEditor_)); + }; + + ExecTask(task, __func__); + return retIndex->load(); } int32_t NativeTextChangedListener::ReceivePrivateCommand( const std::unordered_map &privateCommand) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return ErrorCode::ERROR_NULL_POINTER; - } - - if (textEditor_->receivePrivateCommandFunc == nullptr) { - IMSA_HILOGE("receivePrivateCommandFunc is nullptr"); - return ErrorCode::ERROR_NULL_POINTER; - } - - InputMethod_PrivateCommand **command = new (std::nothrow) InputMethod_PrivateCommand *[privateCommand.size()]; - if (command == nullptr) { - IMSA_HILOGE("command is nullptr"); - return ErrorCode::ERROR_NULL_POINTER; - } - - size_t index = 0; - for (const auto &item : privateCommand) { - command[index] = new InputMethod_PrivateCommand(); - command[index]->key = item.first; - command[index]->value = item.second; - ++index; - } - - auto errCode = textEditor_->receivePrivateCommandFunc(textEditor_, command, privateCommand.size()); - - for (size_t i = 0; i < index; ++i) { - delete command[i]; - } - delete[] command; - return errCode; + auto weakThis = wptr(this); + auto result = std::make_shared(ErrorCode::ERROR_NULL_POINTER); + auto task = [weakThis, privateCommand, result]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->receivePrivateCommandFunc == nullptr) { + IMSA_HILOGE("receivePrivateCommandFunc is nullptr"); + return; + } + + InputMethod_PrivateCommand **command = new (std::nothrow) InputMethod_PrivateCommand *[privateCommand.size()]; + if (command == nullptr) { + IMSA_HILOGE("command is nullptr"); + return; + } + + size_t index = 0; + for (const auto &item : privateCommand) { + command[index] = new InputMethod_PrivateCommand(); + command[index]->key = item.first; + command[index]->value = item.second; + ++index; + } + + auto errCode = promoteThis->textEditor_->receivePrivateCommandFunc( + promoteThis->textEditor_, command, privateCommand.size()); + + for (size_t i = 0; i < index; ++i) { + delete command[i]; + } + delete[] command; + result->store(errCode); + }; + + ExecTask(task, __func__); + return *result; } int32_t NativeTextChangedListener::SetPreviewText(const std::u16string &text, const OHOS::MiscServices::Range &range) { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return ErrorCode::ERROR_NULL_POINTER; - } - - if (textEditor_->setPreviewTextFunc == nullptr) { - IMSA_HILOGE("setPreviewTextFunc is nullptr"); - return ErrorCode::ERROR_NULL_POINTER; - } - - return textEditor_->setPreviewTextFunc(textEditor_, text.c_str(), text.length(), range.start, range.end); + auto weakThis = wptr(this); + auto result = std::make_shared(ErrorCode::ERROR_NULL_POINTER); + auto task = [weakThis, text, range, result]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->setPreviewTextFunc == nullptr) { + IMSA_HILOGE("setPreviewTextFunc is nullptr"); + return; + } + + auto ret = promoteThis->textEditor_->setPreviewTextFunc( + promoteThis->textEditor_, text.c_str(), text.length(), range.start, range.end); + + result->store(ret); + }; + ExecTask(task, __func__); + return *result; } void NativeTextChangedListener::FinishTextPreview() { - if (textEditor_ == nullptr) { - IMSA_HILOGE("textEditor_ is nullptr"); - return; - } - - if (textEditor_->finishTextPreviewFunc == nullptr) { - IMSA_HILOGE("finishTextPreviewFunc is nullptr"); - return; - } - - textEditor_->finishTextPreviewFunc(textEditor_); + auto weakThis = wptr(this); + auto task = [weakThis]() { + auto promoteThis = weakThis.promote(); + if (promoteThis == nullptr) { + IMSA_HILOGE("promoteThis is nullptr"); + return; + } + if (promoteThis->textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr"); + return; + } + + if (promoteThis->textEditor_->finishTextPreviewFunc == nullptr) { + IMSA_HILOGE("finishTextPreviewFunc is nullptr"); + return; + } + + promoteThis->textEditor_->finishTextPreviewFunc(promoteThis->textEditor_); + }; + ExecTask(task, __func__); } void NativeTextChangedListener::OnDetach() @@ -363,5 +519,79 @@ InputMethod_ExtendAction NativeTextChangedListener::ConvertToCExtendAction(int32 return IME_EXTEND_ACTION_SELECT_ALL; } } + +int32_t NativeTextChangedListener::PostToMainThreadSync(std::function func, const std::string &name) +{ + if (AppExecFwk::EventRunner::IsAppMainThread()) { + IMSA_HILOGW("%{public}s in main thread, no need post", name.c_str()); + func(); + return IME_ERR_OK; + } + + std::lock_guard lock(mainHandlerMtx_); + if (mainHandler_ == nullptr && !CreateMainHandler()) { + IMSA_HILOGE("mainHandler_ is null, name:%{public}s", name.c_str()); + return ErrorCode::ERROR_NULL_POINTER; + } + auto isFinished = std::make_shared>(MAX_SYNC_TIMEOUT, false); + auto syncTask = [func, isFinished, name]() { + IMSA_HILOGD("syncTask enter, name:%{public}s", name.c_str()); + if (isFinished->IsTimeout()) { + IMSA_HILOGE("syncTask exit for time out, name:%{public}s", name.c_str()); + return; + } + + func(); + isFinished->SetValue(true); + }; + + auto ret = mainHandler_->PostTask(std::move(syncTask), name, 0, AppExecFwk::EventQueue::Priority::IMMEDIATE); + if (!ret) { + IMSA_HILOGE("PostTask failed, name:%{public}s", name.c_str()); + return ErrorCode::ERROR_IMC_NULLPTR; + } + + if (!isFinished->GetValue()) { + IMSA_HILOGW("Wait for task to finish timeout, name:%{public}s", name.c_str()); + } + return IME_ERR_OK; +} + +int32_t NativeTextChangedListener::ExecTask(std::function func, const std::string &name) +{ + if (textEditor_ == nullptr) { + IMSA_HILOGE("textEditor_ is nullptr, name:%{public}s", name.c_str()); + return ErrorCode::ERROR_IMC_NULLPTR; + } + + if (textEditor_->isCallbakcInMainThread) { + return PostToMainThreadSync(func, name); + } + + func(); + return ErrorCode::NO_ERROR; +} + +bool NativeTextChangedListener::CreateMainHandler() +{ + if (mainHandler_ != nullptr) { + IMSA_HILOGE("mainHandler_ is not nullptr"); + return false; + } + auto mainRunner = AppExecFwk::EventRunner::GetMainEventRunner(); + if (mainRunner == nullptr) { + IMSA_HILOGE("GetMainEventRunner failed"); + return false; + } + + mainHandler_ = std::make_shared(mainRunner); + if (mainHandler_ == nullptr) { + IMSA_HILOGE("create mainHandler_ failed"); + return false; + } + + IMSA_HILOGI("success"); + return true; +} } // namespace MiscServices } // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h index 3a982ae5b..5d8d392ae 100644 --- a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h +++ b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h @@ -695,6 +695,30 @@ InputMethod_ErrorCode OH_TextEditorProxy_GetSetPreviewTextFunc( */ InputMethod_ErrorCode OH_TextEditorProxy_GetFinishTextPreviewFunc( InputMethod_TextEditorProxy *proxy, OH_TextEditorProxy_FinishTextPreviewFunc *finishTextPreviewFunc); + +/** + * @brief Set whether to call callback function in main thread. + * + * @param proxy Represents a pointer to an {@link InputMethod_TextEditorProxy} instance which will be set. + * @param isCallbackInMainThread Represents whether to call callback function in main thread. + * @return Returns a specific error code. + * {@link IME_ERR_OK} - success. + * {@link IME_ERR_NULL_POINTER} - unexpected null pointer. + * @since 20 + */ +InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( + InputMethod_TextEditorProxy *proxy, bool isCallbackInMainThread); + +/** + * @brief Get whether to call callback function in main thread. + * @param proxy Represents a pointer to an {@link InputMethod_TextEditorProxy} instance which will be get. + * @return Returns a specific error code. + * {@link IME_ERR_OK} - success. + * {@link IME_ERR_NULL_POINTER} - unexpected null pointer. + * @since 20 + */ +InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( + InputMethod_TextEditorProxy *proxy, bool *isCallbackInMainThread); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/unittest/cpp_test/BUILD.gn b/test/unittest/cpp_test/BUILD.gn index 020c2faba..f776c95d3 100644 --- a/test/unittest/cpp_test/BUILD.gn +++ b/test/unittest/cpp_test/BUILD.gn @@ -1261,6 +1261,7 @@ ohos_unittest("ImeControllerCpaiTest") { deps = [ "${inputmethod_path}/frameworks/ndk:ohinputmethod_static", "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client_static", + "${inputmethod_path}/test/unittest/cpp_test/common:inputmethod_tdd_util", ] external_deps = [ @@ -1269,7 +1270,14 @@ ohos_unittest("ImeControllerCpaiTest") { "hilog:libhilog", "input:libmmi-client", "ipc:ipc_single", + "bundle_framework:appexecfwk_core", ] + + if (window_manager_use_sceneboard) { + external_deps += [ "window_manager:libwm_lite" ] + } else { + external_deps += [ "window_manager:libwm" ] + } } ohos_unittest("FullImeInfoManagerTest") { diff --git a/test/unittest/cpp_test/src/inputmethod_controller_capi_test.cpp b/test/unittest/cpp_test/src/inputmethod_controller_capi_test.cpp index 52f4e19bf..e82b4c9c8 100644 --- a/test/unittest/cpp_test/src/inputmethod_controller_capi_test.cpp +++ b/test/unittest/cpp_test/src/inputmethod_controller_capi_test.cpp @@ -1881,9 +1881,8 @@ HWTEST_F(InputMethodControllerCapiTest, TestAttachWithPlaceholderAndAbility_001, auto textEditorProxy2 = OH_TextEditorProxy_Create(); EXPECT_NE(nullptr, textEditorProxy2); ConstructTextEditorProxy(textEditorProxy2); - auto fnGetTextConfigFunc = [](InputMethod_TextEditorProxy *textEditorProxy, - InputMethod_TextConfig *config) { - std::u16string placeholder = u"test placeholder"; + auto fnGetTextConfigFunc = [](InputMethod_TextEditorProxy *textEditorProxy, InputMethod_TextConfig *config) { + std::u16string placeholder = u"test placeholder"; std::u16string abilityName = u"test ability name"; OH_TextConfig_SetPlaceholder(config, placeholder.data(), placeholder.size()); OH_TextConfig_SetAbilityName(config, abilityName.data(), abilityName.size()); @@ -1907,7 +1906,7 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_001, TestSi { auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); - std::u16string input= u"test"; + std::u16string input = u"test"; auto ret = OH_TextConfig_SetPlaceholder(config, input.data(), input.size()); EXPECT_EQ(ret, IME_ERR_OK); ret = OH_TextConfig_GetPlaceholder(nullptr, nullptr, nullptr); @@ -1921,11 +1920,11 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_001, TestSi outLen = 1; ret = OH_TextConfig_GetPlaceholder(config, pOut, &outLen); EXPECT_EQ(ret, IME_ERR_PARAMCHECK); - EXPECT_EQ(outLen -1, input.size()); + EXPECT_EQ(outLen - 1, input.size()); outLen = 513; ret = OH_TextConfig_GetPlaceholder(config, pOut, &outLen); EXPECT_EQ(ret, IME_ERR_OK); - EXPECT_EQ(outLen -1, input.size()); + EXPECT_EQ(outLen - 1, input.size()); outLen = input.size(); ret = OH_TextConfig_GetPlaceholder(config, pOut, &outLen); EXPECT_EQ(ret, IME_ERR_PARAMCHECK); @@ -1942,10 +1941,11 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_001, TestSi * @tc.desc: Invalid test input parameter * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_002, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_002, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); - std::u16string input= u"test"; + std::u16string input = u"test"; auto ret = OH_TextConfig_SetPlaceholder(config, input.data(), input.size()); EXPECT_EQ(ret, IME_ERR_OK); ret = OH_TextConfig_SetPlaceholder(config, nullptr, 0); @@ -1963,7 +1963,8 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_002, TestSi * @tc.desc: Invalid test input parameter * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_003, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_003, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); auto ret = OH_TextConfig_SetPlaceholder(config, nullptr, 257); @@ -2006,10 +2007,11 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetPlaceholder_003, TestSi * @tc.desc: Input parameters are valid * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_001, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_001, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); - std::u16string input= u"test"; + std::u16string input = u"test"; auto ret = OH_TextConfig_SetAbilityName(config, input.data(), input.size()); EXPECT_EQ(ret, IME_ERR_OK); ret = OH_TextConfig_GetAbilityName(nullptr, nullptr, nullptr); @@ -2032,8 +2034,7 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_001, TestSi ret = OH_TextConfig_GetAbilityName(config, pOut, &outLen); EXPECT_EQ(ret, IME_ERR_OK); std::u16string out(pOut, outLen); - IMSA_HILOGI("outLen:%{public}zu,out:%{public}s,outSize:%{public}zu", outLen, - Str16ToStr8(out).c_str(), out.size()); + IMSA_HILOGI("outLen:%{public}zu,out:%{public}s,outSize:%{public}zu", outLen, Str16ToStr8(out).c_str(), out.size()); EXPECT_GT(out.size(), input.size()); outLen = input.size(); ret = OH_TextConfig_GetAbilityName(config, pOut, &outLen); @@ -2046,10 +2047,11 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_001, TestSi * @tc.desc: Invalid test input parameter * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_002, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_002, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); - std::u16string input= u"test"; + std::u16string input = u"test"; auto ret = OH_TextConfig_SetAbilityName(config, input.data(), input.size()); EXPECT_EQ(ret, IME_ERR_OK); ret = OH_TextConfig_SetAbilityName(config, nullptr, 0); @@ -2068,7 +2070,8 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_002, TestSi * @tc.desc: Invalid test input parameter * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_003, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_003, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); auto ret = OH_TextConfig_SetAbilityName(config, nullptr, 128); @@ -2090,7 +2093,8 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_003, TestSi * @tc.desc: Invalid test input parameter * @tc.type: FUNC */ -HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_004, TestSize.Level0) { +HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_004, TestSize.Level0) +{ auto config = OH_TextConfig_Create(); ASSERT_NE(nullptr, config); std::u16string input = u""; @@ -2119,7 +2123,7 @@ HWTEST_F(InputMethodControllerCapiTest, OH_TextConfig_SetAbilityName_004, TestSi ret = OH_TextConfig_GetAbilityName(config, out2.data(), &outLen); EXPECT_EQ(ret, IME_ERR_OK); EXPECT_EQ(out2[out2.size() - 1], 0); - input[input.size() -1] = u'\0'; + input[input.size() - 1] = u'\0'; EXPECT_EQ(out2.compare(input), 0); char16_t charInput[65] = u"123456789\0123456789\0012345678901\023456789"; size_t charInputLen = 32; diff --git a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp index 120c8475b..3545bfef3 100644 --- a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp +++ b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp @@ -12,18 +12,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "native_text_changed_listener.h" - #include +#include "native_text_changed_listener.h" using namespace testing::ext; namespace OHOS { namespace MiscServices { -class NativeTextChangedListenerTest : public testing::Test { }; +class NativeTextChangedListenerTest : public testing::Test { +public: + static void SetUpTestSuite() + { + // 100000 means the uid of app + setuid(100000); + textEditor_ = OH_TextEditorProxy_Create(); + ASSERT_NE(textEditor_, nullptr) << "Failed to create InputMethod_TextEditorProxy"; + ConstructTextEditorProxy(textEditor_); + } + + static void TearDownTestSuite() + { + setuid(0); + if (textEditor_ != nullptr) { + OH_TextEditorProxy_Destroy(textEditor_); + textEditor_ = nullptr; + } + } + + void SetUp() override { + ConstructTextEditorProxy(textEditor_); + } -static void TestNativeTextChangedListener(NativeTextChangedListener *listener) + static InputMethod_TextEditorProxy *textEditor_; + + static void ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProxy); +}; + +InputMethod_TextEditorProxy *NativeTextChangedListenerTest::textEditor_ = nullptr; +static void TestNativeTextChangedListener(sptr listener) { listener->InsertText(u""); listener->DeleteForward(0); @@ -49,11 +75,13 @@ static void TestNativeTextChangedListener(NativeTextChangedListener *listener) */ HWTEST_F(NativeTextChangedListenerTest, NativeTextChangedListener_001, TestSize.Level1) { - NativeTextChangedListener listener(nullptr); - TestNativeTextChangedListener(&listener); + IMSA_HILOGI("NativeTextChangedListenerTest::NativeTextChangedListener_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(nullptr); + ASSERT_NE(nullptr, listener); + TestNativeTextChangedListener(listener); std::unordered_map privateCommand; - EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener.ReceivePrivateCommand(privateCommand)); - EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener.SetPreviewText(u"", Range({ 0, 0 }))); + EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener->ReceivePrivateCommand(privateCommand)); + EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener->SetPreviewText(u"", Range({ 0, 0 }))); } /** @@ -63,13 +91,15 @@ HWTEST_F(NativeTextChangedListenerTest, NativeTextChangedListener_001, TestSize. */ HWTEST_F(NativeTextChangedListenerTest, CallbackIsNullTest_001, TestSize.Level1) { + IMSA_HILOGI("NativeTextChangedListenerTest::CallbackIsNullTest_001 start"); auto textEditor = OH_TextEditorProxy_Create(); EXPECT_NE(nullptr, textEditor); - NativeTextChangedListener listener(textEditor); - TestNativeTextChangedListener(&listener); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor); + ASSERT_NE(nullptr, listener); + TestNativeTextChangedListener(listener); std::unordered_map privateCommand; - EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener.ReceivePrivateCommand(privateCommand)); - EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener.SetPreviewText(u"", Range({ 0, 0 }))); + EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener->ReceivePrivateCommand(privateCommand)); + EXPECT_EQ(ErrorCode::ERROR_NULL_POINTER, listener->SetPreviewText(u"", Range({ 0, 0 }))); OH_TextEditorProxy_Destroy(textEditor); } @@ -105,7 +135,7 @@ int32_t SetPreviewText( return 0; } void FinishTextPreview(InputMethod_TextEditorProxy *proxy) { } -static void ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProxy) +void NativeTextChangedListenerTest::ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProxy) { EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetGetTextConfigFunc(textEditorProxy, GetTextConfig)); EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetInsertTextFunc(textEditorProxy, InsertText)); @@ -122,6 +152,7 @@ static void ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProx EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetReceivePrivateCommandFunc(textEditorProxy, ReceivePrivateCommand)); EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetSetPreviewTextFunc(textEditorProxy, SetPreviewText)); EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetFinishTextPreviewFunc(textEditorProxy, FinishTextPreview)); + EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetCallbackInMainThread(textEditorProxy, false)); } /** @@ -131,15 +162,13 @@ static void ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProx */ HWTEST_F(NativeTextChangedListenerTest, CallbackFinishTest_001, TestSize.Level1) { - auto textEditor = OH_TextEditorProxy_Create(); - EXPECT_NE(nullptr, textEditor); - ConstructTextEditorProxy(textEditor); - NativeTextChangedListener listener(textEditor); - TestNativeTextChangedListener(&listener); + IMSA_HILOGI("NativeTextChangedListenerTest::CallbackFinishTest_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + TestNativeTextChangedListener(listener); std::unordered_map privateCommand; - EXPECT_EQ(0, listener.ReceivePrivateCommand(privateCommand)); - EXPECT_EQ(0, listener.SetPreviewText(u"", Range({ 0, 0 }))); - OH_TextEditorProxy_Destroy(textEditor); + EXPECT_EQ(0, listener->ReceivePrivateCommand(privateCommand)); + EXPECT_EQ(0, listener->SetPreviewText(u"", Range({ 0, 0 }))); } /** @@ -149,13 +178,12 @@ HWTEST_F(NativeTextChangedListenerTest, CallbackFinishTest_001, TestSize.Level1) */ HWTEST_F(NativeTextChangedListenerTest, ConvertKeyBoardStarusTest_001, TestSize.Level1) { - auto textEditor = OH_TextEditorProxy_Create(); - EXPECT_NE(nullptr, textEditor); - ConstructTextEditorProxy(textEditor); - NativeTextChangedListener listener(textEditor); - listener.SendKeyboardStatus(KeyboardStatus::NONE); - listener.SendKeyboardStatus(KeyboardStatus::HIDE); - listener.SendKeyboardStatus(KeyboardStatus::SHOW); + IMSA_HILOGI("NativeTextChangedListenerTest::ConvertKeyBoardStarusTest_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + listener->SendKeyboardStatus(KeyboardStatus::NONE); + listener->SendKeyboardStatus(KeyboardStatus::HIDE); + listener->SendKeyboardStatus(KeyboardStatus::SHOW); } /** * @tc.name: ConvertToCEnterKeyTypeTest_001 @@ -164,29 +192,28 @@ HWTEST_F(NativeTextChangedListenerTest, ConvertKeyBoardStarusTest_001, TestSize. */ HWTEST_F(NativeTextChangedListenerTest, ConvertToCEnterKeyTypeTest_001, TestSize.Level1) { - auto textEditor = OH_TextEditorProxy_Create(); - EXPECT_NE(nullptr, textEditor); - ConstructTextEditorProxy(textEditor); - NativeTextChangedListener listener(textEditor); + IMSA_HILOGI("NativeTextChangedListenerTest::ConvertToCEnterKeyTypeTest_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); FunctionKey key; key.SetEnterKeyType(EnterKeyType::UNSPECIFIED); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::NONE); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::GO); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::SEARCH); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::SEND); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::NEXT); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::DONE); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::PREVIOUS); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); key.SetEnterKeyType(EnterKeyType::NEW_LINE); - listener.SendFunctionKey(key); + listener->SendFunctionKey(key); } /** @@ -196,16 +223,16 @@ HWTEST_F(NativeTextChangedListenerTest, ConvertToCEnterKeyTypeTest_001, TestSize */ HWTEST_F(NativeTextChangedListenerTest, ConvertToCDirectionTest_001, TestSize.Level1) { - auto textEditor = OH_TextEditorProxy_Create(); - EXPECT_NE(nullptr, textEditor); - ConstructTextEditorProxy(textEditor); - NativeTextChangedListener listener(textEditor); - listener.MoveCursor(OHOS::MiscServices::Direction::NONE); - listener.MoveCursor(OHOS::MiscServices::Direction::UP); - listener.MoveCursor(OHOS::MiscServices::Direction::DOWN); - listener.MoveCursor(OHOS::MiscServices::Direction::RIGHT); - listener.MoveCursor(OHOS::MiscServices::Direction::LEFT); + IMSA_HILOGI("NativeTextChangedListenerTest::ConvertToCDirectionTest_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + listener->MoveCursor(OHOS::MiscServices::Direction::NONE); + listener->MoveCursor(OHOS::MiscServices::Direction::UP); + listener->MoveCursor(OHOS::MiscServices::Direction::DOWN); + listener->MoveCursor(OHOS::MiscServices::Direction::RIGHT); + listener->MoveCursor(OHOS::MiscServices::Direction::LEFT); } + /** * @tc.name: ConvertToCExtendActionTest_001 * @tc.desc: Test ConvertToCExtendAction. @@ -213,14 +240,95 @@ HWTEST_F(NativeTextChangedListenerTest, ConvertToCDirectionTest_001, TestSize.Le */ HWTEST_F(NativeTextChangedListenerTest, ConvertToCExtendActionTest_001, TestSize.Level1) { - auto textEditor = OH_TextEditorProxy_Create(); - EXPECT_NE(nullptr, textEditor); - ConstructTextEditorProxy(textEditor); - NativeTextChangedListener listener(textEditor); - listener.HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::SELECT_ALL)); - listener.HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::COPY)); - listener.HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::CUT)); - listener.HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::PASTE)); + IMSA_HILOGI("NativeTextChangedListenerTest::ConvertToCExtendActionTest_001 start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + listener->HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::SELECT_ALL)); + listener->HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::COPY)); + listener->HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::CUT)); + listener->HandleExtendAction(static_cast(OHOS::MiscServices::ExtendAction::PASTE)); +} + +/** + * @tc.name: CallBackTimeOut_FAIL + * @tc.desc: test callback timeout + * @tc.type: FUNC + */ +HWTEST_F(NativeTextChangedListenerTest, CallBackTimeOut_FAIL, TestSize.Level1) +{ + IMSA_HILOGI("NativeTextChangedListenerTest::CallBackTimeOut_FAIL start"); + auto func = []() { + OH_TextEditorProxy_SetCallbackInMainThread(textEditor_, true); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + OH_TextEditorProxy_GetRightTextOfCursorFunc cbFunc = [](InputMethod_TextEditorProxy *, int32_t number, + char16_t text[], size_t *length) { + for (size_t i = 0; i < number; i++) { + text[i] = 'a'; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }; + + OH_TextEditorProxy_SetGetRightTextOfCursorFunc(textEditor_, cbFunc); + auto str16 = listener->GetRightTextOfCursor(5); + EXPECT_EQ(str16, u""); + }; + + std::thread noMainThread(func); + noMainThread.join(); +} + +/** + * @tc.name: CallInNonMainThreadAndNoPostTest_SUCCESS + * @tc.desc: test call in non main thread and not post to main thread + * @tc.type: FUNC + */ +HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS, TestSize.Level1) +{ + IMSA_HILOGI("NativeTextChangedListenerTest::CallInNonMainThreadAndNoPostTest_SUCCESS start"); + auto func = []() { + bool isCallbakcInMainThread = false; + EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, &isCallbakcInMainThread)); + EXPECT_FALSE(isCallbakcInMainThread); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + TestNativeTextChangedListener(listener); + std::unordered_map privateCommand; + EXPECT_EQ(0, listener->ReceivePrivateCommand(privateCommand)); + EXPECT_EQ(0, listener->SetPreviewText(u"", Range({ 0, 0 }))); + OH_TextEditorProxy_GetRightTextOfCursorFunc cbFunc = [](InputMethod_TextEditorProxy *, int32_t number, + char16_t text[], size_t *length) { + for (size_t i = 0; i < number; i++) { + text[i] = 'a'; + } + *length = number; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }; + + OH_TextEditorProxy_SetGetRightTextOfCursorFunc(textEditor_, cbFunc); + auto realStr = listener->GetRightTextOfCursor(5); + std::u16string expectStr(u"aaaaa", 5); + EXPECT_EQ(realStr, expectStr); + }; + + std::thread noMainThread(func); + noMainThread.join(); +} + +/** + * @tc.name: CallbackInMainThread_ReturnNullPointerErr + * @tc.desc: test invalid param + * @tc.type: FUNC + */ +HWTEST_F(NativeTextChangedListenerTest, CallbackInMainThread_ReturnNullPointerErr, TestSize.Level1) +{ + InputMethod_ErrorCode ret = OH_TextEditorProxy_SetCallbackInMainThread(nullptr, true); + EXPECT_EQ(ret, IME_ERR_NULL_POINTER); + bool isCallbackInMainThread = false; + ret = OH_TextEditorProxy_IsCallbackInMainThread(nullptr, &isCallbackInMainThread); + EXPECT_EQ(ret, IME_ERR_NULL_POINTER); + ret = OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, nullptr); + EXPECT_EQ(ret, IME_ERR_NULL_POINTER); } } // namespace MiscServices } // namespace OHOS \ No newline at end of file -- Gitee From cd8104d481edaf819dbd198c46a040db9dfb39ab Mon Sep 17 00:00:00 2001 From: wuchengwen Date: Thu, 4 Sep 2025 11:45:21 +0800 Subject: [PATCH 2/4] fix:tdd crash and ndk docs Signed-off-by: wuchengwen --- .../ndk/include/native_inputmethod_types.h | 2 +- .../src/inputmethod_texteditor_proxy_capi.cpp | 9 +++-- .../ndk/src/native_text_changed_listener.cpp | 26 +++++++++----- .../c/inputmethod_text_editor_proxy_capi.h | 36 ++++++++++++------- .../src/native_text_changed_listener_test.cpp | 6 ++-- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/frameworks/ndk/include/native_inputmethod_types.h b/frameworks/ndk/include/native_inputmethod_types.h index 22c6e95d5..128e90328 100644 --- a/frameworks/ndk/include/native_inputmethod_types.h +++ b/frameworks/ndk/include/native_inputmethod_types.h @@ -80,7 +80,7 @@ struct InputMethod_TextEditorProxy { OH_TextEditorProxy_ReceivePrivateCommandFunc receivePrivateCommandFunc; OH_TextEditorProxy_SetPreviewTextFunc setPreviewTextFunc; OH_TextEditorProxy_FinishTextPreviewFunc finishTextPreviewFunc; - std::atomic_bool isCallbakcInMainThread = false; + std::atomic_bool isCallbackInMainThread = false; }; struct InputMethod_AttachOptions { diff --git a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp index 82a8b3f83..277202059 100644 --- a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp +++ b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp @@ -513,7 +513,7 @@ InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( return IME_ERR_NULL_POINTER; } IMSA_HILOGI("isCallbackInMainThread: %{public}d", isCallbackInMainThread); - proxy->isCallbakcInMainThread = isCallbackInMainThread; + proxy->isCallbackInMainThread = isCallbackInMainThread; return IME_ERR_OK; } @@ -525,6 +525,11 @@ InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( return IME_ERR_NULL_POINTER; } - *isCallbackInMainThread = proxy->isCallbakcInMainThread; + if (isCallbackInMainThread == nullptr) { + IMSA_HILOGE("isCallbackInMainThread is nullptr"); + return IME_ERR_NULL_POINTER; + } + + *isCallbackInMainThread = proxy->isCallbackInMainThread; return IME_ERR_OK; } \ No newline at end of file diff --git a/frameworks/ndk/src/native_text_changed_listener.cpp b/frameworks/ndk/src/native_text_changed_listener.cpp index 5208b46a0..03f157cd9 100644 --- a/frameworks/ndk/src/native_text_changed_listener.cpp +++ b/frameworks/ndk/src/native_text_changed_listener.cpp @@ -367,9 +367,21 @@ int32_t NativeTextChangedListener::ReceivePrivateCommand( return; } + auto freeCommand = [command](size_t index) { + for (size_t i = 0; i < index; i++) { + delete command[i]; + } + delete[] command; + }; + size_t index = 0; for (const auto &item : privateCommand) { - command[index] = new InputMethod_PrivateCommand(); + command[index] = new (std::nothrow) InputMethod_PrivateCommand(); + if (command[index] == nullptr) { + IMSA_HILOGE("new InputMethod_PrivateCommand failed"); + freeCommand(index); + return; + } command[index]->key = item.first; command[index]->value = item.second; ++index; @@ -378,10 +390,7 @@ int32_t NativeTextChangedListener::ReceivePrivateCommand( auto errCode = promoteThis->textEditor_->receivePrivateCommandFunc( promoteThis->textEditor_, command, privateCommand.size()); - for (size_t i = 0; i < index; ++i) { - delete command[i]; - } - delete[] command; + freeCommand(index); result->store(errCode); }; @@ -553,6 +562,7 @@ int32_t NativeTextChangedListener::PostToMainThreadSync(std::function fu if (!isFinished->GetValue()) { IMSA_HILOGW("Wait for task to finish timeout, name:%{public}s", name.c_str()); + return ErrorCode::ERROR_IMC_NULLPTR; } return IME_ERR_OK; } @@ -564,7 +574,7 @@ int32_t NativeTextChangedListener::ExecTask(std::function func, const st return ErrorCode::ERROR_IMC_NULLPTR; } - if (textEditor_->isCallbakcInMainThread) { + if (textEditor_->isCallbackInMainThread) { return PostToMainThreadSync(func, name); } @@ -575,8 +585,8 @@ int32_t NativeTextChangedListener::ExecTask(std::function func, const st bool NativeTextChangedListener::CreateMainHandler() { if (mainHandler_ != nullptr) { - IMSA_HILOGE("mainHandler_ is not nullptr"); - return false; + IMSA_HILOGW("mainHandler_ is not nullptr"); + return true; } auto mainRunner = AppExecFwk::EventRunner::GetMainEventRunner(); if (mainRunner == nullptr) { diff --git a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h index 5d8d392ae..4c93824b4 100644 --- a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h +++ b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h @@ -697,24 +697,36 @@ InputMethod_ErrorCode OH_TextEditorProxy_GetFinishTextPreviewFunc( InputMethod_TextEditorProxy *proxy, OH_TextEditorProxy_FinishTextPreviewFunc *finishTextPreviewFunc); /** - * @brief Set whether to call callback function in main thread. - * - * @param proxy Represents a pointer to an {@link InputMethod_TextEditorProxy} instance which will be set. - * @param isCallbackInMainThread Represents whether to call callback function in main thread. - * @return Returns a specific error code. - * {@link IME_ERR_OK} - success. - * {@link IME_ERR_NULL_POINTER} - unexpected null pointer. + * @brief Configure the execution thread (main thread/IPC thread) for the callback functions of + * InputMethod_TextEditorProxy. + * This interface only controls all callbacks in InputMethod_TextEditorProxy except + * OH_TextEditorProxy_GetTextConfigFunc. + * The execution thread of OH_TextEditorProxy_GetTextConfigFunc is determined by the thread that calls + * OH_InputMethodController_Attach and is not affected by this interface. + * + * @param proxy Pointer to the target InputMethod_TextEditorProxy instance. + * @param isCallbackInMainThread Thread execution strategy + * - true: The callback function is switched to the main thread for execution (to avoid + * multi-thread concurrency) + * - false: The callback function is executed in the IPC thread (there may be multi-thread + * concurrency) + * @return Execution result. + * {@link IME_ERR_OK} - Configuration succeeded. + * {@link IME_ERR_NULL_POINTER} - Returned when proxy is NULL. * @since 20 */ InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( InputMethod_TextEditorProxy *proxy, bool isCallbackInMainThread); /** - * @brief Get whether to call callback function in main thread. - * @param proxy Represents a pointer to an {@link InputMethod_TextEditorProxy} instance which will be get. - * @return Returns a specific error code. - * {@link IME_ERR_OK} - success. - * {@link IME_ERR_NULL_POINTER} - unexpected null pointer. + * @brief Query the execution thread configuration of the callback functions of InputMethod_TextEditorProxy. + * @param proxy Pointer to the target InputMethod_TextEditorProxy instance. + * @param isCallbackInMainThread Output parameter used to return the current thread execution strategy. + * - true: The callback is executed in the main thread + * - false: The callback is executed in the IPC thread + * @return Execution result. + * {@link IME_ERR_OK} - Query succeeded. + * {@link IME_ERR_NULL_POINTER} - Returned when proxy or isCallbackInMainThread is NULL. * @since 20 */ InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( diff --git a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp index 3545bfef3..b413c9367 100644 --- a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp +++ b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp @@ -287,9 +287,9 @@ HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS { IMSA_HILOGI("NativeTextChangedListenerTest::CallInNonMainThreadAndNoPostTest_SUCCESS start"); auto func = []() { - bool isCallbakcInMainThread = false; - EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, &isCallbakcInMainThread)); - EXPECT_FALSE(isCallbakcInMainThread); + bool isCallbackInMainThread = false; + EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, &isCallbackInMainThread)); + EXPECT_FALSE(isCallbackInMainThread); sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); ASSERT_NE(nullptr, listener); TestNativeTextChangedListener(listener); -- Gitee From c188dfebd6a5b1e054ba06f3b067207bfeca4e2b Mon Sep 17 00:00:00 2001 From: wuchengwen Date: Thu, 4 Sep 2025 11:49:13 +0800 Subject: [PATCH 3/4] fix:delete OH_TextEditorProxy_IsCallbackInMainThread Signed-off-by: wuchengwen --- .../src/inputmethod_texteditor_proxy_capi.cpp | 17 --------------- .../c/inputmethod_text_editor_proxy_capi.h | 14 ------------- .../src/native_text_changed_listener_test.cpp | 21 ++----------------- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp index 277202059..b1915b9e3 100644 --- a/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp +++ b/frameworks/ndk/src/inputmethod_texteditor_proxy_capi.cpp @@ -515,21 +515,4 @@ InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( IMSA_HILOGI("isCallbackInMainThread: %{public}d", isCallbackInMainThread); proxy->isCallbackInMainThread = isCallbackInMainThread; return IME_ERR_OK; -} - -InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( - InputMethod_TextEditorProxy *proxy, bool *isCallbackInMainThread) -{ - if (proxy == nullptr) { - IMSA_HILOGE("proxy is nullptr"); - return IME_ERR_NULL_POINTER; - } - - if (isCallbackInMainThread == nullptr) { - IMSA_HILOGE("isCallbackInMainThread is nullptr"); - return IME_ERR_NULL_POINTER; - } - - *isCallbackInMainThread = proxy->isCallbackInMainThread; - return IME_ERR_OK; } \ No newline at end of file diff --git a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h index 4c93824b4..268ec4885 100644 --- a/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h +++ b/interfaces/kits/c/inputmethod_text_editor_proxy_capi.h @@ -717,20 +717,6 @@ InputMethod_ErrorCode OH_TextEditorProxy_GetFinishTextPreviewFunc( */ InputMethod_ErrorCode OH_TextEditorProxy_SetCallbackInMainThread( InputMethod_TextEditorProxy *proxy, bool isCallbackInMainThread); - -/** - * @brief Query the execution thread configuration of the callback functions of InputMethod_TextEditorProxy. - * @param proxy Pointer to the target InputMethod_TextEditorProxy instance. - * @param isCallbackInMainThread Output parameter used to return the current thread execution strategy. - * - true: The callback is executed in the main thread - * - false: The callback is executed in the IPC thread - * @return Execution result. - * {@link IME_ERR_OK} - Query succeeded. - * {@link IME_ERR_NULL_POINTER} - Returned when proxy or isCallbackInMainThread is NULL. - * @since 20 - */ -InputMethod_ErrorCode OH_TextEditorProxy_IsCallbackInMainThread( - InputMethod_TextEditorProxy *proxy, bool *isCallbackInMainThread); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp index b413c9367..30ea8d344 100644 --- a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp +++ b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp @@ -153,6 +153,8 @@ void NativeTextChangedListenerTest::ConstructTextEditorProxy(InputMethod_TextEdi EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetSetPreviewTextFunc(textEditorProxy, SetPreviewText)); EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetFinishTextPreviewFunc(textEditorProxy, FinishTextPreview)); EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_SetCallbackInMainThread(textEditorProxy, false)); + InputMethod_ErrorCode ret = OH_TextEditorProxy_SetCallbackInMainThread(nullptr, true); + EXPECT_EQ(ret, IME_ERR_NULL_POINTER); } /** @@ -287,9 +289,6 @@ HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS { IMSA_HILOGI("NativeTextChangedListenerTest::CallInNonMainThreadAndNoPostTest_SUCCESS start"); auto func = []() { - bool isCallbackInMainThread = false; - EXPECT_EQ(IME_ERR_OK, OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, &isCallbackInMainThread)); - EXPECT_FALSE(isCallbackInMainThread); sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); ASSERT_NE(nullptr, listener); TestNativeTextChangedListener(listener); @@ -314,21 +313,5 @@ HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS std::thread noMainThread(func); noMainThread.join(); } - -/** - * @tc.name: CallbackInMainThread_ReturnNullPointerErr - * @tc.desc: test invalid param - * @tc.type: FUNC - */ -HWTEST_F(NativeTextChangedListenerTest, CallbackInMainThread_ReturnNullPointerErr, TestSize.Level1) -{ - InputMethod_ErrorCode ret = OH_TextEditorProxy_SetCallbackInMainThread(nullptr, true); - EXPECT_EQ(ret, IME_ERR_NULL_POINTER); - bool isCallbackInMainThread = false; - ret = OH_TextEditorProxy_IsCallbackInMainThread(nullptr, &isCallbackInMainThread); - EXPECT_EQ(ret, IME_ERR_NULL_POINTER); - ret = OH_TextEditorProxy_IsCallbackInMainThread(textEditor_, nullptr); - EXPECT_EQ(ret, IME_ERR_NULL_POINTER); -} } // namespace MiscServices } // namespace OHOS \ No newline at end of file -- Gitee From 0ab4aec2c549fd260d7593560d74e1a9811aff96 Mon Sep 17 00:00:00 2001 From: wuchengwen Date: Thu, 4 Sep 2025 19:27:58 +0800 Subject: [PATCH 4/4] fix:unique_ptr manager memory Signed-off-by: wuchengwen --- .../ndk/src/native_text_changed_listener.cpp | 22 ++++++++++++------- .../src/native_text_changed_listener_test.cpp | 19 ++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/frameworks/ndk/src/native_text_changed_listener.cpp b/frameworks/ndk/src/native_text_changed_listener.cpp index 03f157cd9..c1f0a8d9b 100644 --- a/frameworks/ndk/src/native_text_changed_listener.cpp +++ b/frameworks/ndk/src/native_text_changed_listener.cpp @@ -256,16 +256,19 @@ std::u16string NativeTextChangedListener::GetLeftTextOfCursor(int32_t number) } size_t length = static_cast(number + 1); - char16_t *text = new (std::nothrow) char16_t[length]; + std::unique_ptr text(new (std::nothrow) char16_t[length]); if (text == nullptr) { IMSA_HILOGE("text is nullptr"); return; } - promoteThis->textEditor_->getLeftTextOfCursorFunc(promoteThis->textEditor_, number, text, &length); + promoteThis->textEditor_->getLeftTextOfCursorFunc(promoteThis->textEditor_, number, text.get(), &length); + if (length > number) { + IMSA_HILOGE("length > number"); + return; + } - std::u16string textStr(text, length); - delete[] text; + std::u16string textStr(text.get(), length); retText->Store(textStr); }; ExecTask(task, __func__); @@ -298,15 +301,18 @@ std::u16string NativeTextChangedListener::GetRightTextOfCursor(int32_t number) } size_t length = static_cast(number + 1); - char16_t *text = new (std::nothrow) char16_t[length]; + std::unique_ptr text(new (std::nothrow) char16_t[length]); if (text == nullptr) { IMSA_HILOGE("text is nullptr"); return; } - promoteThis->textEditor_->getRightTextOfCursorFunc(promoteThis->textEditor_, number, text, &length); - std::u16string textStr(text, length); - delete[] text; + promoteThis->textEditor_->getRightTextOfCursorFunc(promoteThis->textEditor_, number, text.get(), &length); + if (length > number) { + IMSA_HILOGE("length > number"); + return; + } + std::u16string textStr(text.get(), length); retText->Store(textStr); }; ExecTask(task, __func__); diff --git a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp index 30ea8d344..2be636d91 100644 --- a/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp +++ b/test/unittest/cpp_test/src/native_text_changed_listener_test.cpp @@ -293,6 +293,7 @@ HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS ASSERT_NE(nullptr, listener); TestNativeTextChangedListener(listener); std::unordered_map privateCommand; + privateCommand.insert({ "key", "value" }); EXPECT_EQ(0, listener->ReceivePrivateCommand(privateCommand)); EXPECT_EQ(0, listener->SetPreviewText(u"", Range({ 0, 0 }))); OH_TextEditorProxy_GetRightTextOfCursorFunc cbFunc = [](InputMethod_TextEditorProxy *, int32_t number, @@ -313,5 +314,23 @@ HWTEST_F(NativeTextChangedListenerTest, CallInNonMainThreadAndNoPostTest_SUCCESS std::thread noMainThread(func); noMainThread.join(); } + +/** + * @tc.name: CallInMainThreadAndPostToMain_SUCCESS + * @tc.desc: test call in main thread andpost to main thread + * @tc.type: FUNC + */ +HWTEST_F(NativeTextChangedListenerTest, CallInMainThreadAndPostToMain_SUCCESS, TestSize.Level1) +{ + IMSA_HILOGI("NativeTextChangedListenerTest::CallInMainThreadAndPostToMain_SUCCESS start"); + sptr listener = new (std::nothrow) NativeTextChangedListener(textEditor_); + ASSERT_NE(nullptr, listener); + OH_TextEditorProxy_SetCallbackInMainThread(textEditor_, true); + TestNativeTextChangedListener(listener); + std::unordered_map privateCommand; + privateCommand.insert({ "key", "value" }); + EXPECT_EQ(0, listener->ReceivePrivateCommand(privateCommand)); + EXPECT_EQ(0, listener->SetPreviewText(u"", Range({ 0, 0 }))); +} } // namespace MiscServices } // namespace OHOS \ No newline at end of file -- Gitee