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 "hilog_wrapper.h"
21 #include "js_plugin_callback_mgr.h"
22 #include "js_plugin_util.h"
23 #include "js_plugin_want.h"
24
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
SendRequestEventResult(napi_value jsObject)104 void JSPluginCallback::SendRequestEventResult(napi_value jsObject)
105 {
106 napi_value jsTemplate = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "template", napi_string);
107 napi_value jsData = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "data", napi_object);
108 napi_value jsExtraData = AceGetPropertyValueByPropertyName(cbInfo_.env, jsObject, "extraData", napi_object);
109
110 struct MyData {
111 AAFwk::Want want;
112 std::string strTemplate;
113 std::string strDate;
114 std::string strExtraData;
115 };
116
117 std::shared_ptr<MyData> data = std::make_shared<MyData>();
118 data->want = want_;
119
120 if (jsTemplate != nullptr) {
121 data->strTemplate = AceUnwrapStringFromJS(cbInfo_.env, jsTemplate);
122 }
123
124 if (jsData != nullptr) {
125 AceKVObjectToString(cbInfo_.env, jsData, data->strDate);
126 }
127
128 if (jsExtraData != nullptr) {
129 AceKVObjectToString(cbInfo_.env, jsExtraData, data->strExtraData);
130 }
131
132 auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
133 if (!container) {
134 HILOG_INFO("%{public}s called, container is null. %{public}d", __func__, cbInfo_.containerId);
135 return;
136 }
137
138 auto taskExecutor = container->GetTaskExecutor();
139 if (!taskExecutor) {
140 HILOG_INFO("%{public}s called, taskExecutor is null.", __func__);
141 return;
142 }
143 taskExecutor->PostTask(
144 [data]() {
145 PluginComponentManager::GetInstance()->ReturnRequest(
146 data->want, data->strTemplate, data->strDate, data->strExtraData);
147 },
148 TaskExecutor::TaskType::BACKGROUND);
149 }
150
MakeCallbackParamForRequest(const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)151 napi_value JSPluginCallback::MakeCallbackParamForRequest(
152 const PluginComponentTemplate& pluginTemplate, const std::string& data, const std::string& extraData)
153 {
154 napi_value jsObject = AceCreateJSObject(cbInfo_.env);
155 if (jsObject == nullptr) {
156 return nullptr;
157 }
158
159 std::string dataTmp("{}");
160 std::string extraDataTmp("{}");
161 if (!data.empty()) {
162 dataTmp = data;
163 }
164 if (!extraData.empty()) {
165 extraDataTmp = extraData;
166 }
167
168 napi_value jsPluginTemplate = MakePluginTemplateObject(pluginTemplate);
169 napi_value jsData = AceStringToKVObject(cbInfo_.env, dataTmp);
170 napi_value jsExtraData = AceStringToKVObject(cbInfo_.env, extraDataTmp);
171
172 if (jsData != nullptr) {
173 AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "componentTemplate", jsPluginTemplate);
174 }
175 if (jsData != nullptr) {
176 AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "data", jsData);
177 }
178 if (jsExtraData != nullptr) {
179 AceSetPropertyValueByPropertyName(cbInfo_.env, jsObject, "extraData", jsExtraData);
180 }
181 return jsObject;
182 }
183
MakePluginTemplateObject(const PluginComponentTemplate & pluginTemplate)184 napi_value JSPluginCallback::MakePluginTemplateObject(const PluginComponentTemplate& pluginTemplate)
185 {
186 HILOG_INFO("%{public}s called.", __func__);
187 napi_value jsPluginTemplate = AceCreateJSObject(cbInfo_.env);
188 if (jsPluginTemplate != nullptr) {
189 napi_value jsSource = AceWrapStringToJS(cbInfo_.env, pluginTemplate.GetSource());
190 napi_value jsAbility = AceWrapStringToJS(cbInfo_.env, pluginTemplate.GetAbility());
191
192 AceSetPropertyValueByPropertyName(cbInfo_.env, jsPluginTemplate, "source", jsSource);
193 AceSetPropertyValueByPropertyName(cbInfo_.env, jsPluginTemplate, "ability", jsAbility);
194 }
195 return jsPluginTemplate;
196 }
197
OnPushEventInner(const OnPluginUvWorkData * workData)198 void JSPluginCallback::OnPushEventInner(const OnPluginUvWorkData* workData)
199 {
200 napi_value jsCallback = nullptr;
201 napi_value undefined = nullptr;
202 napi_value jsResult = nullptr;
203 napi_value callbackParam[ACE_ARGS_FOUR] = {nullptr};
204 napi_handle_scope scope = nullptr;
205 std::string dataTmp("{}");
206 std::string extraDataTmp("{}");
207 if (!workData->data.empty()) {
208 dataTmp = workData->data;
209 }
210 if (!workData->extraData.empty()) {
211 extraDataTmp = workData->extraData;
212 }
213
214 napi_open_handle_scope(cbInfo_.env, &scope);
215 if (scope == nullptr) {
216 napi_close_handle_scope(cbInfo_.env, scope);
217 return;
218 }
219
220 PluginComponentTemplate componentTemplate;
221 componentTemplate.SetSource(workData->sourceName);
222 componentTemplate.SetAbility(workData->abilityName);
223
224 callbackParam[ACE_PARAM0] = AceWrapWant(cbInfo_.env, workData->want);
225 callbackParam[ACE_PARAM1] = MakePluginTemplateObject(componentTemplate);
226 callbackParam[ACE_PARAM2] = AceStringToKVObject(cbInfo_.env, dataTmp);
227 callbackParam[ACE_PARAM3] = AceStringToKVObject(cbInfo_.env, extraDataTmp);
228
229 napi_get_undefined(cbInfo_.env, &undefined);
230 napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
231 napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_FOUR, callbackParam, &jsResult);
232 napi_close_handle_scope(cbInfo_.env, scope);
233 }
234
OnPushEvent(const AAFwk::Want & want,const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)235 void JSPluginCallback::OnPushEvent(const AAFwk::Want& want, const PluginComponentTemplate& pluginTemplate,
236 const std::string& data, const std::string& extraData)
237 {
238 HILOG_INFO("%{public}s called.", __func__);
239 if (cbInfo_.env == nullptr || cbInfo_.callback == nullptr) {
240 HILOG_INFO("%{public}s called, env or callback is null.", __func__);
241 return;
242 }
243
244 auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
245 if (!container) {
246 HILOG_INFO("%{public}s called, container is null. %{public}d", __func__, cbInfo_.containerId);
247 return;
248 }
249
250 auto taskExecutor = container->GetTaskExecutor();
251 if (!taskExecutor) {
252 HILOG_INFO("%{public}s called, taskExecutor is null.", __func__);
253 return;
254 }
255 std::weak_ptr<PluginComponentCallBack> weak = weak_from_this();
256 taskExecutor->PostTask(
257 [weak, want, sourceName = pluginTemplate.GetSource(), abilityName = pluginTemplate.GetAbility(), data,
258 extraData]() {
259 OnPluginUvWorkData uvWorkData;
260 uvWorkData.want = want;
261 uvWorkData.sourceName = sourceName;
262 uvWorkData.abilityName = abilityName;
263 uvWorkData.data = data;
264 uvWorkData.extraData = extraData;
265 auto callBack = weak.lock();
266 if (callBack) {
267 auto jsCallback = std::static_pointer_cast<JSPluginCallback>(callBack);
268 jsCallback->OnPushEventInner(&uvWorkData);
269 }
270 },
271 TaskExecutor::TaskType::UI);
272 }
273
OnRequestEventInner(const OnPluginUvWorkData * workData)274 void JSPluginCallback::OnRequestEventInner(const OnPluginUvWorkData* workData)
275 {
276 napi_value jsCallback = nullptr;
277 napi_value undefined = nullptr;
278 napi_value jsResult = nullptr;
279 napi_handle_scope scope = nullptr;
280 std::string dataTmp("{}");
281 if (!workData->data.empty()) {
282 dataTmp = workData->data;
283 }
284
285 napi_open_handle_scope(cbInfo_.env, &scope);
286 if (scope == nullptr) {
287 napi_close_handle_scope(cbInfo_.env, scope);
288 return;
289 }
290 napi_value callbackParam[ACE_ARGS_THREE] = {nullptr};
291 callbackParam[ACE_PARAM0] = AceWrapWant(cbInfo_.env, workData->want);
292 callbackParam[ACE_PARAM1] = AceWrapStringToJS(cbInfo_.env, workData->name);
293 callbackParam[ACE_PARAM2] = AceStringToKVObject(cbInfo_.env, dataTmp);
294
295 napi_get_undefined(cbInfo_.env, &undefined);
296 napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
297 napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_THREE, callbackParam, &jsResult);
298
299 if (AceIsTypeForNapiValue(cbInfo_.env, jsResult, napi_object)) {
300 SendRequestEventResult(jsResult);
301 }
302 napi_close_handle_scope(cbInfo_.env, scope);
303 }
304
OnRequestEvent(const AAFwk::Want & want,const std::string & name,const std::string & data)305 void JSPluginCallback::OnRequestEvent(const AAFwk::Want& want, const std::string& name,
306 const std::string& data)
307 {
308 if (cbInfo_.env == nullptr || cbInfo_.callback == nullptr) {
309 HILOG_INFO("%{public}s called, env or callback is null.", __func__);
310 return;
311 }
312
313 uvWorkData_.that = (void *)this;
314 uvWorkData_.want = want;
315 uvWorkData_.data = data;
316 uvWorkData_.name = name;
317
318 auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
319 if (!container) {
320 HILOG_INFO("%{public}s called, container is null. %{public}d", __func__, cbInfo_.containerId);
321 return;
322 }
323
324 auto taskExecutor = container->GetTaskExecutor();
325 if (!taskExecutor) {
326 HILOG_INFO("%{public}s called, taskExecutor is null.", __func__);
327 return;
328 }
329 taskExecutor->PostTask(
330 [uvWorkData = &uvWorkData_]() {
331 JSPluginCallback* context = (JSPluginCallback*)uvWorkData->that;
332 if (context) {
333 context->OnRequestEventInner(&context->uvWorkData_);
334 }
335 },
336 TaskExecutor::TaskType::JS);
337 }
338
OnRequestCallBackInner(const OnPluginUvWorkData * workData)339 void JSPluginCallback::OnRequestCallBackInner(const OnPluginUvWorkData* workData)
340 {
341 HILOG_INFO("%{public}s called.", __func__);
342 napi_value jsCallback = nullptr;
343 napi_value undefined = nullptr;
344 napi_value jsResult = nullptr;
345 napi_handle_scope scope = nullptr;
346 napi_open_handle_scope(cbInfo_.env, &scope);
347 if (scope == nullptr) {
348 napi_close_handle_scope(cbInfo_.env, scope);
349 return;
350 }
351 PluginComponentTemplate componentTemplate;
352 componentTemplate.SetSource(workData->sourceName);
353 componentTemplate.SetAbility(workData->abilityName);
354
355 if (cbInfo_.callback != nullptr) {
356 napi_value callbackParam[ACE_ARGS_TWO] = {nullptr};
357 callbackParam[ACE_PARAM0] = AceGetCallbackErrorValue(cbInfo_.env, 0);
358 callbackParam[ACE_PARAM1] = MakeCallbackParamForRequest(componentTemplate, workData->data,
359 workData->extraData);
360 napi_get_undefined(cbInfo_.env, &undefined);
361 napi_get_reference_value(cbInfo_.env, cbInfo_.callback, &jsCallback);
362 napi_call_function(cbInfo_.env, undefined, jsCallback, ACE_ARGS_TWO, callbackParam, &jsResult);
363 }
364 napi_close_handle_scope(cbInfo_.env, scope);
365 }
366
OnRequestCallBack(const PluginComponentTemplate & pluginTemplate,const std::string & data,const std::string & extraData)367 void JSPluginCallback::OnRequestCallBack(const PluginComponentTemplate& pluginTemplate,
368 const std::string& data, const std::string& extraData)
369 {
370 HILOG_INFO("%{public}s called.", __func__);
371 JSPluginCallbackMgr::Instance().UnRegisterEvent(GetID());
372 if (cbInfo_.env == nullptr) {
373 HILOG_INFO("%{public}s called, env or callback is null.", __func__);
374 return;
375 }
376
377 if (cbInfo_.callback != nullptr) {
378 uvWorkData_.that = (void*)this;
379 uvWorkData_.sourceName = pluginTemplate.GetSource();
380 uvWorkData_.abilityName = pluginTemplate.GetAbility();
381 uvWorkData_.data = data;
382 uvWorkData_.extraData = extraData;
383
384 if (getpid() != gettid()) {
385 uv_loop_s* loop = nullptr;
386 napi_get_uv_event_loop(cbInfo_.env, &loop);
387
388 struct ResultData {
389 JSPluginCallback* context = nullptr;
390 std::mutex mtx;
391 bool ready = false;
392 std::condition_variable cv;
393 };
394
395 std::shared_ptr<ResultData> resultData = std::make_shared<ResultData>();
396 resultData->context = this;
397
398 auto container = AceEngine::Get().GetContainer(cbInfo_.containerId);
399 if (!container) {
400 HILOG_INFO("%{public}s called, container is null. %{public}d", __func__, cbInfo_.containerId);
401 return;
402 }
403
404 auto taskExecutor = container->GetTaskExecutor();
405 if (!taskExecutor) {
406 HILOG_INFO("%{public}s called, taskExecutor is null.", __func__);
407 return;
408 }
409 taskExecutor->PostTask(
410 [resultData]() {
411 resultData->context->OnRequestCallBackInner(&resultData->context->uvWorkData_);
412
413 {
414 std::unique_lock<std::mutex> lock(resultData->mtx);
415 resultData->ready = true;
416 resultData->cv.notify_all();
417 }
418 },
419 TaskExecutor::TaskType::JS);
420
421 {
422 std::unique_lock<std::mutex> lock(resultData->mtx);
423 resultData->cv.wait(lock, [&] { return resultData->ready; });
424 }
425 } else {
426 OnRequestCallBackInner(&uvWorkData_);
427 }
428 } else {
429 if (asyncJSCallbackInfo_) {
430 asyncJSCallbackInfo_->requestCallbackData.sourceName = pluginTemplate.GetSource();
431 asyncJSCallbackInfo_->requestCallbackData.abilityName = pluginTemplate.GetAbility();
432 asyncJSCallbackInfo_->requestCallbackData.data = data;
433 asyncJSCallbackInfo_->requestCallbackData.extraData = extraData;
434 asyncJSCallbackInfo_->onRequestCallbackOK = true;
435 }
436 }
437 }
438
OnEventStrictEquals(CallBackType eventType,const AAFwk::Want & want,ACECallbackInfo & cbInfo)439 bool JSPluginCallback::OnEventStrictEquals(CallBackType eventType, const AAFwk::Want& want,
440 ACECallbackInfo& cbInfo)
441 {
442 if (eventType != eventType_) {
443 return false;
444 }
445 AppExecFwk::ElementName leftElement = want.GetElement();
446 AppExecFwk::ElementName rightElement = want_.GetElement();
447 if (leftElement == rightElement) {
448 return AceIsSameFuncFromJS(cbInfo, cbInfo_);
449 } else {
450 return false;
451 }
452 }
453
RequestStrictEquals(CallBackType eventType,const AAFwk::Want & want,ACECallbackInfo & cbInfo,const std::shared_ptr<AceJSPluginRequestParam> & param)454 bool JSPluginCallback::RequestStrictEquals(CallBackType eventType, const AAFwk::Want& want,
455 ACECallbackInfo& cbInfo, const std::shared_ptr<AceJSPluginRequestParam>& param)
456 {
457 if (eventType != eventType_) {
458 return false;
459 }
460 AppExecFwk::ElementName leftElement = want.GetElement();
461 AppExecFwk::ElementName rightElement = want_.GetElement();
462 if (!(leftElement == rightElement)) {
463 return false;
464 }
465 if (param == nullptr || requestParam_ == nullptr) {
466 return false;
467 }
468 if (*param != *requestParam_) {
469 return false;
470 }
471 return AceIsSameFuncFromJS(cbInfo, cbInfo_);
472 }
473 } // namespace OHOS::Ace::Napi
474