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 "nlohmann/json.hpp"
24 #include "fcntl.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
PreprocessTransaction(napi_env env,TransactionContext & ctx,napi_value & error)338 static void PreprocessTransaction(napi_env env, TransactionContext &ctx, napi_value &error)
339 {
340 auto ¶mList = ctx.callInfo_.paramList_;
341 const auto &id = ctx.callInfo_.apiId_;
342 if (id == "Driver.screenCap" || id == "UiDriver.screenCap" || id == "Driver.screenCapture") {
343 if (paramList.size() < 1 || paramList.at(0).type() != nlohmann::detail::value_t::string) {
344 LOG_E("Missing file path argument");
345 error = CreateJsException(env, ERR_INVALID_INPUT, "Missing file path argument");
346 return;
347 }
348 auto path = paramList.at(INDEX_ZERO).get<string>();
349 auto fd = open(path.c_str(), O_RDWR | O_CREAT, 0666);
350 if (fd == -1) {
351 LOG_E("Invalid file path: %{public}s", path.data());
352 error = CreateJsException(env, ERR_INVALID_INPUT, "Invalid file path:" + path);
353 return;
354 }
355 paramList[INDEX_ZERO] = fd;
356 ctx.callInfo_.fdParamIndex_ = INDEX_ZERO;
357 } else if (id == "UIEventObserver.once") {
358 auto err = ApiCallErr(NO_ERROR);
359 UiEventObserverNapi::Get().PreprocessCallOnce(env, ctx.callInfo_, ctx.jsThis_, ctx.jsArgs_, err);
360 if (err.code_ != NO_ERROR) {
361 error = CreateJsException(env, err.code_, err.message_);
362 }
363 }
364 }
365
366 /**Generic js-api callback.*/
GenericCallback(napi_env env,napi_callback_info info)367 static napi_value GenericCallback(napi_env env, napi_callback_info info)
368 {
369 // extract callback data
370 TransactionContext ctx;
371 napi_value argv[NAPI_MAX_ARG_COUNT] = {nullptr};
372 auto count = NAPI_MAX_ARG_COUNT;
373 void *pData = nullptr;
374 NAPI_CALL(env, napi_get_cb_info(env, info, &count, argv, &(ctx.jsThis_), &pData));
375 NAPI_ASSERT(env, pData != nullptr, "Null methodDef");
376 ctx.jsArgs_ = argv;
377 auto methodDef = reinterpret_cast<const FrontendMethodDef *>(pData);
378 g_unCalledJsFuncNames.erase(string(methodDef->name_)); // api used
379 // 1. marshal parameters into json-array
380 napi_value paramArray = nullptr;
381 NAPI_CALL(env, napi_create_array_with_length(env, count, ¶mArray));
382 if (count > NAPI_MAX_ARG_COUNT) {
383 count = NAPI_MAX_ARG_COUNT;
384 }
385 for (size_t idx = 0; idx < count; idx++) {
386 napi_value item = nullptr; // convert to backendObjRef if any
387 NAPI_CALL(env, GetBackendObjRefProp(env, argv[idx], &item));
388 if (item == nullptr) {
389 item = argv[idx];
390 }
391 NAPI_CALL(env, napi_set_element(env, paramArray, idx, item));
392 }
393 napi_value jsTempObj = nullptr;
394 NAPI_CALL(env, ValueStringConvert(env, paramArray, &jsTempObj, true));
395 ctx.callInfo_.paramList_ = nlohmann::json::parse(JsStrToCppStr(env, jsTempObj));
396 // 2. marshal jsThis into json (backendObjRef)
397 if (!methodDef->static_) {
398 NAPI_CALL(env, GetBackendObjRefProp(env, ctx.jsThis_, &jsTempObj));
399 ctx.callInfo_.callerObjRef_ = JsStrToCppStr(env, jsTempObj);
400 }
401 // 3. fill-in apiId
402 ctx.callInfo_.apiId_ = methodDef->name_;
403 napi_value error = nullptr;
404 PreprocessTransaction(env, ctx, error);
405 if (error != nullptr) {
406 NAPI_CALL(env, napi_throw(env, error));
407 NAPI_CALL(env, napi_get_undefined(env, &error));
408 return error;
409 }
410 // 4. call out, sync or async
411 if (methodDef->fast_) {
412 return TransactSync(env, ctx);
413 } else {
414 return TransactAsync(env, ctx);
415 }
416 }
417
418 /**Exports uitest js wrapper-classes and its global constructor.*/
ExportClass(napi_env env,napi_value exports,const FrontEndClassDef & classDef)419 static napi_status ExportClass(napi_env env, napi_value exports, const FrontEndClassDef &classDef)
420 {
421 NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
422 const auto name = classDef.name_.data();
423 const auto methodNeatNameOffset = classDef.name_.length() + 1;
424 auto descs = make_unique<napi_property_descriptor[]>(classDef.methodCount_);
425 for (size_t idx = 0; idx < classDef.methodCount_; idx++) {
426 const auto &methodDef = classDef.methods_[idx];
427 g_unCalledJsFuncNames.insert(string(methodDef.name_));
428 const auto neatName = methodDef.name_.substr(methodNeatNameOffset);
429 napi_property_descriptor desc = DECLARE_NAPI_FUNCTION(neatName.data(), GenericCallback);
430 if (methodDef.static_) {
431 desc.attributes = napi_static;
432 }
433 desc.data = (void *)(&methodDef);
434 descs[idx] = desc;
435 }
436 constexpr auto initializer = [](napi_env env, napi_callback_info info) {
437 auto argc = NAPI_MAX_ARG_COUNT;
438 napi_value argv[NAPI_MAX_ARG_COUNT] = { nullptr };
439 napi_value jsThis = nullptr;
440 NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr), jsThis);
441 auto ref = make_unique<string>(argc <= 0 ? "" : JsStrToCppStr(env, argv[0]));
442 auto finalizer = [](napi_env env, void *data, void *hint) {
443 auto ref = reinterpret_cast<string *>(data);
444 if (ref->length() > 0) {
445 LOG_D("Finalizing object: %{public}s", ref->c_str());
446 unique_lock<mutex> lock(g_gcQueueMutex);
447 g_backendObjsAboutToDelete.push(*ref);
448 }
449 delete ref;
450 };
451 NAPI_CALL_BASE(env, napi_wrap(env, jsThis, ref.release(), finalizer, nullptr, nullptr), jsThis);
452 return jsThis;
453 };
454 // define class, provide the js-class members(property) and initializer.
455 napi_value ctor = nullptr;
456 NAPI_CALL_BASE(env, napi_define_class(env, name, NAPI_AUTO_LENGTH, initializer, nullptr,
457 classDef.methodCount_, descs.get(), &ctor), NAPI_ERR);
458 NAPI_CALL_BASE(env, napi_set_named_property(env, exports, name, ctor), NAPI_ERR);
459 NAPI_CALL_BASE(env, MountJsConstructorToGlobal(env, name, ctor), NAPI_ERR);
460 if (string_view(name) == "On" || string_view(name) == "By") {
461 // create seed-On/By with special objectRef and mount to exporter
462 auto seedName = string_view(name) == "On" ? "ON" : "BY";
463 auto seedRef = string_view(name) == "On" ? REF_SEED_ON.data() : REF_SEED_BY.data();
464 napi_value seed = nullptr;
465 NAPI_CALL_BASE(env, napi_new_instance(env, ctor, 0, nullptr, &seed), NAPI_ERR);
466 napi_value prop = nullptr;
467 NAPI_CALL_BASE(env, napi_create_string_utf8(env, seedRef, NAPI_AUTO_LENGTH, &prop), NAPI_ERR);
468 NAPI_CALL_BASE(env, napi_set_named_property(env, seed, PROP_BACKEND_OBJ_REF, prop), NAPI_ERR);
469 NAPI_CALL_BASE(env, napi_set_named_property(env, exports, seedName, seed), NAPI_ERR);
470 }
471 return napi_ok;
472 }
473
474 /**Exports enumerator class.*/
ExportEnumerator(napi_env env,napi_value exports,const FrontendEnumeratorDef & enumDef)475 static napi_status ExportEnumerator(napi_env env, napi_value exports, const FrontendEnumeratorDef &enumDef)
476 {
477 NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
478 napi_value enumerator;
479 NAPI_CALL_BASE(env, napi_create_object(env, &enumerator), NAPI_ERR);
480 for (size_t idx = 0; idx < enumDef.valueCount_; idx++) {
481 const auto &def = enumDef.values_[idx];
482 napi_value prop = nullptr;
483 NAPI_CALL_BASE(env, napi_create_string_utf8(env, def.valueJson_.data(), NAPI_AUTO_LENGTH, &prop), NAPI_ERR);
484 NAPI_CALL_BASE(env, ValueStringConvert(env, prop, &prop, false), NAPI_ERR);
485 NAPI_CALL_BASE(env, napi_set_named_property(env, enumerator, def.name_.data(), prop), NAPI_ERR);
486 }
487 NAPI_CALL_BASE(env, napi_set_named_property(env, exports, enumDef.name_.data(), enumerator), NAPI_ERR);
488 return napi_ok;
489 }
490
491 /**Function used for statistics, return an array of uncalled js-api names.*/
GetUnCalledJsApis(napi_env env,napi_callback_info info)492 static napi_value GetUnCalledJsApis(napi_env env, napi_callback_info info)
493 {
494 napi_value nameArray;
495 NAPI_CALL(env, napi_create_array_with_length(env, g_unCalledJsFuncNames.size(), &nameArray));
496 size_t idx = 0;
497 for (auto &name : g_unCalledJsFuncNames) {
498 napi_value nameItem = nullptr;
499 NAPI_CALL(env, napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &nameItem));
500 NAPI_CALL(env, napi_set_element(env, nameArray, idx, nameItem));
501 idx++;
502 }
503 return nameArray;
504 }
505
Export(napi_env env,napi_value exports)506 napi_value Export(napi_env env, napi_value exports)
507 {
508 LOG_I("Begin export uitest apis");
509 // export transaction-environment-lifecycle callbacks and dfx functions
510 napi_property_descriptor props[] = {
511 DECLARE_NAPI_STATIC_FUNCTION("scheduleEstablishConnection", ScheduleEstablishConnection),
512 DECLARE_NAPI_STATIC_FUNCTION("getUnCalledJsApis", GetUnCalledJsApis),
513 };
514 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(props) / sizeof(props[0]), props));
515 NAPI_CALL(env, ExportClass(env, exports, BY_DEF));
516 NAPI_CALL(env, ExportClass(env, exports, UI_DRIVER_DEF));
517 NAPI_CALL(env, ExportClass(env, exports, UI_COMPONENT_DEF));
518 NAPI_CALL(env, ExportClass(env, exports, ON_DEF));
519 NAPI_CALL(env, ExportClass(env, exports, DRIVER_DEF));
520 NAPI_CALL(env, ExportClass(env, exports, COMPONENT_DEF));
521 NAPI_CALL(env, ExportClass(env, exports, UI_WINDOW_DEF));
522 NAPI_CALL(env, ExportClass(env, exports, POINTER_MATRIX_DEF));
523 NAPI_CALL(env, ExportClass(env, exports, UI_EVENT_OBSERVER_DEF));
524 NAPI_CALL(env, ExportEnumerator(env, exports, MATCH_PATTERN_DEF));
525 NAPI_CALL(env, ExportEnumerator(env, exports, RESIZE_DIRECTION_DEF));
526 NAPI_CALL(env, ExportEnumerator(env, exports, WINDOW_MODE_DEF));
527 NAPI_CALL(env, ExportEnumerator(env, exports, DISPLAY_ROTATION_DEF));
528 NAPI_CALL(env, ExportEnumerator(env, exports, MOUSE_BUTTON_DEF));
529 NAPI_CALL(env, ExportEnumerator(env, exports, UI_DIRECTION_DEF));
530 LOG_I("End export uitest apis");
531 return exports;
532 }
533
534 static napi_module module = {
535 .nm_version = 1,
536 .nm_flags = 0,
537 .nm_filename = nullptr,
538 .nm_register_func = Export,
539 .nm_modname = "UiTest",
540 .nm_priv = ((void *)0),
541 .reserved = {0},
542 };
543
RegisterModule(void)544 extern "C" __attribute__((constructor)) void RegisterModule(void)
545 {
546 napi_module_register(&module);
547 }
548 } // namespace OHOS::uitest
549
550 // put register functions out of namespace to ensure C-linkage
551 extern const char _binary_uitest_exporter_js_start[];
552 extern const char _binary_uitest_exporter_js_end[];
553 extern const char _binary_uitest_exporter_abc_start[];
554 extern const char _binary_uitest_exporter_abc_end[];
555
NAPI_UiTest_GetJSCode(const char ** buf,int * bufLen)556 extern "C" __attribute__((visibility("default"))) void NAPI_UiTest_GetJSCode(const char **buf, int *bufLen)
557 {
558 if (buf != nullptr) {
559 *buf = _binary_uitest_exporter_js_start;
560 }
561 if (bufLen != nullptr) {
562 *bufLen = _binary_uitest_exporter_js_end - _binary_uitest_exporter_js_start;
563 }
564 }
565
NAPI_UiTest_GetABCCode(const char ** buf,int * bufLen)566 extern "C" __attribute__((visibility("default"))) void NAPI_UiTest_GetABCCode(const char **buf, int *bufLen)
567 {
568 if (buf != nullptr) {
569 *buf = _binary_uitest_exporter_abc_start;
570 }
571 if (bufLen != nullptr) {
572 *bufLen = _binary_uitest_exporter_abc_end - _binary_uitest_exporter_abc_start;
573 }
574 }
575