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/js_frontend/engine/v8/v8_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/js_frontend/engine/common/js_constants.h"
26 #include "frameworks/bridge/js_frontend/engine/v8/v8_engine.h"
27 #include "frameworks/bridge/js_frontend/engine/v8/v8_utils.h"
28
29 namespace OHOS::Ace::Framework {
30 namespace {
31
32 const int32_t PLUGIN_REQUEST_MIN_ARGC_NUM = 4;
33 const int32_t PLUGIN_REQUEST_ARG_RESOLVE_INDEX = 0;
34 const int32_t PLUGIN_REQUEST_ARG_REJECT_INDEX = 1;
35 const int32_t PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX = 2;
36 const int32_t PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX = 3;
37 const int32_t PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX = 4;
38
39 const int32_t PLUGIN_REQUEST_MIN_ARGC_NUM_SYNC = 2;
40 const int32_t PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX_SYNC = 0;
41 const int32_t PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX_SYNC = 1;
42 const int32_t PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX_SYNC = 2;
43
44 } // namespace
45
V8GroupJsBridge(int32_t instanceId)46 V8GroupJsBridge::V8GroupJsBridge(int32_t instanceId) : instanceId_(instanceId) {}
47
InitializeGroupJsBridge(v8::Local<v8::Context> context)48 int32_t V8GroupJsBridge::InitializeGroupJsBridge(v8::Local<v8::Context> context)
49 {
50 LOGI("Enter InitializeGroupJsBridge");
51 if (context.IsEmpty()) {
52 LOGE("group module init, context is null");
53 EventReport::SendAPIChannelException(APIChannelExcepType::JS_BRIDGE_INIT_ERR);
54 return JS_CALL_FAIL;
55 }
56
57 v8::Isolate* isolate = context->GetIsolate();
58 ACE_DCHECK(isolate);
59 v8::HandleScope handleScope(isolate);
60
61 isolate_ = isolate;
62 context_.Reset(isolate_, context);
63
64 if (LoadJsBridgeFunction() != JS_CALL_SUCCESS) {
65 LOGE("group module init, load bridge function failed!");
66 EventReport::SendAPIChannelException(APIChannelExcepType::JS_BRIDGE_INIT_ERR);
67 return JS_CALL_FAIL;
68 }
69
70 eventCallBackFuncs_.clear();
71 moduleCallBackFuncs_.clear();
72 pendingCallbackId_ = 1;
73 return JS_CALL_SUCCESS;
74 }
75
LoadJsBridgeFunction()76 int32_t V8GroupJsBridge::LoadJsBridgeFunction()
77 {
78 v8::HandleScope handleScope(isolate_);
79 v8::Local<v8::Context> context = context_.Get(isolate_);
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 context_.Reset(isolate_, context);
107
108 return JS_CALL_SUCCESS;
109 }
110
CallEvalBuf(v8::Isolate * isolate,v8::Local<v8::String> src)111 int32_t V8GroupJsBridge::CallEvalBuf(v8::Isolate* isolate, v8::Local<v8::String> src)
112 {
113 if (isolate == nullptr) {
114 LOGE("isolate can't be null!");
115 return JS_CALL_FAIL;
116 }
117
118 v8::HandleScope handleScope(isolate);
119 v8::TryCatch tryCatch(isolate);
120 v8::Local<v8::Context> context = isolate->GetCurrentContext();
121
122 v8::Local<v8::Script> script;
123 if (!v8::Script::Compile(context, src).ToLocal(&script)) {
124 LOGE("compile js code failed!");
125 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::COMPILE_ERROR, instanceId_);
126 return JS_CALL_FAIL;
127 }
128
129 v8::Local<v8::Value> res;
130 if (!script->Run(context).ToLocal(&res)) {
131 LOGE("run js code failed!");
132 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::RUNTIME_ERROR, instanceId_);
133 return JS_CALL_FAIL;
134 }
135 return JS_CALL_SUCCESS;
136 }
137
138 // function callback for groupObj's function: sendGroupMessage
ProcessJsRequest(const v8::FunctionCallbackInfo<v8::Value> & args)139 void V8GroupJsBridge::ProcessJsRequest(const v8::FunctionCallbackInfo<v8::Value>& args)
140 {
141 v8::Isolate* isolate = args.GetIsolate();
142 ACE_DCHECK(isolate);
143 v8::HandleScope handleScope(isolate);
144
145 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
146 if (delegate == nullptr) {
147 LOGE("send message para check, fail to get front-end delegate");
148 return;
149 }
150
151 auto groupJsBridge = AceType::DynamicCast<V8GroupJsBridge>((*delegate)->GetGroupJsBridge());
152 if (groupJsBridge == nullptr) {
153 LOGE("send message para check, fail to get group-js-bridge");
154 return;
155 }
156
157 // Should have at least 4 parameters
158 if (args.Length() < PLUGIN_REQUEST_MIN_ARGC_NUM) {
159 LOGE("send message para check, invalid args number:%{public}d", args.Length());
160 return;
161 }
162
163 int32_t callbackId = groupJsBridge->GetPendingCallbackIdAndIncrement();
164 if (!groupJsBridge->SetModuleGroupCallbackFuncs(args, PLUGIN_REQUEST_ARG_RESOLVE_INDEX,
165 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(callbackId, PLUGIN_REQUEST_FAIL,
174 "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 = groupJsBridge->ParseJsPara(args, PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX,
188 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(callbackId, PLUGIN_REQUEST_FAIL,
199 "encode request message failed");
200 return;
201 }
202
203 // CallPlatformFunction
204 auto dispatcher = static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8EngineInstance::DISPATCHER));
205 auto dispatcherUpgrade = (*dispatcher).Upgrade();
206 if (dispatcherUpgrade != nullptr) {
207 dispatcherUpgrade->Dispatch(strGroupName, std::move(encodeBuf), callbackId);
208 } else {
209 LOGW("Dispatcher Upgrade fail when dispatch request message to platform");
210 groupJsBridge->TriggerModulePluginGetErrorCallback(callbackId, PLUGIN_REQUEST_FAIL, "send message failed");
211 }
212 }
213
214 // function callback for groupObj's function: sendGroupMessageSync
ProcessJsRequestSync(const v8::FunctionCallbackInfo<v8::Value> & args)215 void V8GroupJsBridge::ProcessJsRequestSync(const v8::FunctionCallbackInfo<v8::Value>& args)
216 {
217 v8::Isolate* isolate = args.GetIsolate();
218 ACE_DCHECK(isolate);
219 v8::HandleScope handleScope(isolate);
220
221 auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
222 if (delegate == nullptr) {
223 LOGE("send message para check, fail to get front-end delegate");
224 return;
225 }
226
227 auto groupJsBridge = AceType::DynamicCast<V8GroupJsBridge>((*delegate)->GetGroupJsBridge());
228 if (groupJsBridge == nullptr) {
229 LOGE("send message para check, fail to get group-js-bridge");
230 return;
231 }
232
233 // Should have at least 2 parameters
234 if (args.Length() < PLUGIN_REQUEST_MIN_ARGC_NUM_SYNC) {
235 LOGE("send message para check, invalid args number:%{public}d", args.Length());
236 return;
237 }
238
239 v8::String::Utf8Value jsGroupName(isolate, args[PLUGIN_REQUEST_ARG_GROUP_NAME_INDEX_SYNC]);
240 const char* groupName = *jsGroupName;
241 if (groupName == nullptr) {
242 LOGE("plugin name is null");
243 return;
244 }
245 std::string strGroupName(groupName);
246
247 v8::String::Utf8Value jsFunctionName(isolate, args[PLUGIN_REQUEST_ARG_FUNCTION_NAME_INDEX_SYNC]);
248 const char* functionName = *jsFunctionName;
249 std::string strFunctionName(functionName);
250 LOGI("send message, groupName: %{private}s, functionName: %{private}s", groupName, functionName);
251
252 std::vector<CodecData> arguments;
253 ParseJsDataResult parseJsResult =
254 groupJsBridge->ParseJsPara(args, PLUGIN_REQUEST_ARG_APP_PARAMS_INDEX_SYNC, 0, arguments);
255 if (parseJsResult != ParseJsDataResult::PARSE_JS_SUCCESS) {
256 LOGE("parse js data error");
257 return;
258 }
259
260 FunctionCall functionCall(strFunctionName, arguments);
261 StandardFunctionCodec codec;
262 std::vector<uint8_t> encodeBuf;
263 if (!codec.EncodeFunctionCall(functionCall, encodeBuf)) {
264 LOGE("encode arguments fail");
265 return;
266 }
267
268 // CallPlatformFunction
269 auto dispatcher = static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8EngineInstance::DISPATCHER));
270 auto dispatcherUpgrade = (*dispatcher).Upgrade();
271
272 uint8_t* resData = nullptr;
273 int64_t position = 0;
274
275 if (dispatcherUpgrade != nullptr) {
276 dispatcherUpgrade->DispatchSync(strGroupName, std::move(encodeBuf), &resData, position);
277 } else {
278 LOGW("Dispatcher Upgrade fail when dispatch request message to platform");
279 return;
280 }
281
282 std::vector<uint8_t> messageData = std::vector<uint8_t>(resData, resData + position);
283
284 v8::Local<v8::Value> callBackResult;
285 CodecData codecResult;
286 if (codec.DecodePlatformMessage(messageData, codecResult)) {
287 std::string resultString = codecResult.GetStringValue();
288 LOGI("sync resultString = %{private}s", resultString.c_str());
289 if (resultString.empty()) {
290 callBackResult = v8::Null(isolate);
291 } else {
292 callBackResult = v8::String::NewFromUtf8(isolate, resultString.c_str()).ToLocalChecked();
293 }
294 args.GetReturnValue().Set(callBackResult);
295 }
296 }
297
SetEventGroupCallBackFuncs(v8::Isolate * isolate,v8::Local<v8::Value> localEventCallbackFunc,int32_t callbackId,int32_t requestId)298 bool V8GroupJsBridge::SetEventGroupCallBackFuncs(
299 v8::Isolate* isolate, v8::Local<v8::Value> localEventCallbackFunc, int32_t callbackId, int32_t requestId)
300 {
301 if (localEventCallbackFunc->IsNull() || !localEventCallbackFunc->IsFunction()) {
302 LOGE("callback function is invalid!");
303 return false;
304 }
305
306 LOGI("record event callback, requestId:%{private}d, callbackId:%{private}d", requestId, callbackId);
307 v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> eventCallbackFunc;
308 eventCallbackFunc.Reset(isolate, localEventCallbackFunc);
309 auto result = eventCallBackFuncs_.try_emplace(callbackId, eventCallbackFunc);
310 if (!result.second) {
311 result.first->second.Reset();
312 result.first->second.Reset(isolate, localEventCallbackFunc);
313 }
314
315 AddRequestIdCallbackIdRelation(callbackId, requestId);
316
317 return true;
318 }
319
RemoveEventGroupCallBackFuncs(int32_t callbackId)320 void V8GroupJsBridge::RemoveEventGroupCallBackFuncs(int32_t callbackId)
321 {
322 LOGI("remove event callback, callbackId:%{private}d", callbackId);
323 auto itFunc = eventCallBackFuncs_.find(callbackId);
324 if (itFunc != eventCallBackFuncs_.end()) {
325 itFunc->second.Reset();
326 eventCallBackFuncs_.erase(callbackId);
327 }
328 }
329
AddRequestIdCallbackIdRelation(int32_t eventId,int32_t requestId)330 void V8GroupJsBridge::AddRequestIdCallbackIdRelation(int32_t eventId, int32_t requestId)
331 {
332 auto result = requestIdCallbackIdMap_.try_emplace(requestId, eventId);
333 if (!result.second) {
334 result.first->second = eventId;
335 }
336 }
337
RemoveRequestIdCallbackIdRelation(int32_t requestId,bool removeEventCallback)338 void V8GroupJsBridge::RemoveRequestIdCallbackIdRelation(int32_t requestId, bool removeEventCallback)
339 {
340 auto eventId = requestIdCallbackIdMap_.find(requestId);
341 if (eventId != requestIdCallbackIdMap_.end()) {
342 if (removeEventCallback) {
343 RemoveEventGroupCallBackFuncs(eventId->second);
344 }
345 requestIdCallbackIdMap_.erase(requestId);
346 }
347 }
348
ProcessParseJsError(ParseJsDataResult errorType,v8::Isolate * isolate,int32_t callbackId)349 void V8GroupJsBridge::ProcessParseJsError(ParseJsDataResult errorType, v8::Isolate* isolate, int32_t callbackId)
350 {
351 // PluginErrorCallback
352 auto dispatcher = static_cast<WeakPtr<JsMessageDispatcher>*>(isolate->GetData(V8EngineInstance::DISPATCHER));
353 auto dispatcherUpgrade = (*dispatcher).Upgrade();
354 if (dispatcherUpgrade == nullptr) {
355 LOGW("Dispatcher Upgrade fail at ProcessParseJsError");
356 return;
357 }
358 std::string errMessage;
359 switch (errorType) {
360 case ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE:
361 errMessage = "unsupported js parameter types";
362 dispatcherUpgrade->DispatchPluginError(callbackId,
363 static_cast<int32_t>(ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE), std::move(errMessage));
364 break;
365 case ParseJsDataResult::PARSE_JS_ERR_TOO_MANY_PARAM:
366 errMessage = "the number of parameters exceeds 255";
367 dispatcherUpgrade->DispatchPluginError(callbackId,
368 static_cast<int32_t>(ParseJsDataResult::PARSE_JS_ERR_TOO_MANY_PARAM), std::move(errMessage));
369 break;
370 default:
371 break;
372 }
373 }
374
SetModuleGroupCallbackFuncs(const v8::FunctionCallbackInfo<v8::Value> & args,int32_t resolveCallbackIndex,int32_t rejectCallbackIndex,int32_t callbackId)375 bool V8GroupJsBridge::SetModuleGroupCallbackFuncs(const v8::FunctionCallbackInfo<v8::Value>& args,
376 int32_t resolveCallbackIndex, int32_t rejectCallbackIndex, int32_t callbackId)
377 {
378 v8::Isolate* isolate = args.GetIsolate();
379 ACE_DCHECK(isolate);
380 v8::HandleScope handleScope(isolate);
381
382 LOGD("Enter SetModuleGroupCallbackFuncs");
383 if (args[resolveCallbackIndex]->IsNull() || !args[resolveCallbackIndex]->IsFunction() ||
384 args[rejectCallbackIndex]->IsNull() || !args[rejectCallbackIndex]->IsFunction()) {
385 LOGE("resolve or reject callback function is invalid");
386 return false;
387 }
388
389 PromiseCallback promiseCallJsFunc;
390
391 promiseCallJsFunc.resolveCallback.Reset(isolate, args[resolveCallbackIndex]);
392 promiseCallJsFunc.rejectCallback.Reset(isolate, args[rejectCallbackIndex]);
393
394 auto result = moduleCallBackFuncs_.try_emplace(callbackId, promiseCallJsFunc);
395 if (!result.second) {
396 LOGE("module callback function has been existed!");
397 return false;
398 }
399 return true;
400 }
401
SerializationObjectToString(v8::Local<v8::Context> context,v8::Local<v8::Value> val)402 std::string V8GroupJsBridge::SerializationObjectToString(v8::Local<v8::Context> context, v8::Local<v8::Value> val)
403 {
404 v8::Isolate* isolate = context->GetIsolate();
405 ACE_DCHECK(isolate);
406 v8::HandleScope handleScope(isolate);
407 v8::TryCatch tryCatch(isolate);
408
409 v8::Local<v8::Value> strValue;
410 v8::Local<v8::Value> global;
411 v8::Local<v8::Value> json;
412 v8::Local<v8::Value> func;
413
414 global = context->Global();
415 if (!global->IsObject()) {
416 LOGE("SerializationObjectToString error: fail to get Global Object");
417 return "";
418 }
419 v8::Local<v8::Object> globalObj = global->ToObject(context).ToLocalChecked();
420 json = globalObj->Get(context, v8::String::NewFromUtf8(isolate, "JSON").ToLocalChecked()).ToLocalChecked();
421 if (!json->IsObject()) {
422 LOGE("SerializationObjectToString error: global has no attribute JSON");
423 return "";
424 }
425 v8::Local<v8::Object> jsonObj = json->ToObject(context).ToLocalChecked();
426 func = jsonObj->Get(context, v8::String::NewFromUtf8(isolate, "stringify").ToLocalChecked()).ToLocalChecked();
427 if (!func->IsFunction()) {
428 LOGE("SerializationObjectToString error: JSON has no attribute stringify");
429 return "";
430 }
431 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(func);
432 v8::Local<v8::Value> argv[] = { val };
433 bool succ = function->Call(context, globalObj, 1, argv).ToLocal(&strValue);
434 if (!succ) {
435 V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::STRINGIFY_ERROR);
436 return "";
437 }
438
439 v8::String::Utf8Value resStr(isolate, strValue);
440 std::string res;
441 if (*resStr) {
442 res = *resStr;
443 }
444 return res;
445 }
446
ParseJsPara(const v8::FunctionCallbackInfo<v8::Value> & args,int32_t beginIndex,int32_t requestId,std::vector<CodecData> & arguments)447 ParseJsDataResult V8GroupJsBridge::ParseJsPara(const v8::FunctionCallbackInfo<v8::Value>& args, int32_t beginIndex,
448 int32_t requestId, std::vector<CodecData>& arguments)
449 {
450 if (args.Length() < beginIndex) { // no others params
451 return ParseJsDataResult::PARSE_JS_SUCCESS;
452 }
453
454 v8::Isolate* isolate = args.GetIsolate();
455 ACE_DCHECK(isolate);
456 v8::HandleScope handleScope(isolate);
457 auto context = isolate->GetCurrentContext();
458
459 for (int32_t i = beginIndex; i < args.Length(); i++) {
460 v8::Local<v8::Value> val = args[i];
461 if (val->IsString()) {
462 v8::String::Utf8Value jsPara(isolate, val);
463 if (*jsPara) {
464 std::string para(*jsPara);
465 CodecData arg(para);
466 arguments.push_back(arg);
467 }
468 } else if (val->IsNumber()) {
469 if (val->IsInt32()) {
470 int32_t valInt = val->Int32Value(context).ToChecked();
471 CodecData arg(valInt);
472 arguments.push_back(arg);
473 } else {
474 double valDouble = val->NumberValue(context).ToChecked();
475 CodecData arg(valDouble);
476 arguments.push_back(arg);
477 }
478 } else if (val->IsBoolean()) {
479 bool valBool = val->BooleanValue(isolate);
480 CodecData arg(valBool);
481 arguments.push_back(arg);
482 } else if (val->IsNull()) {
483 CodecData argNull;
484 arguments.push_back(argNull);
485 } else if (val->IsFunction()) {
486 int32_t functionId = GetPendingCallbackIdAndIncrement();
487 CodecData arg(functionId, BufferDataType::TYPE_FUNCTION);
488 arguments.push_back(arg);
489 SetEventGroupCallBackFuncs(isolate, val, functionId, requestId);
490 } else if (val->IsArray() || val->IsObject()) {
491 std::string objStr = SerializationObjectToString(context, val);
492 CodecData arg(objStr, BufferDataType::TYPE_OBJECT);
493 arguments.push_back(arg);
494 } else if (val->IsUndefined()) {
495 LOGD("Process callNative para type:undefined");
496 } else {
497 LOGE("Process callNative para type: unsupported type");
498 return ParseJsDataResult::PARSE_JS_ERR_UNSUPPORTED_TYPE;
499 }
500 }
501 return ParseJsDataResult::PARSE_JS_SUCCESS;
502 }
503
TriggerModuleJsCallback(int32_t callbackId,int32_t code,std::vector<uint8_t> && messageData)504 void V8GroupJsBridge::TriggerModuleJsCallback(int32_t callbackId, int32_t code, std::vector<uint8_t>&& messageData)
505 {
506 v8::HandleScope handleScope(isolate_);
507 v8::TryCatch tryCatch(isolate_);
508
509 v8::Local<v8::Value> callBackResult;
510 CodecData codecResult;
511 StandardFunctionCodec codec;
512 if (codec.DecodePlatformMessage(messageData, codecResult)) {
513 std::string resultString = codecResult.GetStringValue();
514 if (resultString.empty()) {
515 callBackResult = v8::Null(isolate_);
516 } else {
517 callBackResult = v8::String::NewFromUtf8(isolate_, resultString.c_str()).ToLocalChecked();
518 }
519 } else {
520 LOGE("trigger JS result function error, decode message fail, callbackId:%{private}d",
521 callbackId);
522 code = PLUGIN_REQUEST_FAIL;
523 std::string errorString = std::string("{\"code\":").append(std::to_string(code)).append(",")
524 .append("\"data\":\"invalid response data\"}");
525 callBackResult = v8::String::NewFromUtf8(isolate_, errorString.c_str()).ToLocalChecked();
526 }
527 CallModuleJsCallback(callbackId, code, callBackResult);
528
529 messageData.clear();
530 }
531
CallModuleJsCallback(int32_t callbackId,int32_t code,v8::Local<v8::Value> callBackResult)532 void V8GroupJsBridge::CallModuleJsCallback(int32_t callbackId, int32_t code, v8::Local<v8::Value> callBackResult)
533 {
534 RemoveRequestIdCallbackIdRelation(callbackId, code != PLUGIN_REQUEST_SUCCESS);
535
536 v8::HandleScope handleScope(isolate_);
537 v8::Local<v8::Context> context = context_.Get(isolate_);
538 v8::Local<v8::Object> global = context->Global();
539
540 v8::TryCatch tryCatch(isolate_);
541
542 auto itFunc = moduleCallBackFuncs_.find(callbackId);
543 if (itFunc != moduleCallBackFuncs_.end()) {
544 v8::Local<v8::Value> jsFunc = (code == PLUGIN_REQUEST_SUCCESS ?
545 itFunc->second.resolveCallback.Get(isolate_) : itFunc->second.rejectCallback.Get(isolate_));
546 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
547 LOGE("trigger JS result function error, it is not a function, callbackId:%{private}d", callbackId);
548 return;
549 }
550
551 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
552 v8::Local<v8::Value> argv[] = { callBackResult };
553 // Pass only 1 parameter, call promise resolve call back.
554 v8::Local<v8::Value> res;
555 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
556 if (succ) {
557 LOGI("trigger JS result function success, callbackId:%{private}d, code:%{private}d", callbackId, code);
558 } else {
559 LOGW("trigger JS result function fail, callbackId:%{private}d, code:%{private}d", callbackId, code);
560 V8Utils::JsStdDumpErrorAce(isolate_, &tryCatch);
561 }
562
563 while (v8::platform::PumpMessageLoop(V8Engine::GetPlatform().get(), isolate_)) {
564 continue;
565 }
566
567 itFunc->second.rejectCallback.Reset();
568 itFunc->second.resolveCallback.Reset();
569 moduleCallBackFuncs_.erase(itFunc);
570 } else {
571 LOGE("trigger JS result function is not exists, callbackId:%{private}d, code:%{private}d", callbackId, code);
572 }
573 }
574
TriggerModulePluginGetErrorCallback(int32_t callbackId,int32_t errorCode,std::string && errorMessage)575 void V8GroupJsBridge::TriggerModulePluginGetErrorCallback(
576 int32_t callbackId, int32_t errorCode, std::string&& errorMessage)
577 {
578 RemoveRequestIdCallbackIdRelation(callbackId, true);
579
580 v8::HandleScope handleScope(isolate_);
581 v8::Local<v8::Context> context = context_.Get(isolate_);
582 v8::Local<v8::Object> global = context->Global();
583
584 auto itFunc = moduleCallBackFuncs_.find(callbackId);
585 if (itFunc != moduleCallBackFuncs_.end()) {
586 v8::Local<v8::Value> jsFunc = itFunc->second.rejectCallback.Get(isolate_);
587 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
588 LOGE("trigger JS result function error, reject is not a function, callbackId:%{private}d",
589 callbackId);
590 return;
591 }
592
593 auto resultJson = JsonUtil::Create(true);
594 resultJson->Put(std::string("code").c_str(), errorCode);
595 resultJson->Put(std::string("data").c_str(), errorMessage.c_str());
596
597 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
598 v8::Local<v8::String> emptyReplyCallback = v8::String::NewFromUtf8(isolate_,
599 resultJson->ToString().c_str()).ToLocalChecked();
600 v8::Local<v8::Value> argv[] = {emptyReplyCallback};
601 // Pass only 1 parameter, call promise reject call back for error get in plugin.
602 v8::Local<v8::Value> res;
603 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
604 if (succ) {
605 LOGI("trigger JS result function success, callbackId:%{private}d", callbackId);
606 } else {
607 LOGW("trigger JS result function fail, callbackId:%{private}d", callbackId);
608 }
609
610 while (v8::platform::PumpMessageLoop(V8Engine::GetPlatform().get(), isolate_)) {
611 continue;
612 }
613
614 itFunc->second.rejectCallback.Reset();
615 itFunc->second.resolveCallback.Reset();
616 moduleCallBackFuncs_.erase(itFunc);
617 } else {
618 LOGE("trigger JS result function is not exists, callbackId:%{private}d", callbackId);
619 }
620 }
621
CallEventJsCallback(int32_t callbackId,std::vector<uint8_t> && eventData)622 void V8GroupJsBridge::CallEventJsCallback(int32_t callbackId, std::vector<uint8_t>&& eventData)
623 {
624 v8::HandleScope handleScope(isolate_);
625 v8::Local<v8::Context> context = context_.Get(isolate_);
626 v8::Local<v8::Object> global = context->Global();
627
628 v8::Local<v8::Value> callBackEvent;
629 CodecData codecEvent;
630 StandardFunctionCodec codec;
631 if (codec.DecodePlatformMessage(eventData, codecEvent)) {
632 std::string eventString = codecEvent.GetStringValue();
633 if (eventString.empty()) {
634 callBackEvent = v8::Null(isolate_);
635 } else {
636 callBackEvent = v8::String::NewFromUtf8(isolate_, eventString.c_str()).ToLocalChecked();
637 }
638 } else {
639 LOGE("trigger JS callback function error, decode message fail, callbackId:%{private}d", callbackId);
640 return;
641 }
642
643 auto itFunc = eventCallBackFuncs_.find(callbackId);
644 if (itFunc != eventCallBackFuncs_.end()) {
645 v8::Local<v8::Value> jsFunc = itFunc->second.Get(isolate_);
646 if (!jsFunc->IsFunction() || jsFunc->IsNull()) {
647 LOGE("trigger JS callback function error, callback is not a function, callbackId:%{private}d", callbackId);
648 return;
649 }
650
651 // Pass only 1 parameter
652 v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(jsFunc);
653 v8::Local<v8::Value> argv[] = { callBackEvent };
654 v8::Local<v8::Value> res;
655 bool succ = func->Call(context, global, 1, argv).ToLocal(&res);
656 if (!succ) {
657 LOGW("trigger JS callback function failed, callbackId:%{private}d", callbackId);
658 } else {
659 LOGW("trigger JS callback function success, callbackId:%{private}d", callbackId);
660 }
661
662 while (v8::platform::PumpMessageLoop(V8Engine::GetPlatform().get(), isolate_)) {
663 continue;
664 }
665 } else {
666 LOGE("trigger JS callback function error, it is not exists, callbackId:%{private}d", callbackId);
667 }
668 eventData.clear();
669 }
670
TriggerEventJsCallback(int32_t callbackId,int32_t code,std::vector<uint8_t> && eventData)671 void V8GroupJsBridge::TriggerEventJsCallback(int32_t callbackId, int32_t code, std::vector<uint8_t>&& eventData)
672 {
673 if (code == PLUGIN_CALLBACK_DESTROY) {
674 RemoveEventGroupCallBackFuncs(callbackId);
675 } else {
676 CallEventJsCallback(callbackId, std::move(eventData));
677 }
678 }
679
LoadPluginJsCode(std::string && jsCode)680 void V8GroupJsBridge::LoadPluginJsCode(std::string&& jsCode)
681 {
682 LOGI("Load plugin js code, code len:%{public}d", static_cast<int32_t>(jsCode.length()));
683 jsCode_ = std::move(jsCode);
684 v8::HandleScope handleScope(isolate_);
685 v8::Local<v8::Context> context = context_.Get(isolate_);
686 v8::Context::Scope contextScope(context);
687
688 v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate_, jsCode_.c_str()).ToLocalChecked();
689 if (CallEvalBuf(isolate_, source) != JS_CALL_SUCCESS) {
690 LOGE("run plugin js code failed!");
691 }
692 }
693
LoadPluginJsByteCode(std::vector<uint8_t> && jsCode,std::vector<int32_t> && jsCodeLen)694 void V8GroupJsBridge::LoadPluginJsByteCode(std::vector<uint8_t>&& jsCode, std::vector<int32_t>&& jsCodeLen)
695 {
696 LOGW("V8 do not support load js bytecode now.");
697 }
698
Destroy()699 void V8GroupJsBridge::Destroy()
700 {
701 eventCallBackFuncs_.clear();
702 moduleCallBackFuncs_.clear();
703 requestIdCallbackIdMap_.clear();
704 context_.Reset();
705 }
706
707 } // namespace OHOS::Ace::Framework
708