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 #include "js_ui_extension_callback.h"
16 #include "ability_business_error.h"
17 #include "hilog_tag_wrapper.h"
18 #include "js_runtime_utils.h"
19 #include "napi/native_api.h"
20 #include "napi_common_util.h"
21 #ifdef SUPPORT_SCREEN
22 #include "ui_content.h"
23 #include "ws_common.h"
24 #endif // SUPPORT_SCREEN
25 #include "napi_common_want.h"
26
27 namespace OHOS {
28 namespace AbilityRuntime {
29 #ifdef SUPPORT_SCREEN
30 constexpr const char* ERROR_MSG_INNER = "Inner error.";
31 #endif // SUPPORT_SCREEN
~JsUIExtensionCallback()32 JsUIExtensionCallback::~JsUIExtensionCallback()
33 {
34 if (jsCallbackObject_ == nullptr) {
35 return;
36 }
37
38 uv_loop_t *loop = nullptr;
39 napi_get_uv_event_loop(env_, &loop);
40 if (loop == nullptr) {
41 return;
42 }
43
44 uv_work_t *work = new (std::nothrow) uv_work_t;
45 if (work == nullptr) {
46 return;
47 }
48 work->data = reinterpret_cast<void *>(jsCallbackObject_.release());
49 int ret = uv_queue_work(loop, work, [](uv_work_t *work) {},
50 [](uv_work_t *work, int status) {
51 if (work == nullptr) {
52 return;
53 }
54 if (work->data == nullptr) {
55 delete work;
56 work = nullptr;
57 return;
58 }
59 delete reinterpret_cast<NativeReference *>(work->data);
60 work->data = nullptr;
61 delete work;
62 work = nullptr;
63 });
64 if (ret != 0) {
65 delete reinterpret_cast<NativeReference *>(work->data);
66 work->data = nullptr;
67 delete work;
68 work = nullptr;
69 }
70 }
71
SetSessionId(int32_t sessionId)72 void JsUIExtensionCallback::SetSessionId(int32_t sessionId)
73 {
74 sessionId_ = sessionId;
75 }
76 #ifdef SUPPORT_SCREEN
SetUIContent(Ace::UIContent * uiContent)77 void JsUIExtensionCallback::SetUIContent(Ace::UIContent* uiContent)
78 {
79 uiContent_ = uiContent;
80 }
81
SetJsCallbackObject(napi_value jsCallbackObject)82 void JsUIExtensionCallback::SetJsCallbackObject(napi_value jsCallbackObject)
83 {
84 napi_ref ref = nullptr;
85 napi_create_reference(env_, jsCallbackObject, 1, &ref);
86 jsCallbackObject_ = std::unique_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
87 if (jsCallbackObject_ == nullptr) {
88 TAG_LOGE(AAFwkTag::UI_EXT, "null jsCallbackObject_");
89 }
90 }
91
OnError(int32_t number)92 void JsUIExtensionCallback::OnError(int32_t number)
93 {
94 TAG_LOGI(AAFwkTag::UI_EXT, "call");
95 if (env_ == nullptr) {
96 TAG_LOGE(AAFwkTag::UI_EXT, "null env_");
97 return;
98 }
99 // js callback should run in js thread
100 std::shared_ptr<JsUIExtensionCallback> jsUIExtensionCallback = shared_from_this();
101 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
102 ([jsUIExtensionCallback, number](napi_env env, NapiAsyncTask &task, int32_t status) {
103 if (jsUIExtensionCallback != nullptr) {
104 jsUIExtensionCallback->CallJsError(number);
105 }
106 });
107 napi_ref callback = nullptr;
108 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
109 NapiAsyncTask::Schedule("JsUIExtensionCallback::OnError:",
110 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
111 if (uiContent_ == nullptr) {
112 TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent_");
113 return;
114 }
115 uiContent_->CloseModalUIExtension(sessionId_);
116 }
117
OnResult(int32_t resultCode,const AAFwk::Want & want)118 void JsUIExtensionCallback::OnResult(int32_t resultCode, const AAFwk::Want &want)
119 {
120 TAG_LOGI(AAFwkTag::UI_EXT, "call");
121 if (env_ == nullptr) {
122 TAG_LOGE(AAFwkTag::UI_EXT, "null env_");
123 return;
124 }
125 // js callback should run in js thread
126 std::shared_ptr<JsUIExtensionCallback> jsUIExtensionCallback = shared_from_this();
127 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
128 ([jsUIExtensionCallback, resultCode, want](napi_env env, NapiAsyncTask &task, int32_t status) {
129 if (jsUIExtensionCallback != nullptr) {
130 jsUIExtensionCallback->CallJsResult(resultCode, want);
131 }
132 });
133 napi_ref callback = nullptr;
134 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
135 NapiAsyncTask::Schedule("JsUIExtensionCallback::OnResult:",
136 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
137 if (uiContent_ == nullptr) {
138 TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent_");
139 return;
140 }
141 uiContent_->CloseModalUIExtension(sessionId_);
142 }
143
CallJsResult(int32_t resultCode,const AAFwk::Want & want)144 void JsUIExtensionCallback::CallJsResult(int32_t resultCode, const AAFwk::Want &want)
145 {
146 TAG_LOGI(AAFwkTag::UI_EXT, "call");
147 if (env_ == nullptr) {
148 TAG_LOGE(AAFwkTag::UI_EXT, "null env_");
149 return;
150 }
151
152 napi_value abilityResult = OHOS::AppExecFwk::WrapAbilityResult(env_, resultCode, want);
153 if (abilityResult == nullptr) {
154 TAG_LOGE(AAFwkTag::UI_EXT, "null abilityResult");
155 return;
156 }
157
158 if (jsCallbackObject_ == nullptr) {
159 TAG_LOGE(AAFwkTag::UI_EXT, "null jsCallbackObject_ ");
160 return;
161 }
162 napi_value obj = jsCallbackObject_->GetNapiValue();
163 if (obj == nullptr) {
164 TAG_LOGE(AAFwkTag::UI_EXT, "null obj");
165 return;
166 }
167 napi_value method = nullptr;
168 napi_get_named_property(env_, obj, "onResult", &method);
169 if (method == nullptr || AppExecFwk::IsTypeForNapiValue(env_, method, napi_undefined)
170 || AppExecFwk::IsTypeForNapiValue(env_, method, napi_null)) {
171 TAG_LOGE(AAFwkTag::UI_EXT, "null method");
172 return;
173 }
174
175 napi_value argv[] = { abilityResult };
176 napi_call_function(env_, obj, method, ArraySize(argv), argv, nullptr);
177 TAG_LOGI(AAFwkTag::UI_EXT, "end");
178 }
179
OnRelease(int32_t code)180 void JsUIExtensionCallback::OnRelease(int32_t code)
181 {
182 TAG_LOGI(AAFwkTag::UI_EXT, "call, code:%{public}d", code);
183 if (uiContent_ == nullptr) {
184 TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent_");
185 return;
186 }
187 uiContent_->CloseModalUIExtension(sessionId_);
188 }
189 #endif // SUPPORT_SCREEN
CallJsError(int32_t number)190 void JsUIExtensionCallback::CallJsError(int32_t number)
191 {
192 TAG_LOGI(AAFwkTag::UI_EXT, "call");
193 if (env_ == nullptr) {
194 TAG_LOGE(AAFwkTag::UI_EXT, "null env_ ");
195 return;
196 }
197 std::string name;
198 std::string message;
199 #ifdef SUPPORT_SCREEN
200 if (number != static_cast<int32_t>(Rosen::WSError::WS_OK)) {
201 number = static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INNER);
202 name = ERROR_MSG_INNER;
203 message = "StartAbilityByType failed.";
204 }
205 #endif // SUPPORT_SCREEN
206 napi_value nativeNumber = CreateJsValue(env_, number);
207 napi_value nativeName = CreateJsValue(env_, name);
208 napi_value nativeMessage = CreateJsValue(env_, message);
209 if (jsCallbackObject_ == nullptr) {
210 TAG_LOGE(AAFwkTag::UI_EXT, "null jsCallbackObject_");
211 return;
212 }
213 napi_value obj = jsCallbackObject_->GetNapiValue();
214 if (obj == nullptr) {
215 TAG_LOGE(AAFwkTag::UI_EXT, "null obj");
216 return;
217 }
218 napi_value method = nullptr;
219 napi_get_named_property(env_, obj, "onError", &method);
220 if (method == nullptr || AppExecFwk::IsTypeForNapiValue(env_, method, napi_undefined)
221 || AppExecFwk::IsTypeForNapiValue(env_, method, napi_null)) {
222 TAG_LOGE(AAFwkTag::UI_EXT, "null method");
223 return;
224 }
225
226 napi_value argv[] = { nativeNumber, nativeName, nativeMessage };
227 napi_call_function(env_, obj, method, ArraySize(argv), argv, nullptr);
228 TAG_LOGI(AAFwkTag::UI_EXT, "end");
229 }
230 } // namespace AbilityRuntime
231 } // namespace OHOS