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