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
16 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_declarative_group_js_bridge.h"
17
18 #include "base/json/json_util.h"
19 #include "base/log/event_report.h"
20 #include "base/log/log.h"
21 #include "base/memory/ace_type.h"
22 #include "core/common/ace_page.h"
23 #include "core/common/js_message_dispatcher.h"
24 #include "frameworks/bridge/codec/function_call.h"
25 #include "frameworks/bridge/declarative_frontend/engine/js_execution_scope_defines.h"
26 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_declarative_engine.h"
27 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_module_manager.h"
28 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_types.h"
29 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_utils.h"
30 #include "frameworks/bridge/js_frontend/engine/common/js_constants.h"
31
32 namespace OHOS::Ace::Framework {
33 namespace {
34
35 const int32_t PLUGIN_REQUEST_MIN_ARGC_NUM = 4;
36 const int32_t PLUGIN_REQUEST_ARG_RESOLVE_INDEX = 0;
37 const int32_t PLUGIN_REQUEST_ARG_REJECT_INDEX = 1;
38 const int32_t PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX = 2;
39 const int32_t PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX = 3;
40 const int32_t PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX = 4;
41
42 const int32_t PLUGIN_REQUEST_MIN_ARGC_NUM_SYNC = 2;
43 const int32_t PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX_SYNC = 0;
44 const int32_t PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX_SYNC = 1;
45 const int32_t PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX_SYNC = 2;
46
47 } // namespace
48
InitializeGroupJsBridge(v8::Local<v8::Context> context)49 int32_t V8DeclarativeGroupJsBridge::InitializeGroupJsBridge(v8::Local<v8::Context> context)
50 {
51 LOGI("Enter InitializeGroupJsBridge");
52 if (context.IsEmpty()) {
53 LOGE("group module init, context is null");
54 EventReport::SendAPIChannelException(APIChannelExcepType::JS_BRIDGE_INIT_ERR);
55 return JS_CALL_FAIL;
56 }
57
58 v8::Isolate* isolate = context->GetIsolate();
59 ACE_DCHECK(isolate);
60 v8::HandleScope handleScope(isolate);
61
62 if (isolate_ == nullptr) {
63 isolate_ = isolate;
64 context_.Reset(isolate_, context);
65 }
66
67 if (LoadJsBridgeFunction(isolate) != JS_CALL_SUCCESS) {
68 LOGE("group module init, load bridge function failed!");
69 EventReport::SendAPIChannelException(APIChannelExcepType::JS_BRIDGE_INIT_ERR);
70 return JS_CALL_FAIL;
71 }
72
73 return JS_CALL_SUCCESS;
74 }
75
LoadJsBridgeFunction(v8::Isolate * isolate)76 int32_t V8DeclarativeGroupJsBridge::LoadJsBridgeFunction(v8::Isolate* isolate)
77 {
78 v8::HandleScope handleScope(isolate);
79 v8::Local<v8::Context> context = isolate->GetCurrentContext();
80 v8::Local<v8::Object> global = context->Global();
81 v8::Local<v8::Object> group = v8::Object::New(isolate);
82
83 bool succ = group->Set(context, v8::String::NewFromUtf8(isolate, "sendGroupMessage").ToLocalChecked(),
84 v8::Function::New(context, ProcessJsRequest).ToLocalChecked()).ToChecked();
85 if (!succ) {
86 LOGE("bridge function, set group message sending function mapping failed!");
87 EventReport::SendAPIChannelException(APIChannelExcepType::SET_FUNCTION_ERR);
88 return JS_CALL_FAIL;
89 }
90
91 succ = group->Set(context, v8::String::NewFromUtf8(isolate, "sendGroupMessageSync").ToLocalChecked(),
92 v8::Function::New(context, ProcessJsRequestSync).ToLocalChecked()).ToChecked();
93 if (!succ) {
94 LOGE("bridge function, set sync group message sending function mapping failed!");
95 EventReport::SendAPIChannelException(APIChannelExcepType::SET_FUNCTION_ERR);
96 return JS_CALL_FAIL;
97 }
98
99 succ = global->Set(context, v8::String::NewFromUtf8(isolate, "group").ToLocalChecked(), group).ToChecked();
100 if (!succ) {
101 LOGE("bridge function, set root node failed!");
102 EventReport::SendAPIChannelException(APIChannelExcepType::SET_FUNCTION_ERR);
103 return JS_CALL_FAIL;
104 }
105
106 return JS_CALL_SUCCESS;
107 }
108
CallEvalBuf(v8::Isolate * isolate,v8::Local<v8::String> src)109 int32_t V8DeclarativeGroupJsBridge::CallEvalBuf(v8::Isolate* isolate, v8::Local<v8::String> src)
110 {
111 if (isolate == nullptr) {
112 LOGE("isolate can't be null!");
113 return JS_CALL_FAIL;
114 }
115
116 v8::HandleScope handleScope(isolate);
117 v8::TryCatch tryCatch(isolate);
118 v8::Local<v8::Context> context = isolate->GetCurrentContext();
119
120 v8::Local<v8::Script> script;
121 if (!v8::Script::Compile(context, src).ToLocal(&script)) {
122 LOGE("compile js code failed!");
123 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch);
124 return JS_CALL_FAIL;
125 }
126
127 v8::Local<v8::Value> res;
128 if (!script->Run(context).ToLocal(&res)) {
129 LOGE("run js code failed!");
130 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch);
131 return JS_CALL_FAIL;
132 }
133 return JS_CALL_SUCCESS;
134 }
135
136 // function callback for groupObj's function: sendGroupMessage
ProcessJsRequest(const v8::FunctionCallbackInfo<v8::Value> & args)137 void V8DeclarativeGroupJsBridge::ProcessJsRequest(const v8::FunctionCallbackInfo<v8::Value>& args)
138 {
139 v8::Isolate* isolate = args.GetIsolate();
140 ACE_DCHECK(isolate);
141 v8::HandleScope handleScope(isolate);
142
143 auto delegate =
144 static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8DeclarativeEngineInstance::FRONTEND_DELEGATE));
145 if (delegate == nullptr) {
146 LOGE("send message para check, fail to get front-end delegate");
147 return;
148 }
149
150 auto groupJsBridge = AceType::DynamicCast<V8DeclarativeGroupJsBridge>((*delegate)->GetGroupJsBridge());
151 if (groupJsBridge == nullptr) {
152 LOGE("send message para check, fail to get group-js-bridge");
153 return;
154 }
155
156 // Should have at least 4 parameters
157 if (args.Length() < PLUGIN_REQUEST_MIN_ARGC_NUM) {
158 LOGE("send message para check, invalid args number:%{public}d", args.Length());
159 return;
160 }
161
162 int32_t callbackId = groupJsBridge->GetPendingCallbackIdAndIncrement();
163 groupJsBridge->AddCallbackIdIsolateRelation(callbackId, isolate);
164 if (!groupJsBridge->SetModuleGroupCallbackFuncs(
165 args, PLUGIN_REQUEST_ARG_RESOLVE_INDEX, PLUGIN_REQUEST_ARG_REJECT_INDEX, callbackId)) {
166 LOGE("send message, set module callback function failed!");
167 return;
168 }
169
170 v8::String::Utf8Value jsGroupName(isolate, args[PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX]);
171 const char* groupName = *jsGroupName;
172 if (groupName == nullptr) {
173 groupJsBridge->TriggerModulePluginGetErrorCallback(
174 callbackId, PLUGIN_REQUEST_FAIL, "plugin name can't be null");
175 LOGE("plugin name is null");
176 return;
177 }
178
179 LOGI("send message, groupName: %{private}s, callbackId: %{private}d", groupName, callbackId);
180 std::string strGroupName(groupName);
181
182 v8::String::Utf8Value jsFunctionName(isolate, args[PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX]);
183 const char* functionName = *jsFunctionName;
184 std::string strFunctionName(functionName);
185
186 std::vector<CodecData> arguments;
187 ParseJsDataResult parseJsResult =
188 groupJsBridge->ParseJsPara(args, PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX, callbackId, arguments);
189 if (parseJsResult != ParseJsDataResult::PARSE_JS_SUCCESS) {
190 ProcessParseJsError(parseJsResult, isolate, callbackId);
191 return;
192 }
193
194 FunctionCall functionCall(strFunctionName, arguments);
195 StandardFunctionCodec codec;
196 std::vector<uint8_t> encodeBuf;
197 if (!codec.EncodeFunctionCall(functionCall, encodeBuf)) {
198 groupJsBridge->TriggerModulePluginGetErrorCallback(
199 callbackId, PLUGIN_REQUEST_FAIL, "encode request message failed");
200 return;
201 }
202
203 // CallPlatformFunction
204 auto dispatcher =
205 static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8DeclarativeEngineInstance::DISPATCHER));
206 if (dispatcher == nullptr) {
207 LOGE("dispatcher is nullptr");
208 return;
209 }
210 auto dispatcherUpgrade = (*dispatcher).Upgrade();
211 if (dispatcherUpgrade != nullptr) {
212 dispatcherUpgrade->Dispatch(strGroupName, std::move(encodeBuf), callbackId);
213 } else {
214 LOGW("Dispatcher Upgrade fail when dispatch request message to platform");
215 groupJsBridge->TriggerModulePluginGetErrorCallback(callbackId, PLUGIN_REQUEST_FAIL, "send message failed");
216 }
217 }
218
219 // function callback for groupObj's function: sendGroupMessageSync
ProcessJsRequestSync(const v8::FunctionCallbackInfo<v8::Value> & args)220 void V8DeclarativeGroupJsBridge::ProcessJsRequestSync(const v8::FunctionCallbackInfo<v8::Value>& args)
221 {
222 v8::Isolate* isolate = args.GetIsolate();
223 ACE_DCHECK(isolate);
224 v8::HandleScope handleScope(isolate);
225
226 auto delegate =
227 static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8DeclarativeEngineInstance::FRONTEND_DELEGATE));
228 if (delegate == nullptr) {
229 LOGE("send message para check, fail to get front-end delegate");
230 return;
231 }
232
233 auto groupJsBridge = AceType::DynamicCast<V8DeclarativeGroupJsBridge>((*delegate)->GetGroupJsBridge());
234 if (groupJsBridge == nullptr) {
235 LOGE("send message para check, fail to get group-js-bridge");
236 return;
237 }
238
239 // Should have at least 2 parameters
240 if (args.Length() < PLUGIN_REQUEST_MIN_ARGC_NUM_SYNC) {
241 LOGE("send message para check, invalid args number:%{public}d", args.Length());
242 return;
243 }
244
245 v8::String::Utf8Value jsGroupName(isolate, args[PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX_SYNC]);
246 const char* groupName = *jsGroupName;
247 if (groupName == nullptr) {
248 LOGE("plugin name is null");
249 return;
250 }
251 std::string strGroupName(groupName);
252
253 v8::String::Utf8Value jsFunctionName(isolate, args[PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX_SYNC]);
254 const char* functionName = *jsFunctionName;
255 std::string strFunctionName(functionName);
256 LOGI("send message, groupName: %{private}s, functionName: %{private}s", groupName, functionName);
257
258 std::vector<CodecData> arguments;
259 ParseJsDataResult parseJsResult =
260 groupJsBridge->ParseJsPara(args, PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX_SYNC, 0, arguments);
261 if (parseJsResult != ParseJsDataResult::PARSE_JS_SUCCESS) {
262 LOGE("parse js data error");
263 return;
264 }
265
266 FunctionCall functionCall(strFunctionName, arguments);
267 StandardFunctionCodec codec;
268 std::vector<uint8_t> encodeBuf;
269 if (!codec.EncodeFunctionCall(functionCall, encodeBuf)) {
270 LOGE("encode arguments fail");
271 return;
272 }
273
274 // CallPlatformFunction
275 auto dispatcher =
276 static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8DeclarativeEngineInstance::DISPATCHER));
277 if (dispatcher == nullptr) {
278 LOGE("dispatcher is nullptr");
279 return;
280 }
281 auto dispatcherUpgrade = (*dispatcher).Upgrade();
282
283 uint8_t* resData = nullptr;
284 int64 position = 0;
285
286 if (dispatcherUpgrade != nullptr) {
287 dispatcherUpgrade->DispatchSync(strGroupName, std::move(encodeBuf), &resData, position);
288 } else {
289 LOGW("Dispatcher Upgrade fail when dispatch request message to platform");
290 return;
291 }
292
293 std::vector<uint8_t> messageData = std::vector<uint8_t>(resData, resData + position);
294
295 v8::Local<v8::Value> callBackResult;
296 CodecData codecResult;
297 if (codec.DecodePlatformMessage(messageData, codecResult)) {
298 std::string resultString = codecResult.GetStringValue();
299 LOGI("sync resultString = %{private}s", resultString.c_str());
300 if (resultString.empty()) {
301 callBackResult = v8::Null(isolate);
302 } else {
303 callBackResult = v8::String::NewFromUtf8(isolate, resultString.c_str()).ToLocalChecked();
304 }
305 args.GetReturnValue().Set(callBackResult);
306 }
307 }
308
SetEventGroupCallBackFuncs(v8::Isolate * isolate,v8::Local<v8::Value> localEventCallbackFunc,int32_t callbackId,int32_t requestId)309 bool V8DeclarativeGroupJsBridge::SetEventGroupCallBackFuncs(
310 v8::Isolate* isolate, v8::Local<v8::Value> localEventCallbackFunc, int32_t callbackId, int32_t requestId)
311 {
312 if (localEventCallbackFunc->IsNull() || !localEventCallbackFunc->IsFunction()) {
313 LOGE("callback function is invalid!");
314 return false;
315 }
316
317 LOGI("record event callback, requestId:%{private}d, callbackId:%{private}d", requestId, callbackId);
318 v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> eventCallbackFunc;
319 eventCallbackFunc.Reset(isolate, localEventCallbackFunc);
320 auto result = eventCallBackFuncs_.try_emplace(callbackId, eventCallbackFunc);
321 if (!result.second) {
322 result.first->second.Reset();
323 result.first->second.Reset(isolate, localEventCallbackFunc);
324 }
325
326 AddRequestIdCallbackIdRelation(callbackId, requestId);
327
328 return true;
329 }
330
RemoveEventGroupCallBackFuncs(int32_t callbackId)331 void V8DeclarativeGroupJsBridge::RemoveEventGroupCallBackFuncs(int32_t callbackId)
332 {
333 LOGI("remove event callback, callbackId:%{private}d", callbackId);
334 auto itFunc = eventCallBackFuncs_.find(callbackId);
335 if (itFunc != eventCallBackFuncs_.end()) {
336 itFunc->second.Reset();
337 eventCallBackFuncs_.erase(callbackId);
338 }
339 }
340
AddRequestIdCallbackIdRelation(int32_t eventId,int32_t requestId)341 void V8DeclarativeGroupJsBridge::AddRequestIdCallbackIdRelation(int32_t eventId, int32_t requestId)
342 {
343 std::lock_guard<std::mutex> lock(requestIdCallbackIdMapMutex_);
344 auto result = requestIdCallbackIdMap_.try_emplace(requestId, eventId);
345 if (!result.second) {
346 result.first->second = eventId;
347 }
348 }
349
RemoveRequestIdCallbackIdRelation(int32_t requestId,bool removeEventCallback)350 void V8DeclarativeGroupJsBridge::RemoveRequestIdCallbackIdRelation(int32_t requestId, bool removeEventCallback)
351 {
352 requestIdCallbackIdMapMutex_.lock();
353 auto eventId = requestIdCallbackIdMap_.find(requestId);
354 requestIdCallbackIdMapMutex_.unlock();
355 if (eventId != requestIdCallbackIdMap_.end()) {
356 if (removeEventCallback) {
357 RemoveEventGroupCallBackFuncs(eventId->second);
358 }
359 requestIdCallbackIdMapMutex_.lock();
360 requestIdCallbackIdMap_.erase(requestId);
361 requestIdCallbackIdMapMutex_.unlock();
362 }
363 }
364
AddCallbackIdIsolateRelation(int32_t callbackId,v8::Isolate * isolate)365 void V8DeclarativeGroupJsBridge::AddCallbackIdIsolateRelation(int32_t callbackId, v8::Isolate* isolate)
366 {
367 std::lock_guard<std::mutex> lock(callbackIdIsolateMapMutex_);
368 auto result = callbackIdIsolateMap_.try_emplace(callbackId, isolate);
369 if (!result.second) {
370 result.first->second = isolate;
371 }
372 LOGD("callbackIdIsolateMap size = %{private}zu", callbackIdIsolateMap_.size());
373 }
374
AddIsolateNativeEngineRelation(v8::Isolate * isolate,NativeEngine * nativeEngine)375 void V8DeclarativeGroupJsBridge::AddIsolateNativeEngineRelation(v8::Isolate* isolate, NativeEngine* nativeEngine)
376 {
377 if (isolate == nullptr || nativeEngine == nullptr) {
378 LOGE("isolate or nativeEngine is nullptr");
379 return;
380 }
381 std::lock_guard<std::mutex> lock(isolateNativeEngineMapMutex_);
382 auto result = isolateNativeEngineMap_.try_emplace(isolate, nativeEngine);
383 if (!result.second) {
384 result.first->second = nativeEngine;
385 }
386 LOGD("isolateNativeEngineMap size = %{private}zu", isolateNativeEngineMap_.size());
387 }
388
ProcessParseJsError(ParseJsDataResult errorType,v8::Isolate * isolate,int32_t callbackId)389 void V8DeclarativeGroupJsBridge::ProcessParseJsError(
390 ParseJsDataResult errorType, v8::Isolate* isolate, int32_t callbackId)
391 {
392 // PluginErrorCallback
393 auto dispatcher =
394 static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8DeclarativeEngineInstance::DISPATCHER));
395 if (dispatcher == nullptr) {
396 LOGE("dispatcher is nullptr");
397 return;
398 }
399 auto dispatcherUpgrade = (*dispatcher).Upgrade();
400 if (dispatcherUpgrade == nullptr) {
401 LOGW("Dispatcher Upgrade fail at ProcessParseJsError");
402 return;
403 }
404 std::string errMessage;
405 switch (errorType) {
406 case ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE:
407 errMessage = "unsupported js parameter types";
408 dispatcherUpgrade->DispatchPluginError(callbackId,
409 static_cast<int32_t>(ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE), std::move(errMessage));
410 break;
411 case ParseJsDataResult::PARSE_JS_ERR_TOO_MANY_PARAM:
412 errMessage = "the number of parameters exceeds 255";
413 dispatcherUpgrade->DispatchPluginError(callbackId,
414 static_cast<int32_t>(ParseJsDataResult::PARSE_JS_ERR_TOO_MANY_PARAM), std::move(errMessage));
415 break;
416 default:
417 break;
418 }
419 }
420
SetModuleGroupCallbackFuncs(const v8::FunctionCallbackInfo<v8::Value> & args,int32_t resolveCallbackIndex,int32_t rejectCallbackIndex,int32_t callbackId)421 bool V8DeclarativeGroupJsBridge::SetModuleGroupCallbackFuncs(const v8::FunctionCallbackInfo<v8::Value>& args,
422 int32_t resolveCallbackIndex, int32_t rejectCallbackIndex, int32_t callbackId)
423 {
424 v8::Isolate* isolate = args.GetIsolate();
425 ACE_DCHECK(isolate);
426 v8::HandleScope handleScope(isolate);
427
428 LOGD("Enter SetModuleGroupCallbackFuncs");
429 if (args[resolveCallbackIndex]->IsNull() || !args[resolveCallbackIndex]->IsFunction() ||
430 args[rejectCallbackIndex]->IsNull() || !args[rejectCallbackIndex]->IsFunction()) {
431 LOGE("resolve or reject callback function is invalid");
432 return false;
433 }
434
435 PromiseCallback promiseCallJsFunc;
436
437 promiseCallJsFunc.resolveCallback.Reset(isolate, args[resolveCallbackIndex]);
438 promiseCallJsFunc.rejectCallback.Reset(isolate, args[rejectCallbackIndex]);
439
440 moduleCallbackMapMutex_.lock();
441 auto result = moduleCallBackFuncs_.try_emplace(callbackId, promiseCallJsFunc);
442 moduleCallbackMapMutex_.unlock();
443 if (!result.second) {
444 LOGE("module callback function has been existed!");
445 return false;
446 }
447 return true;
448 }
449
SerializationObjectToString(v8::Local<v8::Context> context,v8::Local<v8::Value> val)450 std::string V8DeclarativeGroupJsBridge::SerializationObjectToString(
451 v8::Local<v8::Context> context, v8::Local<v8::Value> val)
452 {
453 v8::Isolate* isolate = context->GetIsolate();
454 ACE_DCHECK(isolate);
455 v8::HandleScope handleScope(isolate);
456 v8::TryCatch tryCatch(isolate);
457
458 v8::Local<v8::Value> strValue;
459 v8::Local<v8::Value> global;
460 v8::Local<v8::Value> json;
461 v8::Local<v8::Value> func;
462
463 global = context->Global();
464 if (!global->IsObject()) {
465 LOGE("SerializationObjectToString error: fail to get Global Object");
466 return "";
467 }
468 v8::Local<v8::Object> globalObj = global->ToObject(context).ToLocalChecked();
469 json = globalObj->Get(context, v8::String::NewFromUtf8(isolate, "JSON").ToLocalChecked()).ToLocalChecked();
470 if (!json->IsObject()) {
471 LOGE("SerializationObjectToString error: global has no attribute JSON");
472 return "";
473 }
474 v8::Local<v8::Object> jsonObj = json->ToObject(context).ToLocalChecked();
475 func = jsonObj->Get(context, v8::String::NewFromUtf8(isolate, "stringify").ToLocalChecked()).ToLocalChecked();
476 if (!func->IsFunction()) {
477 LOGE("SerializationObjectToString error: JSON has no attribute stringify");
478 return "";
479 }
480 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(func);
481 v8::Local<v8::Value> argv[] = { val };
482 bool succ = function->Call(context, globalObj, 1, argv).ToLocal(&strValue);
483 if (!succ) {
484 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch);
485 return "";
486 }
487
488 v8::String::Utf8Value resStr(isolate, strValue);
489 std::string res;
490 if (*resStr) {
491 res = *resStr;
492 }
493 return res;
494 }
495
ParseJsPara(const v8::FunctionCallbackInfo<v8::Value> & args,int32_t beginIndex,int32_t requestId,std::vector<CodecData> & arguments)496 ParseJsDataResult V8DeclarativeGroupJsBridge::ParseJsPara(const v8::FunctionCallbackInfo<v8::Value>& args,
497 int32_t beginIndex, int32_t requestId, std::vector<CodecData>& arguments)
498 {
499 if (args.Length() < beginIndex) { // no others params
500 return ParseJsDataResult::PARSE_JS_SUCCESS;
501 }
502
503 v8::Isolate* isolate = args.GetIsolate();
504 ACE_DCHECK(isolate);
505 v8::HandleScope handleScope(isolate);
506 auto context = isolate->GetCurrentContext();
507
508 for (int32_t i = beginIndex; i < args.Length(); i++) {
509 v8::Local<v8::Value> val = args[i];
510 if (val->IsString()) {
511 v8::String::Utf8Value jsPara(isolate, val);
512 if (*jsPara) {
513 std::string para(*jsPara);
514 CodecData arg(para);
515 arguments.push_back(arg);
516 }
517 } else if (val->IsNumber()) {
518 if (val->IsInt32()) {
519 int32_t valInt = val->Int32Value(context).ToChecked();
520 CodecData arg(valInt);
521 arguments.push_back(arg);
522 } else {
523 double valDouble = val->NumberValue(context).ToChecked();
524 CodecData arg(valDouble);
525 arguments.push_back(arg);
526 }
527 } else if (val->IsBoolean()) {
528 bool valBool = val->BooleanValue(isolate);
529 CodecData arg(valBool);
530 arguments.push_back(arg);
531 } else if (val->IsNull()) {
532 CodecData argNull;
533 arguments.push_back(argNull);
534 } else if (val->IsFunction()) {
535 int32_t functionId = GetPendingCallbackIdAndIncrement();
536 CodecData arg(functionId, BufferDataType::TYPE_FUNCTION);
537 arguments.push_back(arg);
538 SetEventGroupCallBackFuncs(isolate, val, functionId, requestId);
539 } else if (val->IsArray() || val->IsObject()) {
540 std::string objStr = SerializationObjectToString(context, val);
541 CodecData arg(objStr, BufferDataType::TYPE_OBJECT);
542 arguments.push_back(arg);
543 } else if (val->IsUndefined()) {
544 LOGD("Process callNative para type:undefined");
545 } else {
546 LOGE("Process callNative para type: unsupported type");
547 return ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE;
548 }
549 }
550 return ParseJsDataResult::PARSE_JS_SUCCESS;
551 }
552
TriggerModuleJsCallback(int32_t callbackId,int32_t code,std::vector<uint8_t> && messageData)553 void V8DeclarativeGroupJsBridge::TriggerModuleJsCallback(
554 int32_t callbackId, int32_t code, std::vector<uint8_t>&& messageData)
555 {
556 LOGD("TriggerModuleJsCallback in");
557 CodecData codecResult;
558 StandardFunctionCodec codec;
559 std::string resultString;
560 if (codec.DecodePlatformMessage(messageData, codecResult)) {
561 resultString = codecResult.GetStringValue();
562 } else {
563 LOGE("trigger JS result function error, decode message fail, callbackId:%{private}d", callbackId);
564 code = PLUGIN_REQUEST_FAIL;
565 resultString = std::string("{\"code\":")
566 .append(std::to_string(code))
567 .append(",")
568 .append("\"data\":\"invalid response data\"}");
569 }
570 messageData.clear();
571
572 v8::Isolate* isolate = isolate_;
573 callbackIdIsolateMapMutex_.lock();
574 auto result = callbackIdIsolateMap_.find(callbackId);
575 callbackIdIsolateMapMutex_.unlock();
576 if (result != callbackIdIsolateMap_.end()) {
577 isolate = result->second;
578 callbackIdIsolateMapMutex_.lock();
579 callbackIdIsolateMap_.erase(callbackId);
580 callbackIdIsolateMapMutex_.unlock();
581 isolateNativeEngineMapMutex_.lock();
582 auto nativeEngineResult = isolateNativeEngineMap_.find(isolate);
583 isolateNativeEngineMapMutex_.unlock();
584 if (nativeEngineResult != isolateNativeEngineMap_.end()) {
585 // Trigger worker js callback
586 NativeEngine* nativeEngine = nativeEngineResult->second;
587 if (nativeEngine == nullptr) {
588 LOGE("nativeEngine is nullptr");
589 return;
590 }
591 JsCallbackData* callbackData = new JsCallbackData(callbackId, code, resultString, this, isolate);
592 nativeEngine->SendAsyncWork(callbackData);
593 return;
594 }
595 } else {
596 LOGE("callbackIdIsolateMap find failed");
597 }
598
599 TriggerModuleJsCallback(callbackId, code, resultString, isolate);
600 }
601
TriggerModuleJsCallback(int32_t callbackId,int32_t code,std::string result,v8::Isolate * isolate)602 void V8DeclarativeGroupJsBridge::TriggerModuleJsCallback(int32_t callbackId, int32_t code,
603 std::string result, v8::Isolate* isolate)
604 {
605 v8::HandleScope handleScope(isolate);
606 v8::TryCatch tryCatch(isolate);
607
608 v8::Local<v8::Value> callBackResult;
609 if (result.empty()) {
610 callBackResult = v8::Null(isolate);
611 } else {
612 callBackResult = v8::String::NewFromUtf8(isolate, result.c_str()).ToLocalChecked();
613 }
614 CallModuleJsCallback(callbackId, code, callBackResult, isolate);
615 }
616
NativeAsyncExecuteCallback(NativeEngine * engine,void * data)617 void V8DeclarativeGroupJsBridge::NativeAsyncExecuteCallback(NativeEngine* engine, void* data)
618 {
619 LOGD("NativeAsyncExecuteCallback in");
620 auto callbackData = static_cast<JsCallbackData*>(data);
621 if (callbackData == nullptr) {
622 LOGE("callbackData is nullptr");
623 return;
624 }
625 if (callbackData->groupJsBridge == nullptr) {
626 LOGE("callbackData groupJsBridge is nullptr");
627 return;
628 }
629 callbackData->groupJsBridge->TriggerModuleJsCallback(callbackData->callbackId, callbackData->code,
630 callbackData->result, callbackData->isolate);
631 }
632
NativeAsyncCompleteCallback(NativeEngine * engine,int status,void * data)633 void V8DeclarativeGroupJsBridge::NativeAsyncCompleteCallback(NativeEngine* engine, int status, void* data)
634 {
635 LOGD("NativeAsyncCompleteCallback in");
636 auto callbackData = static_cast<JsCallbackData*>(data);
637 if (callbackData == nullptr) {
638 LOGE("callbackData is nullptr in CompleteCallback");
639 return;
640 }
641 delete callbackData;
642 callbackData = nullptr;
643 }
644
CallModuleJsCallback(int32_t callbackId,int32_t code,v8::Local<v8::Value> callBackResult,v8::Isolate * isolate)645 void V8DeclarativeGroupJsBridge::CallModuleJsCallback(
646 int32_t callbackId, int32_t code, v8::Local<v8::Value> callBackResult, v8::Isolate* isolate)
647 {
648 LOGD("CallModuleJsCallback in");
649 RemoveRequestIdCallbackIdRelation(callbackId, code != PLUGIN_REQUEST_SUCCESS);
650
651 v8::Isolate::Scope isolateScope(isolate);
652 v8::HandleScope handleScope(isolate);
653 v8::Local<v8::Context> context = isolate->GetCurrentContext();
654 v8::Context::Scope contextScope(context);
655 v8::Local<v8::Object> global = context->Global();
656
657 v8::TryCatch tryCatch(isolate);
658
659 moduleCallbackMapMutex_.lock();
660 auto itFunc = moduleCallBackFuncs_.find(callbackId);
661 moduleCallbackMapMutex_.unlock();
662 if (itFunc != moduleCallBackFuncs_.end()) {
663 v8::Local<v8::Value> jsFunc = (code == PLUGIN_REQUEST_SUCCESS ? itFunc->second.resolveCallback.Get(isolate)
664 : itFunc->second.rejectCallback.Get(isolate));
665 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
666 LOGE("trigger JS result function error, it is not a function, callbackId:%{private}d", callbackId);
667 return;
668 }
669
670 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
671 v8::Local<v8::Value> argv[] = { callBackResult };
672 // Pass only 1 parameter, call promise resolve call back.
673 v8::Local<v8::Value> res;
674 v8::Persistent<v8::Context> persistentContext;
675 persistentContext.Reset(isolate, context);
676 auto executionContext = V8ExecutionContext { isolate, persistentContext };
677 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK_THREAD(executionContext);
678 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
679 if (succ) {
680 LOGI("trigger JS result function success, callbackId:%{private}d, code:%{private}d", callbackId, code);
681 } else {
682 LOGW("trigger JS result function fail, callbackId:%{private}d, code:%{private}d", callbackId, code);
683 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch);
684 }
685
686 while (v8::platform::PumpMessageLoop(V8DeclarativeEngine::GetPlatform().get(), isolate)) {
687 continue;
688 }
689
690 itFunc->second.rejectCallback.Reset();
691 itFunc->second.resolveCallback.Reset();
692 moduleCallbackMapMutex_.lock();
693 moduleCallBackFuncs_.erase(itFunc);
694 moduleCallbackMapMutex_.unlock();
695 } else {
696 LOGE("trigger JS result function is not exists, callbackId:%{private}d, code:%{private}d", callbackId, code);
697 }
698 }
699
TriggerModulePluginGetErrorCallback(int32_t callbackId,int32_t errorCode,std::string && errorMessage)700 void V8DeclarativeGroupJsBridge::TriggerModulePluginGetErrorCallback(
701 int32_t callbackId, int32_t errorCode, std::string&& errorMessage)
702 {
703 LOGD("TriggerModulePluginGetErrorCallback in");
704 RemoveRequestIdCallbackIdRelation(callbackId, true);
705
706 auto resultJson = JsonUtil::Create(true);
707 resultJson->Put(std::string("code").c_str(), errorCode);
708 resultJson->Put(std::string("data").c_str(), errorMessage.c_str());
709
710 v8::Isolate* isolate = isolate_;
711 callbackIdIsolateMapMutex_.lock();
712 auto result = callbackIdIsolateMap_.find(callbackId);
713 callbackIdIsolateMapMutex_.unlock();
714 if (result != callbackIdIsolateMap_.end()) {
715 isolate = result->second;
716 callbackIdIsolateMapMutex_.lock();
717 callbackIdIsolateMap_.erase(callbackId);
718 callbackIdIsolateMapMutex_.unlock();
719 isolateNativeEngineMapMutex_.lock();
720 auto nativeEngineResult = isolateNativeEngineMap_.find(isolate);
721 isolateNativeEngineMapMutex_.unlock();
722 if (nativeEngineResult != isolateNativeEngineMap_.end()) {
723 // Trigger worker js callback
724 NativeEngine* nativeEngine = nativeEngineResult->second;
725 if (nativeEngine == nullptr) {
726 LOGE("nativeEngine is nullptr");
727 return;
728 }
729 JsCallbackData* callbackData = new JsCallbackData(callbackId, errorCode,
730 resultJson->ToString(), this, isolate);
731 nativeEngine->SendAsyncWork(callbackData);
732 return;
733 }
734 } else {
735 LOGE("callbackIdIsolateMap find failed");
736 }
737
738 v8::HandleScope handleScope(isolate);
739 v8::Local<v8::Context> context = isolate->GetCurrentContext();
740 v8::Local<v8::Object> global = context->Global();
741
742 moduleCallbackMapMutex_.lock();
743 auto itFunc = moduleCallBackFuncs_.find(callbackId);
744 moduleCallbackMapMutex_.unlock();
745 if (itFunc != moduleCallBackFuncs_.end()) {
746 v8::Local<v8::Value> jsFunc = itFunc->second.rejectCallback.Get(isolate);
747 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
748 LOGE("trigger JS result function error, reject is not a function, callbackId:%{private}d", callbackId);
749 return;
750 }
751
752 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
753 v8::Local<v8::String> emptyReplyCallback =
754 v8::String::NewFromUtf8(isolate, resultJson->ToString().c_str()).ToLocalChecked();
755 v8::Local<v8::Value> argv[] = { emptyReplyCallback };
756 // Pass only 1 parameter, call promise reject call back for error get in plugin.
757 v8::Local<v8::Value> res;
758 v8::Persistent<v8::Context> persistentContext;
759 persistentContext.Reset(isolate, context);
760 auto executionContext = V8ExecutionContext { isolate, persistentContext };
761 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK_THREAD(executionContext);
762 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
763 if (succ) {
764 LOGI("trigger JS result function success, callbackId:%{private}d", callbackId);
765 } else {
766 LOGW("trigger JS result function fail, callbackId:%{private}d", callbackId);
767 }
768
769 while (v8::platform::PumpMessageLoop(V8DeclarativeEngine::GetPlatform().get(), isolate)) {
770 continue;
771 }
772
773 itFunc->second.rejectCallback.Reset();
774 itFunc->second.resolveCallback.Reset();
775 moduleCallbackMapMutex_.lock();
776 moduleCallBackFuncs_.erase(itFunc);
777 moduleCallbackMapMutex_.unlock();
778 } else {
779 LOGE("trigger JS result function is not exists, callbackId:%{private}d", callbackId);
780 }
781 }
782
CallEventJsCallback(int32_t callbackId,std::vector<uint8_t> && eventData)783 void V8DeclarativeGroupJsBridge::CallEventJsCallback(int32_t callbackId, std::vector<uint8_t>&& eventData)
784 {
785 v8::Isolate* isolate = isolate_;
786 callbackIdIsolateMapMutex_.lock();
787 auto result = callbackIdIsolateMap_.find(callbackId);
788 callbackIdIsolateMapMutex_.unlock();
789 if (result != callbackIdIsolateMap_.end()) {
790 isolate = result->second;
791 } else {
792 LOGE("callbackIdIsolateMap find failed");
793 }
794
795 v8::HandleScope handleScope(isolate);
796 v8::Local<v8::Context> context = isolate->GetCurrentContext();
797 v8::Local<v8::Object> global = context->Global();
798
799 v8::Local<v8::Value> callBackEvent;
800 CodecData codecEvent;
801 StandardFunctionCodec codec;
802 if (codec.DecodePlatformMessage(eventData, codecEvent)) {
803 std::string eventString = codecEvent.GetStringValue();
804 if (eventString.empty()) {
805 callBackEvent = v8::Null(isolate);
806 } else {
807 callBackEvent = v8::String::NewFromUtf8(isolate, eventString.c_str()).ToLocalChecked();
808 }
809 } else {
810 LOGE("trigger JS callback function error, decode message fail, callbackId:%{private}d", callbackId);
811 return;
812 }
813
814 auto itFunc = eventCallBackFuncs_.find(callbackId);
815 if (itFunc != eventCallBackFuncs_.end()) {
816 v8::Local<v8::Value> jsFunc = itFunc->second.Get(isolate);
817 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
818 LOGE("trigger JS callback function error, callback is not a function, callbackId:%{private}d", callbackId);
819 return;
820 }
821
822 // Pass only 1 parameter
823 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
824 v8::Local<v8::Value> argv[] = { callBackEvent };
825 v8::Local<v8::Value> res;
826 v8::Persistent<v8::Context> persistentContext;
827 persistentContext.Reset(isolate, context);
828 auto executionContext = V8ExecutionContext { isolate, persistentContext };
829 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK_THREAD(executionContext);
830 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
831 if (!succ) {
832 LOGW("trigger JS callback function failed, callbackId:%{private}d", callbackId);
833 } else {
834 LOGW("trigger JS callback function success, callbackId:%{private}d", callbackId);
835 }
836
837 while (v8::platform::PumpMessageLoop(V8DeclarativeEngine::GetPlatform().get(), isolate)) {
838 continue;
839 }
840 } else {
841 LOGE("trigger JS callback function error, it is not exists, callbackId:%{private}d", callbackId);
842 }
843 eventData.clear();
844 }
845
TriggerEventJsCallback(int32_t callbackId,int32_t code,std::vector<uint8_t> && eventData)846 void V8DeclarativeGroupJsBridge::TriggerEventJsCallback(
847 int32_t callbackId, int32_t code, std::vector<uint8_t>&& eventData)
848 {
849 LOGD("TriggerEventJsCallback in");
850 if (code == PLUGIN_CALLBACK_DESTROY) {
851 RemoveEventGroupCallBackFuncs(callbackId);
852 } else {
853 CallEventJsCallback(callbackId, std::move(eventData));
854 }
855 }
856
LoadPluginJsCode(std::string && jsCode)857 void V8DeclarativeGroupJsBridge::LoadPluginJsCode(std::string&& jsCode)
858 {
859 LOGI("Load plugin js code, code len:%{public}d", static_cast<int32_t>(jsCode.length()));
860 jsCode_ = std::move(jsCode);
861 v8::HandleScope handleScope(isolate_);
862 v8::Local<v8::Context> context = context_.Get(isolate_);
863 v8::Context::Scope contextScope(context);
864
865 v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate_, jsCode_.c_str()).ToLocalChecked();
866 if (CallEvalBuf(isolate_, source) != JS_CALL_SUCCESS) {
867 LOGE("run plugin js code failed!");
868 }
869 }
870
LoadPluginJsByteCode(std::vector<uint8_t> && jsCode,std::vector<int32_t> && jsCodeLen)871 void V8DeclarativeGroupJsBridge::LoadPluginJsByteCode(std::vector<uint8_t>&& jsCode, std::vector<int32_t>&& jsCodeLen)
872 {
873 LOGW("V8 do not support load js bytecode now.");
874 }
875
Destroy(v8::Isolate * isolate,bool isWorker)876 void V8DeclarativeGroupJsBridge::Destroy(v8::Isolate* isolate, bool isWorker)
877 {
878 if (!isWorker) {
879 eventCallBackFuncs_.clear();
880 requestIdCallbackIdMap_.clear();
881 context_.Reset();
882 }
883
884 ModuleManager::GetInstance()->ClearTimerIsolate(isolate);
885 DestroyModuleCallbackMap(isolate);
886 DestroyCallbackIdIsolateMap(isolate);
887 DestroyIsolateNativeEngineMap(isolate);
888 }
889
GetCallbackId(v8::Isolate * isolate,std::set<int32_t> & callbackIdSet)890 void V8DeclarativeGroupJsBridge::GetCallbackId(v8::Isolate* isolate, std::set<int32_t>& callbackIdSet)
891 {
892 std::lock_guard<std::mutex> lock(callbackIdIsolateMapMutex_);
893 for (auto iter = callbackIdIsolateMap_.begin(); iter != callbackIdIsolateMap_.end(); ++iter) {
894 if (iter->second == isolate) {
895 callbackIdSet.emplace(iter->first);
896 }
897 }
898 }
899
DestroyModuleCallbackMap(v8::Isolate * isolate)900 void V8DeclarativeGroupJsBridge::DestroyModuleCallbackMap(v8::Isolate* isolate)
901 {
902 std::set<int32_t> callbackIdSet;
903 GetCallbackId(isolate, callbackIdSet);
904
905 std::lock_guard<std::mutex> lock(moduleCallbackMapMutex_);
906 for (auto iter = moduleCallBackFuncs_.begin(); iter != moduleCallBackFuncs_.end();) {
907 if (callbackIdSet.find(iter->first) != callbackIdSet.end()) {
908 iter = moduleCallBackFuncs_.erase(iter);
909 } else {
910 ++iter;
911 }
912 }
913 }
914
DestroyCallbackIdIsolateMap(v8::Isolate * isolate)915 void V8DeclarativeGroupJsBridge::DestroyCallbackIdIsolateMap(v8::Isolate* isolate)
916 {
917 std::lock_guard<std::mutex> lock(callbackIdIsolateMapMutex_);
918 for (auto iter = callbackIdIsolateMap_.begin(); iter != callbackIdIsolateMap_.end();) {
919 if (iter->second == isolate) {
920 iter = callbackIdIsolateMap_.erase(iter);
921 } else {
922 ++iter;
923 }
924 }
925 }
926
DestroyIsolateNativeEngineMap(v8::Isolate * isolate)927 void V8DeclarativeGroupJsBridge::DestroyIsolateNativeEngineMap(v8::Isolate* isolate)
928 {
929 std::lock_guard<std::mutex> lock(isolateNativeEngineMapMutex_);
930 auto result = isolateNativeEngineMap_.find(isolate);
931 if (result != isolateNativeEngineMap_.end()) {
932 isolateNativeEngineMap_.erase(isolate);
933 }
934 }
935
ForwardToWorker(int32_t callbackId)936 bool V8DeclarativeGroupJsBridge::ForwardToWorker(int32_t callbackId)
937 {
938 v8::Isolate* isolate = isolate_;
939 callbackIdIsolateMapMutex_.lock();
940 auto result = callbackIdIsolateMap_.find(callbackId);
941 callbackIdIsolateMapMutex_.unlock();
942 if (result != callbackIdIsolateMap_.end()) {
943 isolate = result->second;
944 isolateNativeEngineMapMutex_.lock();
945 auto nativeEngineResult = isolateNativeEngineMap_.find(isolate);
946 isolateNativeEngineMapMutex_.unlock();
947 if (nativeEngineResult != isolateNativeEngineMap_.end()) {
948 return true;
949 }
950 }
951 return false;
952 }
953
954 } // namespace OHOS::Ace::Framework
955