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