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_insight_intent_executor.h"
17
18 #include <want_params.h>
19
20 #include "ability_transaction_callback_info.h"
21 #include "hilog_tag_wrapper.h"
22 #include "insight_intent_constant.h"
23 #include "insight_intent_execute_result.h"
24 #include "js_insight_intent_context.h"
25 #include "js_runtime.h"
26 #include "js_runtime_utils.h"
27 #include "napi_common_execute_result.h"
28 #include "napi_common_util.h"
29 #include "napi_common_want.h"
30 #include "native_reference.h"
31
32 #undef STATE_PATTERN_NAIVE_H
33 #define STATE_PATTERN_NAIVE_STATE state_
34 #include "state_pattern_naive.h"
35
36 #define TMP_NAPI_ANONYMOUS_FUNC "_"
37
38 namespace OHOS::AbilityRuntime {
39
Create(JsRuntime & runtime)40 std::shared_ptr<JsInsightIntentExecutor> JsInsightIntentExecutor::Create(JsRuntime& runtime)
41 {
42 std::shared_ptr<JsInsightIntentExecutor> ptr(new (std::nothrow) JsInsightIntentExecutor(runtime));
43 return ptr;
44 }
45
46 using State = JsInsightIntentExecutor::State;
47
JsInsightIntentExecutor(JsRuntime & runtime)48 JsInsightIntentExecutor::JsInsightIntentExecutor(JsRuntime& runtime) : runtime_(runtime)
49 { }
50
~JsInsightIntentExecutor()51 JsInsightIntentExecutor::~JsInsightIntentExecutor()
52 {
53 state_ = State::DESTROYED;
54 TAG_LOGI(AAFwkTag::INTENT, "called");
55 }
56
Init(const InsightIntentExecutorInfo & insightIntentInfo)57 bool JsInsightIntentExecutor::Init(const InsightIntentExecutorInfo& insightIntentInfo)
58 {
59 TAG_LOGD(AAFwkTag::INTENT, "called");
60 STATE_PATTERN_NAIVE_ACCEPT(State::CREATED, false);
61 state_ = State::INITIALIZED;
62 InsightIntentExecutor::Init(insightIntentInfo);
63
64 HandleScope handleScope(runtime_);
65 jsObj_ = JsInsightIntentExecutor::LoadJsCode(insightIntentInfo, runtime_);
66 if (jsObj_ == nullptr) {
67 TAG_LOGE(AAFwkTag::INTENT, "null jsObj_");
68 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
69 }
70
71 auto env = runtime_.GetNapiEnv();
72 if (env == nullptr) {
73 TAG_LOGE(AAFwkTag::INTENT, "null env");
74 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
75 }
76
77 auto context = GetContext();
78 if (context == nullptr) {
79 TAG_LOGE(AAFwkTag::INTENT, "null Context");
80 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
81 }
82
83 napi_value contextObj = CreateJsInsightIntentContext(env, context);
84 contextObj_ = JsRuntime::LoadSystemModuleByEngine(env, "app.ability.InsightIntentContext", &contextObj, 1);
85 if (contextObj_ == nullptr) {
86 TAG_LOGE(AAFwkTag::INTENT, "null contextObj_");
87 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
88 }
89
90 auto executorNapiVal = jsObj_->GetNapiValue();
91 auto contextNapiVal = contextObj_->GetNapiValue();
92 if (!CheckTypeForNapiValue(env, executorNapiVal, napi_object) ||
93 !CheckTypeForNapiValue(env, contextNapiVal, napi_object) ||
94 napi_set_named_property(env, executorNapiVal, "context", contextNapiVal) != napi_ok) {
95 TAG_LOGE(AAFwkTag::INTENT, "Set context property failed");
96 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
97 }
98
99 return true;
100 }
101
ExecuteIntentCheckError()102 bool JsInsightIntentExecutor::ExecuteIntentCheckError()
103 {
104 ReplyFailedInner();
105 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
106 }
107
HandleExecuteIntent(InsightIntentExecuteMode mode,const std::string & name,const AAFwk::WantParams & param,const std::shared_ptr<NativeReference> & pageLoader,std::unique_ptr<InsightIntentExecutorAsyncCallback> callback,bool & isAsync)108 bool JsInsightIntentExecutor::HandleExecuteIntent(
109 InsightIntentExecuteMode mode,
110 const std::string& name,
111 const AAFwk::WantParams& param,
112 const std::shared_ptr<NativeReference>& pageLoader,
113 std::unique_ptr<InsightIntentExecutorAsyncCallback> callback,
114 bool& isAsync)
115 {
116 TAG_LOGD(AAFwkTag::INTENT, "called");
117 STATE_PATTERN_NAIVE_ACCEPT(State::INITIALIZED, false);
118 state_ = State::EXECUTING;
119
120 if (callback == nullptr || callback->IsEmpty()) {
121 TAG_LOGE(AAFwkTag::INTENT, "callback is nullptr");
122 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
123 }
124 callback_ = std::move(callback);
125 bool successful = false;
126 switch (mode) {
127 case InsightIntentExecuteMode::UIABILITY_FOREGROUND:
128 if (!JsInsightIntentExecutor::CheckParametersUIAbilityForeground(pageLoader)) {
129 TAG_LOGE(AAFwkTag::INTENT, "CheckParametersUIAbilityForeground error");
130 return ExecuteIntentCheckError();
131 }
132 successful = ExecuteInsightIntentUIAbilityForeground(name, param, pageLoader);
133 break;
134 case InsightIntentExecuteMode::UIABILITY_BACKGROUND:
135 if (!JsInsightIntentExecutor::CheckParametersUIAbilityBackground()) {
136 TAG_LOGE(AAFwkTag::INTENT, "CheckParametersUIAbilityBackground error");
137 return ExecuteIntentCheckError();
138 }
139 successful = ExecuteInsightIntentUIAbilityBackground(name, param);
140 break;
141 case InsightIntentExecuteMode::UIEXTENSION_ABILITY:
142 if (!JsInsightIntentExecutor::CheckParametersUIExtension(pageLoader)) {
143 TAG_LOGE(AAFwkTag::INTENT, "CheckParametersUIExtension error");
144 return ExecuteIntentCheckError();
145 }
146 successful = ExecuteInsightIntentUIExtension(name, param, pageLoader);
147 break;
148 case InsightIntentExecuteMode::SERVICE_EXTENSION_ABILITY:
149 if (!JsInsightIntentExecutor::CheckParametersServiceExtension()) {
150 TAG_LOGE(AAFwkTag::INTENT, "CheckParametersServiceExtension error");
151 return ExecuteIntentCheckError();
152 }
153 successful = ExecuteInsightIntentServiceExtension(name, param);
154 break;
155 default:
156 TAG_LOGE(AAFwkTag::INTENT, "InsightIntentExecuteMode not supported yet");
157 return ExecuteIntentCheckError();
158 }
159 isAsync = isAsync_;
160 return successful;
161 }
162
LoadJsCode(const InsightIntentExecutorInfo & info,JsRuntime & runtime)163 std::unique_ptr<NativeReference> JsInsightIntentExecutor::LoadJsCode(
164 const InsightIntentExecutorInfo& info,
165 JsRuntime& runtime)
166 {
167 TAG_LOGD(AAFwkTag::INTENT, "called");
168 auto executeParam = info.executeParam;
169 if (executeParam == nullptr) {
170 TAG_LOGE(AAFwkTag::INTENT, "null executeParam");
171 return std::unique_ptr<NativeReference>();
172 }
173
174 std::string moduleName(executeParam->moduleName_);
175 std::string srcPath(executeParam->moduleName_ + "/" + info.srcEntry);
176 auto pos = srcPath.rfind('.');
177 if (pos == std::string::npos) {
178 return nullptr;
179 }
180 srcPath.erase(pos);
181 srcPath.append(".abc");
182
183 std::unique_ptr<NativeReference> jsCode(
184 runtime.LoadModule(moduleName, srcPath, info.hapPath, info.esmodule));
185 return jsCode;
186 }
187
CallJsFunctionWithResult(napi_env env,napi_value obj,const char * funcName,size_t argc,const napi_value * argv,napi_value & result)188 bool JsInsightIntentExecutor::CallJsFunctionWithResult(
189 napi_env env,
190 napi_value obj,
191 const char* funcName,
192 size_t argc,
193 const napi_value* argv,
194 napi_value& result)
195 {
196 TAG_LOGD(AAFwkTag::INTENT, "called");
197 napi_value method = AppExecFwk::GetPropertyValueByPropertyName(
198 env,
199 obj,
200 funcName,
201 napi_valuetype::napi_function);
202 if (method == nullptr) {
203 TAG_LOGE(AAFwkTag::INTENT, "CallJsFunctionWithResult error");
204 return false;
205 }
206 napi_call_function(
207 env,
208 obj,
209 method,
210 argc,
211 argv,
212 &result);
213 return true;
214 }
215
CallJsFunctionWithResultInner(const char * funcName,size_t argc,const napi_value * argv,napi_value & result)216 bool JsInsightIntentExecutor::CallJsFunctionWithResultInner(
217 const char* funcName,
218 size_t argc,
219 const napi_value* argv,
220 napi_value& result)
221 {
222 TAG_LOGD(AAFwkTag::INTENT, "called");
223 auto* env = runtime_.GetNapiEnv();
224 napi_value obj = jsObj_->GetNapiValue();
225 if (!CheckTypeForNapiValue(env, obj, napi_valuetype::napi_object)) {
226 TAG_LOGE(AAFwkTag::INTENT, "CallJsFunctionWithResultInner Type error");
227 return false;
228 }
229 return JsInsightIntentExecutor::CallJsFunctionWithResult(
230 env,
231 obj,
232 funcName,
233 argc,
234 argv,
235 result);
236 }
237
GetResultFromJs(napi_env env,napi_value resultJs)238 std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> JsInsightIntentExecutor::GetResultFromJs(
239 napi_env env, napi_value resultJs)
240 {
241 TAG_LOGD(AAFwkTag::INTENT, "called");
242 auto resultCpp = std::make_shared<AppExecFwk::InsightIntentExecuteResult>();
243 if (!UnwrapExecuteResult(env, resultJs, *resultCpp)) {
244 return nullptr;
245 }
246 return resultCpp;
247 }
248
ResolveCbCpp(napi_env env,napi_callback_info info)249 napi_value JsInsightIntentExecutor::ResolveCbCpp(napi_env env, napi_callback_info info)
250 {
251 TAG_LOGD(AAFwkTag::INTENT, "called");
252 constexpr size_t argc = 1;
253 napi_value argv[argc] = {nullptr};
254 size_t actualArgc = argc;
255 void* data = nullptr;
256 napi_get_cb_info(env, info, &actualArgc, argv, nullptr, &data);
257 auto* callback = static_cast<InsightIntentExecutorAsyncCallback*>(data);
258 napi_value resultJs = argv[0];
259 if (resultJs == nullptr) {
260 JsInsightIntentExecutor::ReplyFailed(callback);
261 return nullptr;
262 }
263 std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> resultCpp =
264 JsInsightIntentExecutor::GetResultFromJs(env, resultJs);
265 JsInsightIntentExecutor::ReplySucceeded(callback, resultCpp);
266 return nullptr;
267 }
268
RejectCbCpp(napi_env env,napi_callback_info info)269 napi_value JsInsightIntentExecutor::RejectCbCpp(napi_env env, napi_callback_info info)
270 {
271 void* data = nullptr;
272 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data);
273 auto* callback = static_cast<InsightIntentExecutorAsyncCallback*>(data);
274 JsInsightIntentExecutor::ReplyFailed(callback);
275 return nullptr;
276 }
277
ReplyFailed(InsightIntentExecutorAsyncCallback * callback,InsightIntentInnerErr innerErr)278 void JsInsightIntentExecutor::ReplyFailed(InsightIntentExecutorAsyncCallback* callback,
279 InsightIntentInnerErr innerErr)
280 {
281 TAG_LOGD(AAFwkTag::INTENT, "called");
282 if (callback == nullptr) {
283 return;
284 }
285 AppExecFwk::InsightIntentExecuteResult errorResult{};
286 errorResult.innerErr = innerErr;
287 callback->Call(errorResult);
288 delete callback;
289 }
290
ReplySucceeded(InsightIntentExecutorAsyncCallback * callback,std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> resultCpp)291 void JsInsightIntentExecutor::ReplySucceeded(InsightIntentExecutorAsyncCallback* callback,
292 std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> resultCpp)
293 {
294 TAG_LOGD(AAFwkTag::INTENT, "called");
295 if (callback == nullptr) {
296 return;
297 }
298 if (resultCpp == nullptr) {
299 ReplyFailed(callback);
300 return;
301 }
302 resultCpp->innerErr = InsightIntentInnerErr::INSIGHT_INTENT_ERR_OK;
303 callback->Call(*resultCpp);
304 delete callback;
305 }
306
ReplyFailedInner(InsightIntentInnerErr innerErr)307 void JsInsightIntentExecutor::ReplyFailedInner(InsightIntentInnerErr innerErr)
308 {
309 TAG_LOGD(AAFwkTag::INTENT, "called");
310 state_ = JsInsightIntentExecutor::State::INVALID;
311 auto* callback = callback_.release();
312 JsInsightIntentExecutor::ReplyFailed(callback, innerErr);
313 }
314
ReplySucceededInner(std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> resultCpp)315 void JsInsightIntentExecutor::ReplySucceededInner(std::shared_ptr<AppExecFwk::InsightIntentExecuteResult> resultCpp)
316 {
317 TAG_LOGD(AAFwkTag::INTENT, "called");
318 state_ = JsInsightIntentExecutor::State::EXECUTATION_DONE;
319 auto* callback = callback_.release();
320 JsInsightIntentExecutor::ReplySucceeded(callback, resultCpp);
321 }
322
HandleResultReturnedFromJsFunc(napi_value resultJs)323 bool JsInsightIntentExecutor::HandleResultReturnedFromJsFunc(napi_value resultJs)
324 {
325 TAG_LOGD(AAFwkTag::INTENT, "called");
326 auto* env = runtime_.GetNapiEnv();
327 if (resultJs == nullptr) {
328 ReplyFailedInner();
329 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
330 }
331 bool isPromise = false;
332 napi_is_promise(env, resultJs, &isPromise);
333
334 isAsync_ = isPromise;
335
336 if (isPromise) {
337 TAG_LOGI(AAFwkTag::INTENT, "Is promise");
338 auto* callback = callback_.release();
339
340 napi_value then = nullptr;
341 napi_get_named_property(env, resultJs, "then", &then);
342 napi_value resolveCbJs = nullptr;
343 napi_create_function(env, TMP_NAPI_ANONYMOUS_FUNC, strlen(TMP_NAPI_ANONYMOUS_FUNC),
344 ResolveCbCpp, callback, &resolveCbJs);
345 constexpr size_t argcThen = 1;
346 napi_value argvThen[argcThen] = { resolveCbJs };
347 napi_call_function(env, resultJs, then, argcThen, argvThen, nullptr);
348
349 napi_value promiseCatch = nullptr;
350 napi_get_named_property(env, resultJs, "catch", &promiseCatch);
351 napi_value rejectCbJs = nullptr;
352 napi_create_function(env, TMP_NAPI_ANONYMOUS_FUNC, strlen(TMP_NAPI_ANONYMOUS_FUNC),
353 RejectCbCpp, callback, &rejectCbJs);
354 constexpr size_t argcCatch = 1;
355 napi_value argvCatch[argcCatch] = { rejectCbJs };
356 napi_call_function(env, resultJs, promiseCatch, argcCatch, argvCatch, nullptr);
357 } else {
358 TAG_LOGI(AAFwkTag::INTENT, "Not promise");
359 auto resultCpp = JsInsightIntentExecutor::GetResultFromJs(env, resultJs);
360 if (resultCpp == nullptr) {
361 TAG_LOGE(AAFwkTag::INTENT, "Result invalid");
362 ReplyFailedInner();
363 STATE_PATTERN_NAIVE_STATE_SET_AND_RETURN(State::INVALID, false);
364 }
365 TAG_LOGD(AAFwkTag::INTENT, "Call succeed");
366 ReplySucceededInner(resultCpp);
367 }
368 return true;
369 }
370
CheckParametersUIAbilityForeground(const std::shared_ptr<NativeReference> & windowStage)371 bool JsInsightIntentExecutor::CheckParametersUIAbilityForeground(const std::shared_ptr<NativeReference>& windowStage)
372 {
373 return windowStage != nullptr;
374 }
375
ExecuteInsightIntentUIAbilityForeground(const std::string & name,const AAFwk::WantParams & param,const std::shared_ptr<NativeReference> & windowStageJs)376 bool JsInsightIntentExecutor::ExecuteInsightIntentUIAbilityForeground(
377 const std::string& name,
378 const AAFwk::WantParams& param,
379 const std::shared_ptr<NativeReference>& windowStageJs)
380 {
381 TAG_LOGD(AAFwkTag::INTENT, "called");
382 HandleScope handleScope(runtime_);
383
384 constexpr auto intentMode = static_cast<size_t>(InsightIntentExecuteMode::UIABILITY_FOREGROUND);
385 constexpr auto funcName = JsInsightIntentExecutor::JS_FUNC_NAME_FOR_MODE[intentMode];
386 constexpr auto argc = JsInsightIntentExecutor::JS_ARGC_FOR_MODE[intentMode];
387
388 auto* env = runtime_.GetNapiEnv();
389 napi_value nameJs = AppExecFwk::WrapStringToJS(env, name);
390 napi_value paramJs = AppExecFwk::WrapWantParams(env, param);
391 napi_value argv[argc] = { nameJs, paramJs, windowStageJs->GetNapiValue() };
392 napi_value result = nullptr;
393
394 if (!CallJsFunctionWithResultInner(funcName, argc, argv, result)) {
395 ReplyFailedInner();
396 return false;
397 }
398
399 return HandleResultReturnedFromJsFunc(result);
400 }
401
CheckParametersUIAbilityBackground()402 bool JsInsightIntentExecutor::CheckParametersUIAbilityBackground()
403 {
404 return true;
405 }
406
ExecuteInsightIntentUIAbilityBackground(const std::string & name,const AAFwk::WantParams & param)407 bool JsInsightIntentExecutor::ExecuteInsightIntentUIAbilityBackground(
408 const std::string& name,
409 const AAFwk::WantParams& param)
410 {
411 TAG_LOGD(AAFwkTag::INTENT, "called");
412 HandleScope handleScope(runtime_);
413
414 constexpr auto intentMode = static_cast<size_t>(InsightIntentExecuteMode::UIABILITY_BACKGROUND);
415 constexpr auto funcName = JsInsightIntentExecutor::JS_FUNC_NAME_FOR_MODE[intentMode];
416 constexpr auto argc = JsInsightIntentExecutor::JS_ARGC_FOR_MODE[intentMode];
417
418 auto* env = runtime_.GetNapiEnv();
419 napi_value nameJs = AppExecFwk::WrapStringToJS(env, name);
420 napi_value paramJs = AppExecFwk::WrapWantParams(env, param);
421 napi_value argv[argc] = { nameJs, paramJs };
422 napi_value result = nullptr;
423
424 if (!CallJsFunctionWithResultInner(funcName, argc, argv, result)) {
425 ReplyFailedInner();
426 return false;
427 }
428
429 return HandleResultReturnedFromJsFunc(result);
430 }
431
CheckParametersUIExtension(const std::shared_ptr<NativeReference> & UIExtensionContentSession)432 bool JsInsightIntentExecutor::CheckParametersUIExtension(
433 const std::shared_ptr<NativeReference>& UIExtensionContentSession)
434 {
435 return UIExtensionContentSession != nullptr;
436 }
437
ExecuteInsightIntentUIExtension(const std::string & name,const AAFwk::WantParams & param,const std::shared_ptr<NativeReference> & UIExtensionContentSession)438 bool JsInsightIntentExecutor::ExecuteInsightIntentUIExtension(
439 const std::string& name,
440 const AAFwk::WantParams& param,
441 const std::shared_ptr<NativeReference>& UIExtensionContentSession)
442 {
443 TAG_LOGD(AAFwkTag::INTENT, "called");
444 HandleScope handleScope(runtime_);
445
446 constexpr auto intentMode = static_cast<size_t>(InsightIntentExecuteMode::UIEXTENSION_ABILITY);
447 constexpr auto funcName = JsInsightIntentExecutor::JS_FUNC_NAME_FOR_MODE[intentMode];
448 constexpr auto argc = JsInsightIntentExecutor::JS_ARGC_FOR_MODE[intentMode];
449
450 auto* env = runtime_.GetNapiEnv();
451 napi_value nameJs = AppExecFwk::WrapStringToJS(env, name);
452 napi_value paramJs = AppExecFwk::WrapWantParams(env, param);
453 napi_value argv[argc] = { nameJs, paramJs, UIExtensionContentSession->GetNapiValue() };
454 napi_value result = nullptr;
455
456 if (!CallJsFunctionWithResultInner(funcName, argc, argv, result)) {
457 ReplyFailedInner();
458 return false;
459 }
460
461 return HandleResultReturnedFromJsFunc(result);
462 }
463
CheckParametersServiceExtension()464 bool JsInsightIntentExecutor::CheckParametersServiceExtension()
465 {
466 return true;
467 }
468
ExecuteInsightIntentServiceExtension(const std::string & name,const AAFwk::WantParams & param)469 bool JsInsightIntentExecutor::ExecuteInsightIntentServiceExtension(
470 const std::string& name,
471 const AAFwk::WantParams& param)
472 {
473 TAG_LOGD(AAFwkTag::INTENT, "called");
474 HandleScope handleScope(runtime_);
475
476 constexpr auto intentMode = static_cast<size_t>(InsightIntentExecuteMode::SERVICE_EXTENSION_ABILITY);
477 constexpr auto funcName = JsInsightIntentExecutor::JS_FUNC_NAME_FOR_MODE[intentMode];
478 constexpr auto argc = JsInsightIntentExecutor::JS_ARGC_FOR_MODE[intentMode];
479
480 auto* env = runtime_.GetNapiEnv();
481 napi_value nameJs = AppExecFwk::WrapStringToJS(env, name);
482 napi_value paramJs = AppExecFwk::WrapWantParams(env, param);
483 napi_value argv[argc] = { nameJs, paramJs };
484 napi_value result = nullptr;
485
486 if (!CallJsFunctionWithResultInner(funcName, argc, argv, result)) {
487 ReplyFailedInner();
488 return false;
489 }
490
491 return HandleResultReturnedFromJsFunc(result);
492 }
493 } // namespace OHOS::AbilityRuntime
494