1 /*
2  * Copyright (c) 2021-2022 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_plugin_callback.h"
16 
17 #include <memory>
18 #include <mutex>
19 
20 #include "js_plugin_callback_mgr.h"
21 #include "js_plugin_util.h"
22 #include "js_plugin_want.h"
23 
24 #include "base/log/log_wrapper.h"
25 #include "core/common/ace_engine.h"
26 #include "core/components/plugin/plugin_component_manager.h"
27 
28 namespace OHOS::Ace::Napi {
29 std::atomic_size_t JSPluginCallback::uuid_ = 0;
30 constexpr int ACE_ARGS_TWO = 2;
31 constexpr int ACE_ARGS_THREE = 3;
32 constexpr int ACE_ARGS_FOUR = 4;
33 constexpr int ACE_PARAM0 = 0;
34 constexpr int ACE_PARAM1 = 1;
35 constexpr int ACE_PARAM2 = 2;
36 constexpr int ACE_PARAM3 = 3;
37 
operator ==(const AceJSPluginRequestParam & param) const38 bool AceJSPluginRequestParam::operator==(const AceJSPluginRequestParam& param) const
39 {
40     AppExecFwk::ElementName leftElement = want_.GetElement();
41     AppExecFwk::ElementName rightElement = param.want_.GetElement();
42     if (leftElement == rightElement) {
43         if (name_ == param.name_ && data_ == param.data_ && jsonPath_ == param.jsonPath_) {
44             return true;
45         }
46     }
47     return false;
48 }
49 
operator !=(const AceJSPluginRequestParam & param) const50 bool AceJSPluginRequestParam::operator!=(const AceJSPluginRequestParam& param) const
51 {
52     return !operator==(param);
53 }
54 
JSPluginCallback(CallBackType eventType,ACECallbackInfo & cbInfo,ACEAsyncJSCallbackInfo * jsCallbackInfo)55 JSPluginCallback::JSPluginCallback(
56     CallBackType eventType, ACECallbackInfo& cbInfo, ACEAsyncJSCallbackInfo* jsCallbackInfo)
57     : eventType_(eventType), asyncJSCallbackInfo_(jsCallbackInfo)
58 {
59     uuid_++;
60     cbInfo_.env = cbInfo.env;
61     cbInfo_.callback = cbInfo.callback;
62     cbInfo_.containerId = cbInfo.containerId;
63 }
64 
~JSPluginCallback()65 JSPluginCallback::~JSPluginCallback()
66 {
67     if (uuid_ > 0) {
68         uuid_--;
69     }
70     DestroyAllResource();
71 }
72 
DestroyAllResource(void)73 void JSPluginCallback::DestroyAllResource(void)
74 {
75     if (cbInfo_.env != nullptr && cbInfo_.callback != nullptr) {
76         napi_delete_reference(cbInfo_.env, cbInfo_.callback);
77     }
78     cbInfo_.env = nullptr;
79     cbInfo_.callback = nullptr;
80     cbInfo_.containerId = -1;
81     asyncJSCallbackInfo_ = nullptr;
82 }
83 
SetWant(const AAFwk::Want & want)84 void JSPluginCallback::SetWant(const AAFwk::Want& want)
85 {
86     want_ = want;
87 }
88 
GetWant()89 AAFwk::Want& JSPluginCallback::GetWant()
90 {
91     return want_;
92 }
93 
SetRequestParam(const std::shared_ptr<AceJSPluginRequestParam> & param)94 void JSPluginCallback::SetRequestParam(const std::shared_ptr<AceJSPluginRequestParam>& param)
95 {
96     requestParam_ = param;
97 }
98 
GetID(void)99 size_t JSPluginCallback::GetID(void)
100 {
101     return uuid_;
102 }
103 
GetContainerId()104 int32_t JSPluginCallback::GetContainerId()
105 {
106     return cbInfo_.containerId;
107 }
108 
SendRequestEventResult(napi_value jsObject)109 void JSPluginCallback::SendRequestEventResult(napi_value jsObject)
110 {
111     napi_value jsTemplate = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "template", napi_string);
112     napi_value jsData = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "data", napi_object);
113     napi_value jsExtraData = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "extraData", napi_object);
114 
115     struct MyData {
116         AAFwk::Want want;
117         std::string strTemplate;
118         std::string strDate;
119         std::string strExtraData;
120     };
121 
122     std::shared_ptr<MyData> data = std::make_shared<MyData>();
123     data->want = want_;
124 
125     if (jsTemplate != nullptr) {
126         data->strTemplate = AceUnwrapStringFromJS(cbInfo_.env, jsTemplate);
127     }
128 
129     if (jsData != nullptr) {
130         AceKVObjectToString(cbInfo_.env, jsData, data->strDate);
131     }
132 
133     if (jsExtraData != nullptr) {
134         AceKVObjectToString(cbInfo_.env, jsExtraData, data->strExtraData);
135     }
136 
137     auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
138     if (!container) {
139         return;
140     }
141 
142     auto taskExecutor = container->GetTaskExecutor();
143     if (!taskExecutor) {
144         return;
145     }
146     taskExecutor->PostTask(
147         [data]() {
148             PluginComponentManager::GetInstance()->ReturnRequest(
149                 data->want, data->strTemplate, data->strDate, data->strExtraData);
150         },
151         TaskExecutor::TaskType::BACKGROUND, "ArkUIPluginReturnRequest");
152 }
153 
MakeCallbackParamForRequest(const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)154 napi_value JSPluginCallback::MakeCallbackParamForRequest(
155     const PluginComponentTemplate& pluginTemplate, const std::string& data, const std::string& extraData)
156 {
157     napi_value jsObject = AceCreateJSObject(cbInfo_.env);
158     if (jsObject == nullptr) {
159         return nullptr;
160     }
161 
162     std::string dataTmp("{}");
163     std::string extraDataTmp("{}");
164     if (!data.empty()) {
165         dataTmp = data;
166     }
167     if (!extraData.empty()) {
168         extraDataTmp = extraData;
169     }
170 
171     napi_value jsPluginTemplate = MakePluginTemplateObject(pluginTemplate);
172     napi_value jsData = AceStringToKVObject(cbInfo_.env, dataTmp);
173     napi_value jsExtraData = AceStringToKVObject(cbInfo_.env, extraDataTmp);
174 
175     if (jsData != nullptr) {
176         AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "componentTemplate", jsPluginTemplate);
177     }
178     if (jsData != nullptr) {
179         AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "data", jsData);
180     }
181     if (jsExtraData != nullptr) {
182         AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "extraData", jsExtraData);
183     }
184     return jsObject;
185 }
186 
MakePluginTemplateObject(const PluginComponentTemplate & pluginTemplate)187 napi_value JSPluginCallback::MakePluginTemplateObject(const PluginComponentTemplate& pluginTemplate)
188 {
189     napi_value jsPluginTemplate = AceCreateJSObject(cbInfo_.env);
190     if (jsPluginTemplate != nullptr) {
191         napi_value jsSource = AceWrapStringToJS(cbInfo_.env, pluginTemplate.GetSource());
192         napi_value jsAbility = AceWrapStringToJS(cbInfo_.env, pluginTemplate.GetAbility());
193 
194         AceSetPropertyValueByPropertyName(cbInfo_.env, jsPluginTemplate, "source", jsSource);
195         AceSetPropertyValueByPropertyName(cbInfo_.env, jsPluginTemplate, "ability", jsAbility);
196     }
197     return jsPluginTemplate;
198 }
199 
OnPushEventInner(const OnPluginUvWorkData * workData)200 void JSPluginCallback::OnPushEventInner(const OnPluginUvWorkData* workData)
201 {
202     napi_value jsCallback = nullptr;
203     napi_value undefined = nullptr;
204     napi_value jsResult = nullptr;
205     napi_value callbackParam[ACE_ARGS_FOUR] = { nullptr };
206     napi_handle_scope scope = nullptr;
207     std::string dataTmp("{}");
208     std::string extraDataTmp("{}");
209     if (!workData->data.empty()) {
210         dataTmp = workData->data;
211     }
212     if (!workData->extraData.empty()) {
213         extraDataTmp = workData->extraData;
214     }
215 
216     napi_open_handle_scope(cbInfo_.env, &scope);
217     if (scope == nullptr) {
218         napi_close_handle_scope(cbInfo_.env, scope);
219         return;
220     }
221 
222     PluginComponentTemplate componentTemplate;
223     componentTemplate.SetSource(workData->sourceName);
224     componentTemplate.SetAbility(workData->abilityName);
225 
226     callbackParam[ACE_PARAM0] = AceWrapWant(cbInfo_.env, workData->want);
227     callbackParam[ACE_PARAM1] = MakePluginTemplateObject(componentTemplate);
228     callbackParam[ACE_PARAM2] = AceStringToKVObject(cbInfo_.env, dataTmp);
229     callbackParam[ACE_PARAM3] = AceStringToKVObject(cbInfo_.env, extraDataTmp);
230 
231     napi_get_undefined(cbInfo_.env, &undefined);
232     napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
233     napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_FOUR, callbackParam, &jsResult);
234     napi_close_handle_scope(cbInfo_.env, scope);
235 }
236 
OnPushEvent(const AAFwk::Want & want,const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)237 void JSPluginCallback::OnPushEvent(const AAFwk::Want& want, const PluginComponentTemplate& pluginTemplate,
238     const std::string& data, const std::string& extraData)
239 {
240     if (cbInfo_.env == nullptr || cbInfo_.callback == nullptr) {
241         return;
242     }
243 
244     auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
245     if (!container) {
246         return;
247     }
248 
249     auto taskExecutor = container->GetTaskExecutor();
250     if (!taskExecutor) {
251         return;
252     }
253     std::weak_ptr<PluginComponentCallBack> weak = weak_from_this();
254     taskExecutor->PostTask(
255         [weak, want, sourceName = pluginTemplate.GetSource(), abilityName = pluginTemplate.GetAbility(), data,
256             extraData]() {
257             OnPluginUvWorkData uvWorkData;
258             uvWorkData.want = want;
259             uvWorkData.sourceName = sourceName;
260             uvWorkData.abilityName = abilityName;
261             uvWorkData.data = data;
262             uvWorkData.extraData = extraData;
263             auto callBack = weak.lock();
264             if (callBack) {
265                 auto jsCallback = std::static_pointer_cast<JSPluginCallback>(callBack);
266                 jsCallback->OnPushEventInner(&uvWorkData);
267             }
268         },
269         TaskExecutor::TaskType::UI, "ArkUIPluginPushEventInner");
270 }
271 
OnRequestEventInner(const OnPluginUvWorkData * workData)272 void JSPluginCallback::OnRequestEventInner(const OnPluginUvWorkData* workData)
273 {
274     napi_value jsCallback = nullptr;
275     napi_value undefined = nullptr;
276     napi_value jsResult = nullptr;
277     napi_handle_scope scope = nullptr;
278     std::string dataTmp("{}");
279     if (!workData->data.empty()) {
280         dataTmp = workData->data;
281     }
282 
283     napi_open_handle_scope(cbInfo_.env, &scope);
284     if (scope == nullptr) {
285         napi_close_handle_scope(cbInfo_.env, scope);
286         return;
287     }
288     napi_value callbackParam[ACE_ARGS_THREE] = { nullptr };
289     callbackParam[ACE_PARAM0] = AceWrapWant(cbInfo_.env, workData->want);
290     callbackParam[ACE_PARAM1] = AceWrapStringToJS(cbInfo_.env, workData->name);
291     callbackParam[ACE_PARAM2] = AceStringToKVObject(cbInfo_.env, dataTmp);
292 
293     napi_get_undefined(cbInfo_.env, &undefined);
294     napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
295     napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_THREE, callbackParam, &jsResult);
296 
297     if (AceIsTypeForNapiValue(cbInfo_.env, jsResult, napi_object)) {
298         SendRequestEventResult(jsResult);
299     }
300     napi_close_handle_scope(cbInfo_.env, scope);
301 }
302 
OnRequestEvent(const AAFwk::Want & want,const std::string & name,const std::string & data)303 void JSPluginCallback::OnRequestEvent(const AAFwk::Want& want, const std::string& name, const std::string& data)
304 {
305     if (cbInfo_.env == nullptr || cbInfo_.callback == nullptr) {
306         return;
307     }
308 
309     auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
310     if (!container) {
311         return;
312     }
313 
314     auto taskExecutor = container->GetTaskExecutor();
315     if (!taskExecutor) {
316         return;
317     }
318 
319     std::weak_ptr<PluginComponentCallBack> weak = weak_from_this();
320     taskExecutor->PostTask(
321         [weak, want, name, data]() {
322             auto callBack = weak.lock();
323             if (callBack) {
324                 OnPluginUvWorkData uvWorkData;
325                 uvWorkData.that = callBack.get();
326                 uvWorkData.want = want;
327                 uvWorkData.data = data;
328                 uvWorkData.name = name;
329 
330                 auto jsCallback = std::static_pointer_cast<JSPluginCallback>(callBack);
331                 jsCallback->OnRequestEventInner(&uvWorkData);
332             }
333         },
334         TaskExecutor::TaskType::JS, "ArkUIPluginRequestEventInner");
335 }
336 
OnRequestCallBackInner(const OnPluginUvWorkData * workData)337 void JSPluginCallback::OnRequestCallBackInner(const OnPluginUvWorkData* workData)
338 {
339     napi_value jsCallback = nullptr;
340     napi_value undefined = nullptr;
341     napi_value jsResult = nullptr;
342     napi_handle_scope scope = nullptr;
343     napi_open_handle_scope(cbInfo_.env, &scope);
344     if (scope == nullptr) {
345         napi_close_handle_scope(cbInfo_.env, scope);
346         return;
347     }
348     PluginComponentTemplate componentTemplate;
349     componentTemplate.SetSource(workData->sourceName);
350     componentTemplate.SetAbility(workData->abilityName);
351 
352     if (cbInfo_.callback != nullptr) {
353         napi_value callbackParam[ACE_ARGS_TWO] = { nullptr };
354         callbackParam[ACE_PARAM0] = AceGetCallbackErrorValue(cbInfo_.env, 0);
355         callbackParam[ACE_PARAM1] = MakeCallbackParamForRequest(componentTemplate, workData->data, workData->extraData);
356         napi_get_undefined(cbInfo_.env, &undefined);
357         napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
358         napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_TWO, callbackParam, &jsResult);
359     }
360     napi_close_handle_scope(cbInfo_.env, scope);
361 }
362 
OnRequestCallBack(const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)363 void JSPluginCallback::OnRequestCallBack(
364     const PluginComponentTemplate& pluginTemplate, const std::string& data, const std::string& extraData)
365 {
366     JSPluginCallbackMgr::Instance().UnRegisterEvent(GetID());
367     if (cbInfo_.env == nullptr) {
368         return;
369     }
370 
371     if (cbInfo_.callback != nullptr) {
372         uvWorkData_.that = (void*)this;
373         uvWorkData_.sourceName = pluginTemplate.GetSource();
374         uvWorkData_.abilityName = pluginTemplate.GetAbility();
375         uvWorkData_.data = data;
376         uvWorkData_.extraData = extraData;
377 
378         if (getpid() != gettid()) {
379             struct ResultData {
380                 JSPluginCallback* context = nullptr;
381                 std::mutex mtx;
382                 bool ready = false;
383                 std::condition_variable cv;
384             };
385 
386             std::shared_ptr<ResultData> resultData = std::make_shared<ResultData>();
387             resultData->context = this;
388 
389             auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
390             if (!container) {
391                 return;
392             }
393 
394             auto taskExecutor = container->GetTaskExecutor();
395             if (!taskExecutor) {
396                 return;
397             }
398             taskExecutor->PostTask(
399                 [resultData]() {
400                     resultData->context->OnRequestCallBackInner(&resultData->context->uvWorkData_);
401 
402                     {
403                         std::unique_lock<std::mutex> lock(resultData->mtx);
404                         resultData->ready = true;
405                         resultData->cv.notify_all();
406                     }
407                 },
408                 TaskExecutor::TaskType::JS, "ArkUIPluginRequestCallbackInner");
409 
410             {
411                 std::unique_lock<std::mutex> lock(resultData->mtx);
412                 if (!resultData->ready) {
413                     resultData->cv.wait(lock, [&] { return resultData->ready; });
414                 }
415             }
416         } else {
417             OnRequestCallBackInner(&uvWorkData_);
418         }
419     } else {
420         if (asyncJSCallbackInfo_) {
421             asyncJSCallbackInfo_->requestCallbackData.sourceName = pluginTemplate.GetSource();
422             asyncJSCallbackInfo_->requestCallbackData.abilityName = pluginTemplate.GetAbility();
423             asyncJSCallbackInfo_->requestCallbackData.data = data;
424             asyncJSCallbackInfo_->requestCallbackData.extraData = extraData;
425             asyncJSCallbackInfo_->onRequestCallbackOK = true;
426         }
427     }
428 }
429 
OnEventStrictEquals(CallBackType eventType,const AAFwk::Want & want,ACECallbackInfo & cbInfo)430 bool JSPluginCallback::OnEventStrictEquals(CallBackType eventType, const AAFwk::Want& want, ACECallbackInfo& cbInfo)
431 {
432     if (eventType != eventType_) {
433         return false;
434     }
435     AppExecFwk::ElementName leftElement = want.GetElement();
436     AppExecFwk::ElementName rightElement = want_.GetElement();
437     if (leftElement == rightElement) {
438         return AceIsSameFuncFromJS(cbInfo, cbInfo_);
439     } else {
440         return false;
441     }
442 }
443 
RequestStrictEquals(CallBackType eventType,const AAFwk::Want & want,ACECallbackInfo & cbInfo,const std::shared_ptr<AceJSPluginRequestParam> & param)444 bool JSPluginCallback::RequestStrictEquals(CallBackType eventType, const AAFwk::Want& want, ACECallbackInfo& cbInfo,
445     const std::shared_ptr<AceJSPluginRequestParam>& param)
446 {
447     if (eventType != eventType_) {
448         return false;
449     }
450     AppExecFwk::ElementName leftElement = want.GetElement();
451     AppExecFwk::ElementName rightElement = want_.GetElement();
452     if (!(leftElement == rightElement)) {
453         return false;
454     }
455     if (param == nullptr || requestParam_ == nullptr) {
456         return false;
457     }
458     if (*param != *requestParam_) {
459         return false;
460     }
461     return AceIsSameFuncFromJS(cbInfo, cbInfo_);
462 }
463 } // namespace OHOS::Ace::Napi
464