• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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