1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "js_fill_request_callback.h"
17 
18 #include "ability_manager_client.h"
19 #include "accesstoken_kit.h"
20 #include "hilog_tag_wrapper.h"
21 #include "int_wrapper.h"
22 #include "ipc_skeleton.h"
23 #include "js_auto_fill_extension_util.h"
24 #include "js_error_utils.h"
25 #include "js_runtime_utils.h"
26 #include "napi_common_util.h"
27 #include "native_engine.h"
28 #include "native_value.h"
29 #include "tokenid_kit.h"
30 #include "ui_content.h"
31 #include "view_data.h"
32 #include "want.h"
33 #include "window.h"
34 
35 namespace OHOS {
36 namespace AbilityRuntime {
37 namespace {
38 constexpr int32_t INDEX_ZERO = 0;
39 constexpr size_t ARGC_ONE = 1;
40 constexpr const char *WANT_PARAMS_VIEW_DATA = "ohos.ability.params.viewData";
41 constexpr const char *WANT_PARAMS_AUTO_FILL_CMD = "ohos.ability.params.autoFillCmd";
42 constexpr const char *WANT_PARAMS_AUTO_FILL_CMD_AUTOFILL = "autofill";
43 constexpr const char *WANT_PARAMS_UPDATE_POPUP_WIDTH = "ohos.ability.params.popupWidth";
44 constexpr const char *WANT_PARAMS_UPDATE_POPUP_HEIGHT = "ohos.ability.params.popupHeight";
45 constexpr const char *WANT_PARAMS_UPDATE_POPUP_PLACEMENT = "ohos.ability.params.popupPlacement";
46 constexpr const char *CONFIG_POPUP_SIZE = "popupSize";
47 constexpr const char *CONFIG_POPUP_PLACEMENT = "placement";
48 constexpr const char *WANT_PARAMS_FILL_CONTENT = "ohos.ability.params.fillContent";
49 constexpr const char *ERROR_MSG_INVALID_EMPTY = "JsonString is empty.";
50 constexpr const char *ERROR_MSG_VIEWDATA_INVALID = "The type of storeld, must be ViewData.";
51 constexpr const char *ERROR_MSG_AUTOFILLPOPUPCONFIG_INVALID =
52     "The type of storeld, must be AutoFillPopupConfig.";
53 constexpr const char *ERROR_MSG_PARAMETER_INVALID =
54     "The storeld can consist of only letters, digits, and underscores(_), and cannot exceed 128 characters.";
55 } // namespace
56 
JsFillRequestCallback(const sptr<AAFwk::SessionInfo> & sessionInfo,const sptr<Rosen::Window> & uiWindow)57 JsFillRequestCallback::JsFillRequestCallback(
58     const sptr<AAFwk::SessionInfo> &sessionInfo, const sptr<Rosen::Window> &uiWindow)
59     : sessionInfo_(sessionInfo), uiWindow_(uiWindow)
60 {}
61 
Finalizer(napi_env env,void * data,void * hint)62 void JsFillRequestCallback::Finalizer(napi_env env, void* data, void *hint)
63 {
64     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
65     std::unique_ptr<JsFillRequestCallback>(static_cast<JsFillRequestCallback*>(data));
66 }
67 
FillRequestSuccess(napi_env env,napi_callback_info info)68 napi_value JsFillRequestCallback::FillRequestSuccess(napi_env env, napi_callback_info info)
69 {
70     GET_NAPI_INFO_AND_CALL(env, info, JsFillRequestCallback, OnFillRequestSuccess);
71 }
72 
FillRequestFailed(napi_env env,napi_callback_info info)73 napi_value JsFillRequestCallback::FillRequestFailed(napi_env env, napi_callback_info info)
74 {
75     GET_NAPI_INFO_AND_CALL(env, info, JsFillRequestCallback, OnFillRequestFailed);
76 }
77 
FillRequestCanceled(napi_env env,napi_callback_info info)78 napi_value JsFillRequestCallback::FillRequestCanceled(napi_env env, napi_callback_info info)
79 {
80     GET_NAPI_INFO_AND_CALL(env, info, JsFillRequestCallback, OnFillRequestCanceled);
81 }
82 
FillRequestAutoFillPopupConfig(napi_env env,napi_callback_info info)83 napi_value JsFillRequestCallback::FillRequestAutoFillPopupConfig(napi_env env, napi_callback_info info)
84 {
85     GET_NAPI_INFO_AND_CALL(env, info, JsFillRequestCallback, OnFillRequestAutoFillPopupConfig);
86 }
87 
OnFillRequestSuccess(napi_env env,NapiCallbackInfo & info)88 napi_value JsFillRequestCallback::OnFillRequestSuccess(napi_env env, NapiCallbackInfo &info)
89 {
90     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
91     if (info.argc < ARGC_ONE || !IsTypeForNapiValue(env, info.argv[INDEX_ZERO], napi_object)) {
92         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "parse viewData failed");
93         ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), ERROR_MSG_VIEWDATA_INVALID);
94         SendResultCodeAndViewData(
95             JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_FAILED_INVALID_PARAM, "");
96         return CreateJsUndefined(env);
97     }
98 
99     FillResponse response;
100     JsAutoFillExtensionUtil::UnwrapFillResponse(env, info.argv[INDEX_ZERO], response);
101     std::string jsonString = response.viewData.ToJsonString();
102     if (jsonString.empty()) {
103         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "empty jsonString");
104         ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
105         SendResultCodeAndViewData(
106             JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_FAILED_INVALID_PARAM, "");
107         return CreateJsUndefined(env);
108     }
109 
110     SendResultCodeAndViewData(JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_SUCESS, jsonString);
111     return CreateJsUndefined(env);
112 }
113 
OnFillRequestFailed(napi_env env,NapiCallbackInfo & info)114 napi_value JsFillRequestCallback::OnFillRequestFailed(napi_env env, NapiCallbackInfo &info)
115 {
116     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
117     SendResultCodeAndViewData(JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_FAILED, "");
118     return CreateJsUndefined(env);
119 }
120 
OnFillRequestCanceled(napi_env env,NapiCallbackInfo & info)121 napi_value JsFillRequestCallback::OnFillRequestCanceled(napi_env env, NapiCallbackInfo &info)
122 {
123     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
124     if (info.argc < ARGC_ONE) {
125         SendResultCodeAndViewData(JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_CANCEL, "");
126         return CreateJsUndefined(env);
127     }
128     if (!IsTypeForNapiValue(env, info.argv[INDEX_ZERO], napi_string)) {
129         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "parse fillContent failed");
130         ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), ERROR_MSG_PARAMETER_INVALID);
131         SendResultCodeAndViewData(
132             JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_FAILED_INVALID_PARAM, "");
133         return CreateJsUndefined(env);
134     }
135     std::string jsonString = UnwrapStringFromJS(env, info.argv[INDEX_ZERO], "");
136     if (jsonString.empty()) {
137         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "empty jsonString");
138         ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), ERROR_MSG_INVALID_EMPTY);
139         SendResultCodeAndViewData(
140             JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_FAILED_INVALID_PARAM, "");
141         return CreateJsUndefined(env);
142     }
143     SendResultCodeAndViewData(JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_CANCEL, jsonString);
144     return CreateJsUndefined(env);
145 }
146 
OnFillRequestAutoFillPopupConfig(napi_env env,NapiCallbackInfo & info)147 napi_value JsFillRequestCallback::OnFillRequestAutoFillPopupConfig(napi_env env, NapiCallbackInfo &info)
148 {
149     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
150     auto selfToken = IPCSkeleton::GetSelfTokenID();
151     if (!Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken)) {
152         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "Non-system app forbidden to call");
153         ThrowError(env, AbilityErrorCode::ERROR_CODE_NOT_SYSTEM_APP);
154         return CreateJsUndefined(env);
155     }
156     if (uiWindow_ == nullptr) {
157         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "null uiWindow");
158         ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER);
159         return CreateJsUndefined(env);
160     }
161 
162     if (info.argc < ARGC_ONE || !IsTypeForNapiValue(env, info.argv[INDEX_ZERO], napi_object)) {
163         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "parse resize failed");
164         ThrowError(env, static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM),
165             ERROR_MSG_AUTOFILLPOPUPCONFIG_INVALID);
166         return CreateJsUndefined(env);
167     }
168     AAFwk::WantParams wantParams;
169     wantParams.SetParam(WANT_PARAMS_AUTO_FILL_CMD, AAFwk::Integer::Box(AutoFillCommand::RESIZE));
170     auto isValueChanged = SetPopupConfigToWantParams(env, info, wantParams);
171     if (isValueChanged) {
172         auto ret = uiWindow_->TransferExtensionData(wantParams);
173         if (ret != Rosen::WMError::WM_OK) {
174             TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "Transfer ability result failed");
175             ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER);
176         }
177     }
178     return CreateJsUndefined(env);
179 }
180 
SetPopupConfigToWantParams(napi_env env,NapiCallbackInfo & info,AAFwk::WantParams & wantParams)181 bool JsFillRequestCallback::SetPopupConfigToWantParams(
182     napi_env env, NapiCallbackInfo& info, AAFwk::WantParams& wantParams)
183 {
184     napi_value jsValue = nullptr;
185     bool isValueChanged = false;
186     jsValue = GetPropertyValueByPropertyName(env, info.argv[INDEX_ZERO], CONFIG_POPUP_SIZE, napi_object);
187     if (jsValue) {
188         PopupSize popupSize;
189         JsAutoFillExtensionUtil::UnwrapPopupSize(env, jsValue, popupSize);
190         wantParams.SetParam(WANT_PARAMS_UPDATE_POPUP_WIDTH, AAFwk::Integer::Box(popupSize.width));
191         wantParams.SetParam(WANT_PARAMS_UPDATE_POPUP_HEIGHT, AAFwk::Integer::Box(popupSize.height));
192         isValueChanged = true;
193     }
194 
195     jsValue = nullptr;
196     jsValue = GetPropertyValueByPropertyName(env, info.argv[INDEX_ZERO], CONFIG_POPUP_PLACEMENT, napi_number);
197     if (jsValue) {
198         int popupPlacement = 0;
199         napi_get_value_int32(env, jsValue, &popupPlacement);
200         wantParams.SetParam(WANT_PARAMS_UPDATE_POPUP_PLACEMENT, AAFwk::Integer::Box(popupPlacement));
201         isValueChanged = true;
202     }
203     return isValueChanged;
204 }
205 
SendResultCodeAndViewData(const JsAutoFillExtensionUtil::AutoFillResultCode & resultCode,const std::string & jsString)206 void JsFillRequestCallback::SendResultCodeAndViewData(
207     const JsAutoFillExtensionUtil::AutoFillResultCode &resultCode, const std::string &jsString)
208 {
209     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
210     if (uiWindow_ == nullptr) {
211         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "null uiWindow");
212         return;
213     }
214 
215     AAFwk::Want want;
216     if (resultCode == JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_SUCESS) {
217         want.SetParam(WANT_PARAMS_VIEW_DATA, jsString);
218         want.SetParam(WANT_PARAMS_AUTO_FILL_CMD, WANT_PARAMS_AUTO_FILL_CMD_AUTOFILL);
219     }
220 
221     if (resultCode == JsAutoFillExtensionUtil::AutoFillResultCode::CALLBACK_CANCEL) {
222         want.SetParam(WANT_PARAMS_FILL_CONTENT, jsString);
223     }
224 
225     auto ret = uiWindow_->TransferAbilityResult(resultCode, want);
226     if (ret != Rosen::WMError::WM_OK) {
227         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "TransferAbilityResult failed");
228         return;
229     }
230 
231     auto errorCode = AAFwk::AbilityManagerClient::GetInstance()->TerminateUIExtensionAbility(sessionInfo_);
232     if (errorCode != ERR_OK) {
233         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "TerminateUIExtensionAbility error: %{public}d", errorCode);
234     }
235 }
236 
CreateJsFillRequestCallback(napi_env env,const sptr<AAFwk::SessionInfo> & sessionInfo,const sptr<Rosen::Window> & uiWindow)237 napi_value JsFillRequestCallback::CreateJsFillRequestCallback(napi_env env,
238     const sptr<AAFwk::SessionInfo> &sessionInfo, const sptr<Rosen::Window> &uiWindow)
239 {
240     TAG_LOGD(AAFwkTag::AUTOFILL_EXT, "called");
241     napi_value object = nullptr;
242     napi_create_object(env, &object);
243     if (object == nullptr) {
244         TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "null object");
245         return CreateJsUndefined(env);
246     }
247 
248     std::unique_ptr<JsFillRequestCallback> jsSession =
249         std::make_unique<JsFillRequestCallback>(sessionInfo, uiWindow);
250     napi_wrap(env, object, jsSession.release(), Finalizer, nullptr, nullptr);
251 
252     const char *moduleName = "JsFillRequestCallback";
253     BindNativeFunction(env, object, "onSuccess", moduleName, FillRequestSuccess);
254     BindNativeFunction(env, object, "onFailure", moduleName, FillRequestFailed);
255     BindNativeFunction(env, object, "onCancel", moduleName, FillRequestCanceled);
256     BindNativeFunction(env, object, "setAutoFillPopupConfig", moduleName, FillRequestAutoFillPopupConfig);
257     return object;
258 }
259 } // namespace AbilityRuntime
260 } // namespace OHOS