• 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 <future>
17 #include <napi/native_api.h>
18 #include <napi/native_node_api.h>
19 #include <queue>
20 #include <set>
21 #include <string>
22 #include <unistd.h>
23 #include "json.hpp"
24 #include "pasteboard_client.h"
25 #include "common_utilities_hpp.h"
26 #include "frontend_api_defines.h"
27 #include "ipc_transactor.h"
28 #include "ui_event_observer_napi.h"
29 
30 namespace OHOS::uitest {
31     using namespace nlohmann;
32     using namespace std;
33 
34     static constexpr size_t NAPI_MAX_BUF_LEN = 1024;
35     static constexpr size_t NAPI_MAX_ARG_COUNT = 8;
36     static constexpr size_t BACKEND_OBJ_GC_BATCH = 100;
37     // type of unexpected or napi-internal error
38     static constexpr napi_status NAPI_ERR = napi_status::napi_generic_failure;
39     // the name of property that represents the objectRef of the backend object
40     static constexpr char PROP_BACKEND_OBJ_REF[] = "backendObjRef";
41     /**For dfx usage, records the uncalled js apis. */
42     static set<string> g_unCalledJsFuncNames;
43     /**For gc usage, records the backend objRefs about to delete. */
44     static queue<string> g_backendObjsAboutToDelete;
45     static mutex g_gcQueueMutex;
46     /**IPC client. */
47     static ApiTransactor g_apiTransactClient(false);
48     static future<void> g_establishConnectionFuture;
49 
50     /** Convert js string to cpp string.*/
JsStrToCppStr(napi_env env,napi_value jsStr)51     static string JsStrToCppStr(napi_env env, napi_value jsStr)
52     {
53         if (jsStr == nullptr) {
54             return "";
55         }
56         napi_valuetype type;
57         NAPI_CALL_BASE(env, napi_typeof(env, jsStr, &type), "");
58         if (type == napi_undefined || type == napi_null) {
59             return "";
60         }
61         size_t bufSize = 0;
62         char buf[NAPI_MAX_BUF_LEN] = {0};
63         NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, jsStr, buf, NAPI_MAX_BUF_LEN, &bufSize), "");
64         return string(buf, bufSize);
65     }
66 
67     /**Lifecycle function, establish connection async, called externally.*/
ScheduleEstablishConnection(napi_env env,napi_callback_info info)68     static napi_value ScheduleEstablishConnection(napi_env env, napi_callback_info info)
69     {
70         size_t argc = 1;
71         napi_value value = nullptr;
72         napi_value argv[1] = {0};
73         NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &value, nullptr));
74         NAPI_ASSERT(env, argc > 0, "Need session token argument!");
75         auto token = JsStrToCppStr(env, argv[0]);
76         g_establishConnectionFuture = async(launch::async, [env, token]() {
77             auto &instance = UiEventObserverNapi::Get();
78             using namespace std::placeholders;
79             auto callbackHandler = std::bind(&UiEventObserverNapi::HandleEventCallback, &instance, env, _1, _2);
80             auto result = g_apiTransactClient.InitAndConnectPeer(token, callbackHandler);
81             LOG_I("End setup transaction connection, result=%{public}d", result);
82         });
83         return nullptr;
84     }
85 
86     /**Wait connection result sync if need.*/
WaitForConnectionIfNeed()87     static void WaitForConnectionIfNeed()
88     {
89         if (g_establishConnectionFuture.valid()) {
90             LOG_I("Begin WaitForConnection");
91             g_establishConnectionFuture.get();
92         }
93     }
94 
95     /**Encapsulates the data objects needed in once api transaction.*/
96     struct TransactionContext {
97         napi_value jsThis_ = nullptr;
98         napi_value *jsArgs_ = nullptr;
99         ApiCallInfo callInfo_;
100     };
101 
CreateJsException(napi_env env,uint32_t code,string_view msg)102     static napi_value CreateJsException(napi_env env, uint32_t code, string_view msg)
103     {
104         napi_value codeValue;
105         napi_value msgValue;
106         napi_value errorValue;
107         napi_create_uint32(env, code, &codeValue);
108         napi_create_string_utf8(env, msg.data(), NAPI_AUTO_LENGTH, &msgValue);
109         napi_create_error(env, nullptr, msgValue, &errorValue);
110         napi_set_named_property(env, errorValue, "code", codeValue);
111         return errorValue;
112     }
113 
114     /**Set object constructor function to global as attribute.*/
MountJsConstructorToGlobal(napi_env env,string_view typeName,napi_value function)115     static napi_status MountJsConstructorToGlobal(napi_env env, string_view typeName, napi_value function)
116     {
117         NAPI_ASSERT_BASE(env, function != nullptr, "Null constructor function", napi_invalid_arg);
118         const string name = "constructor_" + string(typeName);
119         napi_value global = nullptr;
120         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
121         NAPI_CALL_BASE(env, napi_set_named_property(env, global, name.c_str(), function), NAPI_ERR);
122         return napi_ok;
123     }
124 
125     /**Get object constructor function from global as attribute.*/
GetJsConstructorFromGlobal(napi_env env,string_view typeName,napi_value * pFunction)126     static napi_status GetJsConstructorFromGlobal(napi_env env, string_view typeName, napi_value *pFunction)
127     {
128         NAPI_ASSERT_BASE(env, pFunction != nullptr, "Null constructor receiver", napi_invalid_arg);
129         const string name = "constructor_" + string(typeName);
130         napi_value global = nullptr;
131         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
132         NAPI_CALL_BASE(env, napi_get_named_property(env, global, name.c_str(), pFunction), NAPI_ERR);
133         return napi_ok;
134     }
135 
136     /**Conversion between value and string, using the builtin JSON methods.*/
ValueStringConvert(napi_env env,napi_value in,napi_value * out,bool stringify)137     static napi_status ValueStringConvert(napi_env env, napi_value in, napi_value *out, bool stringify)
138     {
139         if (in == nullptr || out == nullptr) {
140             return napi_invalid_arg;
141         }
142         napi_value global = nullptr;
143         napi_value jsonProp = nullptr;
144         napi_value jsonFunc = nullptr;
145         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
146         NAPI_CALL_BASE(env, napi_get_named_property(env, global, "JSON", &jsonProp), NAPI_ERR);
147         if (stringify) {
148             NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "stringify", &jsonFunc), NAPI_ERR);
149         } else {
150             NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "parse", &jsonFunc), NAPI_ERR);
151         }
152         napi_value argv[1] = {in};
153         NAPI_CALL_BASE(env, napi_call_function(env, jsonProp, jsonFunc, 1, argv, out), NAPI_ERR);
154         return napi_ok;
155     }
156 
157     /**Unmarshal object from json, throw error and return false if the object cannot be deserialized.*/
UnmarshalObject(napi_env env,const json & in,napi_value * pOut,napi_value jsThis)158     static napi_status UnmarshalObject(napi_env env, const json &in, napi_value *pOut, napi_value jsThis)
159     {
160         NAPI_ASSERT_BASE(env, pOut != nullptr, "Illegal arguments", napi_invalid_arg);
161         const auto type = in.type();
162         if (type == nlohmann::detail::value_t::null) { // return null
163             NAPI_CALL_BASE(env, napi_get_null(env, pOut), NAPI_ERR);
164             return napi_ok;
165         }
166         if (type != nlohmann::detail::value_t::string) { // non-string value, convert and return object
167             NAPI_CALL_BASE(env, napi_create_string_utf8(env, in.dump().c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR);
168             NAPI_CALL_BASE(env, ValueStringConvert(env, *pOut, pOut, false), NAPI_ERR);
169             return napi_ok;
170         }
171         const auto cppString = in.get<string>();
172         string frontendTypeName;
173         bool bindJsThis = false;
174         for (const auto &classDef : FRONTEND_CLASS_DEFS) {
175             const auto objRefFormat = string(classDef->name_) + "#";
176             if (cppString.find(objRefFormat) == 0) {
177                 frontendTypeName = string(classDef->name_);
178                 bindJsThis = classDef->bindUiDriver_;
179                 break;
180             }
181         }
182         NAPI_CALL_BASE(env, napi_create_string_utf8(env, cppString.c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR);
183         if (frontendTypeName.empty()) { // plain string, return it
184             return napi_ok;
185         }
186         LOG_D("Convert to frontend object: '%{public}s'", frontendTypeName.c_str());
187         // covert to wrapper object and bind the backend objectRef
188         napi_value refValue = *pOut;
189         napi_value constructor = nullptr;
190         NAPI_CALL_BASE(env, GetJsConstructorFromGlobal(env, frontendTypeName, &constructor), NAPI_ERR);
191         NAPI_CALL_BASE(env, napi_new_instance(env, constructor, 1, &refValue, pOut), NAPI_ERR);
192         NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, PROP_BACKEND_OBJ_REF, refValue), NAPI_ERR);
193         if (bindJsThis) { // bind the jsThis object
194             LOG_D("Bind jsThis");
195             NAPI_ASSERT_BASE(env, jsThis != nullptr, "null jsThis", NAPI_ERR);
196             NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, "boundObject", jsThis), NAPI_ERR);
197         }
198         return napi_ok;
199     }
200 
201     /**Evaluate and convert transaction reply to object. Return the exception raised during the
202      * transaction if any, else return the result object. */
UnmarshalReply(napi_env env,const TransactionContext & ctx,const ApiReplyInfo & reply)203     static napi_value UnmarshalReply(napi_env env, const TransactionContext &ctx, const ApiReplyInfo &reply)
204     {
205         if (ctx.callInfo_.fdParamIndex_ >= 0) {
206             auto fd = ctx.callInfo_.paramList_.at(INDEX_ZERO).get<int>();
207             (void) close(fd);
208         }
209         LOG_I("Start to Unmarshal transaction result");
210         const auto &message = reply.exception_.message_;
211         ErrCode code = reply.exception_.code_;
212         if (code == INTERNAL_ERROR || code == ERR_INTERNAL) {
213             LOG_E("ErrorInfo: code='%{public}u', message='%{public}s'", code, message.c_str());
214         } else if (reply.exception_.code_ != NO_ERROR) {
215             LOG_I("ErrorInfo: code='%{public}u', message='%{public}s'", code, message.c_str());
216             return CreateJsException(env, code, message);
217         }
218         LOG_I("Start to Unmarshal return value: %{public}s", reply.resultValue_.dump().c_str());
219         const auto resultType = reply.resultValue_.type();
220         napi_value result = nullptr;
221         if (resultType == nlohmann::detail::value_t::null) { // return null
222             NAPI_CALL(env, napi_get_null(env, &result));
223         } else if (resultType == nlohmann::detail::value_t::array) { // return array
224             NAPI_CALL(env, napi_create_array_with_length(env, reply.resultValue_.size(), &result));
225             for (size_t idx = 0; idx < reply.resultValue_.size(); idx++) {
226                 napi_value item = nullptr;
227                 NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_.at(idx), &item, ctx.jsThis_));
228                 NAPI_CALL(env, napi_set_element(env, result, idx, item));
229             }
230         } else { // return single value
231             NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_, &result, ctx.jsThis_));
232         }
233         return result;
234     }
235 
236     /**Call api with parameters out, wait for and return result value or throw raised exception.*/
TransactSync(napi_env env,TransactionContext & ctx)237     napi_value TransactSync(napi_env env, TransactionContext &ctx)
238     {
239         WaitForConnectionIfNeed();
240         LOG_D("TargetApi=%{public}s", ctx.callInfo_.apiId_.data());
241         auto reply = ApiReplyInfo();
242         g_apiTransactClient.Transact(ctx.callInfo_, reply);
243         auto resultValue = UnmarshalReply(env, ctx, reply);
244         auto isError = false;
245         NAPI_CALL(env, napi_is_error(env, resultValue, &isError));
246         if (isError) {
247             NAPI_CALL(env, napi_throw(env, resultValue));
248             NAPI_CALL(env, napi_get_undefined(env, &resultValue)); // return undefined it's error
249         }
250         // notify backend objects deleting
251         if (g_backendObjsAboutToDelete.size() >= BACKEND_OBJ_GC_BATCH) {
252             auto gcCall = ApiCallInfo {.apiId_ = "BackendObjectsCleaner"};
253             unique_lock<mutex> lock(g_gcQueueMutex);
254             for (size_t count = 0; count < BACKEND_OBJ_GC_BATCH; count++) {
255                 gcCall.paramList_.emplace_back(g_backendObjsAboutToDelete.front());
256                 g_backendObjsAboutToDelete.pop();
257             }
258             lock.unlock();
259             auto gcReply = ApiReplyInfo();
260             g_apiTransactClient.Transact(gcCall, gcReply);
261         }
262         return resultValue;
263     }
264 
265     /**Encapsulates the data objects needed for async transaction.*/
266     struct AsyncTransactionCtx {
267         TransactionContext ctx_;
268         ApiReplyInfo reply_;
269         napi_async_work asyncWork_ = nullptr;
270         napi_deferred deferred_ = nullptr;
271         napi_ref jsThisRef_ = nullptr;
272     };
273 
274     /**Call api with parameters out, return a promise.*/
TransactAsync(napi_env env,TransactionContext & ctx)275     static napi_value TransactAsync(napi_env env, TransactionContext &ctx)
276     {
277         constexpr uint32_t refCount = 1;
278         LOG_D("TargetApi=%{public}s", ctx.callInfo_.apiId_.data());
279         napi_value resName;
280         NAPI_CALL(env, napi_create_string_latin1(env, __FUNCTION__, NAPI_AUTO_LENGTH, &resName));
281         auto aCtx = new AsyncTransactionCtx();
282         aCtx->ctx_ = ctx;
283         napi_value promise;
284         NAPI_CALL(env, napi_create_promise(env, &(aCtx->deferred_), &promise));
285         NAPI_CALL(env, napi_create_reference(env, ctx.jsThis_, refCount, &(aCtx->jsThisRef_)));
286         napi_create_async_work(
287             env, nullptr, resName,
288             [](napi_env env, void *data) {
289                 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data);
290                 // NOT:: use 'auto&' rather than 'auto', or the result will be set to copy-constructed temp-object
291                 auto &ctx = aCtx->ctx_;
292                 g_apiTransactClient.Transact(ctx.callInfo_, aCtx->reply_);
293             },
294             [](napi_env env, napi_status status, void *data) {
295                 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data);
296                 napi_handle_scope scope = nullptr;
297                 napi_open_handle_scope(env, &scope);
298                 if (scope == nullptr) {
299                     return;
300                 }
301                 napi_get_reference_value(env, aCtx->jsThisRef_, &(aCtx->ctx_.jsThis_));
302                 auto resultValue = UnmarshalReply(env, aCtx->ctx_, aCtx->reply_);
303                 napi_delete_reference(env, aCtx->jsThisRef_);
304                 auto isError = false;
305                 napi_is_error(env, resultValue, &isError);
306                 if (isError) {
307                     napi_reject_deferred(env, aCtx->deferred_, resultValue);
308                 } else {
309                     napi_resolve_deferred(env, aCtx->deferred_, resultValue);
310                 }
311                 napi_delete_async_work(env, aCtx->asyncWork_);
312                 napi_close_handle_scope(env, scope);
313                 delete aCtx;
314             },
315             (void *)aCtx, &(aCtx->asyncWork_));
316         napi_queue_async_work(env, aCtx->asyncWork_);
317         return promise;
318     }
319 
GetBackendObjRefProp(napi_env env,napi_value value,napi_value * pOut)320     static napi_status GetBackendObjRefProp(napi_env env, napi_value value, napi_value* pOut)
321     {
322         napi_valuetype type = napi_undefined;
323         NAPI_CALL_BASE(env, napi_typeof(env, value, &type), NAPI_ERR);
324         if (type != napi_object) {
325             *pOut = nullptr;
326             return napi_ok;
327         }
328         bool hasRef = false;
329         NAPI_CALL_BASE(env, napi_has_named_property(env, value, PROP_BACKEND_OBJ_REF, &hasRef), NAPI_ERR);
330         if (!hasRef) {
331             *pOut = nullptr;
332         } else {
333             NAPI_CALL_BASE(env, napi_get_named_property(env, value, PROP_BACKEND_OBJ_REF, pOut), NAPI_ERR);
334         }
335         return napi_ok;
336     }
337 
SetPasteBoardData(string_view text)338     static void SetPasteBoardData(string_view text)
339     {
340         auto pasteBoardMgr = MiscServices::PasteboardClient::GetInstance();
341         pasteBoardMgr->Clear();
342         auto pasteData = MiscServices::PasteboardClient::GetInstance()->CreatePlainTextData(string(text));
343         pasteBoardMgr->SetPasteData(*pasteData);
344     }
345 
PreprocessTransaction(napi_env env,TransactionContext & ctx,napi_value & error)346     static void PreprocessTransaction(napi_env env, TransactionContext &ctx, napi_value &error)
347     {
348         auto &paramList = ctx.callInfo_.paramList_;
349         const auto &id = ctx.callInfo_.apiId_;
350         if (id  == "Component.inputText" && paramList.size() > 0) {
351             auto text = paramList.at(INDEX_ZERO).get<string>();
352             SetPasteBoardData(text);
353         } else if (id  == "Driver.inputText" && paramList.size() > 0) {
354             auto text = paramList.at(INDEX_ONE).get<string>();
355             SetPasteBoardData(text);
356         } else if (id  == "Driver.screenCap" || id  == "UiDriver.screenCap" || id  == "Driver.screenCapture") {
357             if (paramList.size() < 1 || paramList.at(0).type() != nlohmann::detail::value_t::string) {
358                 LOG_E("Missing file path argument");
359                 error = CreateJsException(env, ERR_INVALID_INPUT, "Missing file path argument");
360                 return;
361             }
362             auto path = paramList.at(INDEX_ZERO).get<string>();
363             auto fd = open(path.c_str(), O_RDWR | O_CREAT, 0666);
364             if (fd == -1) {
365                 LOG_E("Invalid file path: %{public}s", path.data());
366                 error = CreateJsException(env, ERR_INVALID_INPUT, "Invalid file path:" + path);
367                 return;
368             }
369             paramList[INDEX_ZERO] = fd;
370             ctx.callInfo_.fdParamIndex_ = INDEX_ZERO;
371         } else if (id  == "UIEventObserver.once") {
372             auto err = ApiCallErr(NO_ERROR);
373             UiEventObserverNapi::Get().PreprocessCallOnce(env, ctx.callInfo_, ctx.jsThis_, ctx.jsArgs_, err);
374             if (err.code_ != NO_ERROR) {
375                 error = CreateJsException(env, err.code_, err.message_);
376             }
377         }
378     }
379 
380     /**Generic js-api callback.*/
GenericCallback(napi_env env,napi_callback_info info)381     static napi_value GenericCallback(napi_env env, napi_callback_info info)
382     {
383         // extract callback data
384         TransactionContext ctx;
385         napi_value argv[NAPI_MAX_ARG_COUNT] = {nullptr};
386         auto count = NAPI_MAX_ARG_COUNT;
387         void *pData = nullptr;
388         NAPI_CALL(env, napi_get_cb_info(env, info, &count, argv, &(ctx.jsThis_), &pData));
389         NAPI_ASSERT(env, pData != nullptr, "Null methodDef");
390         ctx.jsArgs_ = argv;
391         auto methodDef = reinterpret_cast<const FrontendMethodDef *>(pData);
392         g_unCalledJsFuncNames.erase(string(methodDef->name_)); // api used
393         // 1. marshal parameters into json-array
394         napi_value paramArray = nullptr;
395         NAPI_CALL(env, napi_create_array_with_length(env, count, &paramArray));
396         if (count > NAPI_MAX_ARG_COUNT) {
397             count = NAPI_MAX_ARG_COUNT;
398         }
399         for (size_t idx = 0; idx < count; idx++) {
400             napi_value item = nullptr; // convert to backendObjRef if any
401             NAPI_CALL(env, GetBackendObjRefProp(env, argv[idx], &item));
402             if (item == nullptr) {
403                 item = argv[idx];
404             }
405             NAPI_CALL(env, napi_set_element(env, paramArray, idx, item));
406         }
407         napi_value jsTempObj = nullptr;
408         NAPI_CALL(env, ValueStringConvert(env, paramArray, &jsTempObj, true));
409         ctx.callInfo_.paramList_ = nlohmann::json::parse(JsStrToCppStr(env, jsTempObj));
410         // 2. marshal jsThis into json (backendObjRef)
411         if (!methodDef->static_) {
412             NAPI_CALL(env, GetBackendObjRefProp(env, ctx.jsThis_, &jsTempObj));
413             ctx.callInfo_.callerObjRef_ = JsStrToCppStr(env, jsTempObj);
414         }
415         // 3. fill-in apiId
416         ctx.callInfo_.apiId_ = methodDef->name_;
417         napi_value error = nullptr;
418         PreprocessTransaction(env, ctx, error);
419         if (error != nullptr) {
420             NAPI_CALL(env, napi_throw(env, error));
421             NAPI_CALL(env, napi_get_undefined(env, &error));
422             return error;
423         }
424         // 4. call out, sync or async
425         if (methodDef->fast_) {
426             return TransactSync(env, ctx);
427         } else {
428             return TransactAsync(env, ctx);
429         }
430     }
431 
432     /**Exports uitest js wrapper-classes and its global constructor.*/
ExportClass(napi_env env,napi_value exports,const FrontEndClassDef & classDef)433     static napi_status ExportClass(napi_env env, napi_value exports, const FrontEndClassDef &classDef)
434     {
435         NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
436         const auto name = classDef.name_.data();
437         const auto methodNeatNameOffset = classDef.name_.length() + 1;
438         auto descs = make_unique<napi_property_descriptor[]>(classDef.methodCount_);
439         for (size_t idx = 0; idx < classDef.methodCount_; idx++) {
440             const auto &methodDef = classDef.methods_[idx];
441             g_unCalledJsFuncNames.insert(string(methodDef.name_));
442             const auto neatName = methodDef.name_.substr(methodNeatNameOffset);
443             napi_property_descriptor desc = DECLARE_NAPI_FUNCTION(neatName.data(), GenericCallback);
444             if (methodDef.static_) {
445                 desc.attributes = napi_static;
446             }
447             desc.data = (void *)(&methodDef);
448             descs[idx] = desc;
449         }
450         constexpr auto initializer = [](napi_env env, napi_callback_info info) {
451             auto argc = NAPI_MAX_ARG_COUNT;
452             napi_value argv[NAPI_MAX_ARG_COUNT] = { nullptr };
453             napi_value jsThis = nullptr;
454             NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr), jsThis);
455             auto ref = make_unique<string>(argc <= 0 ? "" : JsStrToCppStr(env, argv[0]));
456             auto finalizer = [](napi_env env, void *data, void *hint) {
457                 auto ref = reinterpret_cast<string *>(data);
458                 if (ref->length() > 0) {
459                     LOG_D("Finalizing object: %{public}s", ref->c_str());
460                     unique_lock<mutex> lock(g_gcQueueMutex);
461                     g_backendObjsAboutToDelete.push(*ref);
462                 }
463                 delete ref;
464             };
465             NAPI_CALL_BASE(env, napi_wrap(env, jsThis, ref.release(), finalizer, nullptr, nullptr), jsThis);
466             return jsThis;
467         };
468         // define class, provide the js-class members(property) and initializer.
469         napi_value ctor = nullptr;
470         NAPI_CALL_BASE(env, napi_define_class(env, name, NAPI_AUTO_LENGTH, initializer, nullptr,
471                                               classDef.methodCount_, descs.get(), &ctor), NAPI_ERR);
472         NAPI_CALL_BASE(env, napi_set_named_property(env, exports, name, ctor), NAPI_ERR);
473         NAPI_CALL_BASE(env, MountJsConstructorToGlobal(env, name, ctor), NAPI_ERR);
474         if (string_view(name) == "On" || string_view(name) == "By") {
475             // create seed-On/By with special objectRef and mount to exporter
476             auto seedName = string_view(name) == "On" ? "ON" : "BY";
477             auto seedRef = string_view(name) == "On" ? REF_SEED_ON.data() : REF_SEED_BY.data();
478             napi_value seed = nullptr;
479             NAPI_CALL_BASE(env, napi_new_instance(env, ctor, 0, nullptr, &seed), NAPI_ERR);
480             napi_value prop = nullptr;
481             NAPI_CALL_BASE(env, napi_create_string_utf8(env, seedRef, NAPI_AUTO_LENGTH, &prop), NAPI_ERR);
482             NAPI_CALL_BASE(env, napi_set_named_property(env, seed, PROP_BACKEND_OBJ_REF, prop), NAPI_ERR);
483             NAPI_CALL_BASE(env, napi_set_named_property(env, exports, seedName, seed), NAPI_ERR);
484         }
485         return napi_ok;
486     }
487 
488     /**Exports enumerator class.*/
ExportEnumerator(napi_env env,napi_value exports,const FrontendEnumeratorDef & enumDef)489     static napi_status ExportEnumerator(napi_env env, napi_value exports, const FrontendEnumeratorDef &enumDef)
490     {
491         NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
492         napi_value enumerator;
493         NAPI_CALL_BASE(env, napi_create_object(env, &enumerator), NAPI_ERR);
494         for (size_t idx = 0; idx < enumDef.valueCount_; idx++) {
495             const auto &def = enumDef.values_[idx];
496             napi_value prop = nullptr;
497             NAPI_CALL_BASE(env, napi_create_string_utf8(env, def.valueJson_.data(), NAPI_AUTO_LENGTH, &prop), NAPI_ERR);
498             NAPI_CALL_BASE(env, ValueStringConvert(env, prop, &prop, false), NAPI_ERR);
499             NAPI_CALL_BASE(env, napi_set_named_property(env, enumerator, def.name_.data(), prop), NAPI_ERR);
500         }
501         NAPI_CALL_BASE(env, napi_set_named_property(env, exports, enumDef.name_.data(), enumerator), NAPI_ERR);
502         return napi_ok;
503     }
504 
505     /**Function used for statistics, return an array of uncalled js-api names.*/
GetUnCalledJsApis(napi_env env,napi_callback_info info)506     static napi_value GetUnCalledJsApis(napi_env env, napi_callback_info info)
507     {
508         napi_value nameArray;
509         NAPI_CALL(env, napi_create_array_with_length(env, g_unCalledJsFuncNames.size(), &nameArray));
510         size_t idx = 0;
511         for (auto &name : g_unCalledJsFuncNames) {
512             napi_value nameItem = nullptr;
513             NAPI_CALL(env, napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &nameItem));
514             NAPI_CALL(env, napi_set_element(env, nameArray, idx, nameItem));
515             idx++;
516         }
517         return nameArray;
518     }
519 
Export(napi_env env,napi_value exports)520     napi_value Export(napi_env env, napi_value exports)
521     {
522         LOG_I("Begin export uitest apis");
523         // export transaction-environment-lifecycle callbacks and dfx functions
524         napi_property_descriptor props[] = {
525             DECLARE_NAPI_STATIC_FUNCTION("scheduleEstablishConnection", ScheduleEstablishConnection),
526             DECLARE_NAPI_STATIC_FUNCTION("getUnCalledJsApis", GetUnCalledJsApis),
527         };
528         NAPI_CALL(env, napi_define_properties(env, exports, sizeof(props) / sizeof(props[0]), props));
529         NAPI_CALL(env, ExportClass(env, exports, BY_DEF));
530         NAPI_CALL(env, ExportClass(env, exports, UI_DRIVER_DEF));
531         NAPI_CALL(env, ExportClass(env, exports, UI_COMPONENT_DEF));
532         NAPI_CALL(env, ExportClass(env, exports, ON_DEF));
533         NAPI_CALL(env, ExportClass(env, exports, DRIVER_DEF));
534         NAPI_CALL(env, ExportClass(env, exports, COMPONENT_DEF));
535         NAPI_CALL(env, ExportClass(env, exports, UI_WINDOW_DEF));
536         NAPI_CALL(env, ExportClass(env, exports, POINTER_MATRIX_DEF));
537         NAPI_CALL(env, ExportClass(env, exports, UI_EVENT_OBSERVER_DEF));
538         NAPI_CALL(env, ExportEnumerator(env, exports, MATCH_PATTERN_DEF));
539         NAPI_CALL(env, ExportEnumerator(env, exports, RESIZE_DIRECTION_DEF));
540         NAPI_CALL(env, ExportEnumerator(env, exports, WINDOW_MODE_DEF));
541         NAPI_CALL(env, ExportEnumerator(env, exports, DISPLAY_ROTATION_DEF));
542         NAPI_CALL(env, ExportEnumerator(env, exports, MOUSE_BUTTON_DEF));
543         NAPI_CALL(env, ExportEnumerator(env, exports, UI_DIRECTION_DEF));
544         LOG_I("End export uitest apis");
545         return exports;
546     }
547 
548     static napi_module module = {
549         .nm_version = 1,
550         .nm_flags = 0,
551         .nm_filename = nullptr,
552         .nm_register_func = Export,
553         .nm_modname = "UiTest",
554         .nm_priv = ((void *)0),
555         .reserved = {0},
556     };
557 
RegisterModule(void)558     extern "C" __attribute__((constructor)) void RegisterModule(void)
559     {
560         napi_module_register(&module);
561     }
562 } // namespace OHOS::uitest
563 
564 // put register functions out of namespace to ensure C-linkage
565 extern const char _binary_uitest_exporter_js_start[];
566 extern const char _binary_uitest_exporter_js_end[];
567 extern const char _binary_uitest_exporter_abc_start[];
568 extern const char _binary_uitest_exporter_abc_end[];
569 
NAPI_UiTest_GetJSCode(const char ** buf,int * bufLen)570 extern "C" __attribute__((visibility("default"))) void NAPI_UiTest_GetJSCode(const char **buf, int *bufLen)
571 {
572     if (buf != nullptr) {
573         *buf = _binary_uitest_exporter_js_start;
574     }
575     if (bufLen != nullptr) {
576         *bufLen = _binary_uitest_exporter_js_end - _binary_uitest_exporter_js_start;
577     }
578 }
579 
NAPI_UiTest_GetABCCode(const char ** buf,int * bufLen)580 extern "C" __attribute__((visibility("default"))) void NAPI_UiTest_GetABCCode(const char **buf, int *bufLen)
581 {
582     if (buf != nullptr) {
583         *buf = _binary_uitest_exporter_abc_start;
584     }
585     if (bufLen != nullptr) {
586         *bufLen = _binary_uitest_exporter_abc_end - _binary_uitest_exporter_abc_start;
587     }
588 }
589