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 "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#include "test_server_client.h" 30 31namespace OHOS::uitest { 32 using namespace nlohmann; 33 using namespace std; 34 35 static constexpr size_t NAPI_MAX_BUF_LEN = 1024; 36 static constexpr size_t NAPI_MAX_ARG_COUNT = 8; 37 static constexpr size_t BACKEND_OBJ_GC_BATCH = 100; 38 // type of unexpected or napi-internal error 39 static constexpr napi_status NAPI_ERR = napi_status::napi_generic_failure; 40 // the name of property that represents the objectRef of the backend object 41 static constexpr char PROP_BACKEND_OBJ_REF[] = "backendObjRef"; 42 /**For dfx usage, records the uncalled js apis. */ 43 static set<string> g_unCalledJsFuncNames; 44 /**For gc usage, records the backend objRefs about to delete. */ 45 static queue<string> g_backendObjsAboutToDelete; 46 static mutex g_gcQueueMutex; 47 /**IPC client. */ 48 static ApiTransactor g_apiTransactClient(false); 49 static future<void> g_establishConnectionFuture; 50 51 /** Convert js string to cpp string.*/ 52 static string JsStrToCppStr(napi_env env, napi_value jsStr) 53 { 54 if (jsStr == nullptr) { 55 return ""; 56 } 57 napi_valuetype type; 58 NAPI_CALL_BASE(env, napi_typeof(env, jsStr, &type), ""); 59 if (type == napi_undefined || type == napi_null) { 60 return ""; 61 } 62 size_t bufSize = 0; 63 char buf[NAPI_MAX_BUF_LEN] = {0}; 64 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, jsStr, buf, NAPI_MAX_BUF_LEN, &bufSize), ""); 65 return string(buf, bufSize); 66 } 67 68 /**Lifecycle function, establish connection async, called externally.*/ 69 static napi_value ScheduleEstablishConnection(napi_env env, napi_callback_info info) 70 { 71 size_t argc = 1; 72 napi_value value = nullptr; 73 napi_value argv[1] = {0}; 74 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &value, nullptr)); 75 NAPI_ASSERT(env, argc > 0, "Need session token argument!"); 76 auto token = JsStrToCppStr(env, argv[0]); 77 g_establishConnectionFuture = async(launch::async, [env, token]() { 78 auto &instance = UiEventObserverNapi::Get(); 79 using namespace std::placeholders; 80 auto callbackHandler = std::bind(&UiEventObserverNapi::HandleEventCallback, &instance, env, _1, _2); 81 auto result = g_apiTransactClient.InitAndConnectPeer(token, callbackHandler); 82 LOG_I("End setup transaction connection, result=%{public}d", result); 83 }); 84 return nullptr; 85 } 86 87 /**Wait connection result sync if need.*/ 88 static void WaitForConnectionIfNeed() 89 { 90 if (g_establishConnectionFuture.valid()) { 91 LOG_I("Begin WaitForConnection"); 92 g_establishConnectionFuture.get(); 93 } 94 } 95 96 /**Encapsulates the data objects needed in once api transaction.*/ 97 struct TransactionContext { 98 napi_value jsThis_ = nullptr; 99 napi_value *jsArgs_ = nullptr; 100 ApiCallInfo callInfo_; 101 }; 102 103 static napi_value CreateJsException(napi_env env, uint32_t code, string_view msg) 104 { 105 napi_value codeValue; 106 napi_value msgValue; 107 napi_value errorValue; 108 napi_create_uint32(env, code, &codeValue); 109 napi_create_string_utf8(env, msg.data(), NAPI_AUTO_LENGTH, &msgValue); 110 napi_create_error(env, nullptr, msgValue, &errorValue); 111 napi_set_named_property(env, errorValue, "code", codeValue); 112 return errorValue; 113 } 114 115 /**Set object constructor function to global as attribute.*/ 116 static napi_status MountJsConstructorToGlobal(napi_env env, string_view typeName, napi_value function) 117 { 118 NAPI_ASSERT_BASE(env, function != nullptr, "Null constructor function", napi_invalid_arg); 119 const string name = "constructor_" + string(typeName); 120 napi_value global = nullptr; 121 NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR); 122 NAPI_CALL_BASE(env, napi_set_named_property(env, global, name.c_str(), function), NAPI_ERR); 123 return napi_ok; 124 } 125 126 /**Get object constructor function from global as attribute.*/ 127 static napi_status GetJsConstructorFromGlobal(napi_env env, string_view typeName, napi_value *pFunction) 128 { 129 NAPI_ASSERT_BASE(env, pFunction != nullptr, "Null constructor receiver", napi_invalid_arg); 130 const string name = "constructor_" + string(typeName); 131 napi_value global = nullptr; 132 NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR); 133 NAPI_CALL_BASE(env, napi_get_named_property(env, global, name.c_str(), pFunction), NAPI_ERR); 134 return napi_ok; 135 } 136 137 /**Conversion between value and string, using the builtin JSON methods.*/ 138 static napi_status ValueStringConvert(napi_env env, napi_value in, napi_value *out, bool stringify) 139 { 140 if (in == nullptr || out == nullptr) { 141 return napi_invalid_arg; 142 } 143 napi_value global = nullptr; 144 napi_value jsonProp = nullptr; 145 napi_value jsonFunc = nullptr; 146 NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR); 147 NAPI_CALL_BASE(env, napi_get_named_property(env, global, "JSON", &jsonProp), NAPI_ERR); 148 if (stringify) { 149 NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "stringify", &jsonFunc), NAPI_ERR); 150 } else { 151 NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "parse", &jsonFunc), NAPI_ERR); 152 } 153 napi_value argv[1] = {in}; 154 NAPI_CALL_BASE(env, napi_call_function(env, jsonProp, jsonFunc, 1, argv, out), NAPI_ERR); 155 return napi_ok; 156 } 157 158 /**Unmarshal object from json, throw error and return false if the object cannot be deserialized.*/ 159 static napi_status UnmarshalObject(napi_env env, const json &in, napi_value *pOut, napi_value jsThis) 160 { 161 NAPI_ASSERT_BASE(env, pOut != nullptr, "Illegal arguments", napi_invalid_arg); 162 const auto type = in.type(); 163 if (type == nlohmann::detail::value_t::null) { // return null 164 NAPI_CALL_BASE(env, napi_get_null(env, pOut), NAPI_ERR); 165 return napi_ok; 166 } 167 if (type != nlohmann::detail::value_t::string) { // non-string value, convert and return object 168 NAPI_CALL_BASE(env, napi_create_string_utf8(env, in.dump().c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR); 169 NAPI_CALL_BASE(env, ValueStringConvert(env, *pOut, pOut, false), NAPI_ERR); 170 return napi_ok; 171 } 172 const auto cppString = in.get<string>(); 173 string frontendTypeName; 174 bool bindJsThis = false; 175 for (const auto &classDef : FRONTEND_CLASS_DEFS) { 176 const auto objRefFormat = string(classDef->name_) + "#"; 177 if (cppString.find(objRefFormat) == 0) { 178 frontendTypeName = string(classDef->name_); 179 bindJsThis = classDef->bindUiDriver_; 180 break; 181 } 182 } 183 NAPI_CALL_BASE(env, napi_create_string_utf8(env, cppString.c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR); 184 if (frontendTypeName.empty()) { // plain string, return it 185 return napi_ok; 186 } 187 LOG_D("Convert to frontend object: '%{public}s'", frontendTypeName.c_str()); 188 // covert to wrapper object and bind the backend objectRef 189 napi_value refValue = *pOut; 190 napi_value constructor = nullptr; 191 NAPI_CALL_BASE(env, GetJsConstructorFromGlobal(env, frontendTypeName, &constructor), NAPI_ERR); 192 NAPI_CALL_BASE(env, napi_new_instance(env, constructor, 1, &refValue, pOut), NAPI_ERR); 193 NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, PROP_BACKEND_OBJ_REF, refValue), NAPI_ERR); 194 if (bindJsThis) { // bind the jsThis object 195 LOG_D("Bind jsThis"); 196 NAPI_ASSERT_BASE(env, jsThis != nullptr, "null jsThis", NAPI_ERR); 197 NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, "boundObject", jsThis), NAPI_ERR); 198 } 199 return napi_ok; 200 } 201 202 /**Evaluate and convert transaction reply to object. Return the exception raised during the 203 * transaction if any, else return the result object. */ 204 static napi_value UnmarshalReply(napi_env env, const TransactionContext &ctx, const ApiReplyInfo &reply) 205 { 206 if (ctx.callInfo_.fdParamIndex_ >= 0) { 207 auto fd = ctx.callInfo_.paramList_.at(INDEX_ZERO).get<int>(); 208 (void) close(fd); 209 } 210 LOG_I("Start to Unmarshal transaction result"); 211 const auto &message = reply.exception_.message_; 212 ErrCode code = reply.exception_.code_; 213 if (code == INTERNAL_ERROR || code == ERR_INTERNAL) { 214 LOG_E("ErrorInfo: code='%{public}u', message='%{public}s'", code, message.c_str()); 215 } else if (reply.exception_.code_ != NO_ERROR) { 216 LOG_I("ErrorInfo: code='%{public}u', message='%{public}s'", code, message.c_str()); 217 return CreateJsException(env, code, message); 218 } 219 LOG_I("Start to Unmarshal return value: %{public}s", reply.resultValue_.dump().c_str()); 220 const auto resultType = reply.resultValue_.type(); 221 napi_value result = nullptr; 222 if (resultType == nlohmann::detail::value_t::null) { // return null 223 NAPI_CALL(env, napi_get_null(env, &result)); 224 } else if (resultType == nlohmann::detail::value_t::array) { // return array 225 NAPI_CALL(env, napi_create_array_with_length(env, reply.resultValue_.size(), &result)); 226 for (size_t idx = 0; idx < reply.resultValue_.size(); idx++) { 227 napi_value item = nullptr; 228 NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_.at(idx), &item, ctx.jsThis_)); 229 NAPI_CALL(env, napi_set_element(env, result, idx, item)); 230 } 231 } else { // return single value 232 NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_, &result, ctx.jsThis_)); 233 } 234 return result; 235 } 236 237 /**Call api with parameters out, wait for and return result value or throw raised exception.*/ 238 napi_value TransactSync(napi_env env, TransactionContext &ctx) 239 { 240 WaitForConnectionIfNeed(); 241 LOG_D("TargetApi=%{public}s", ctx.callInfo_.apiId_.data()); 242 auto reply = ApiReplyInfo(); 243 g_apiTransactClient.Transact(ctx.callInfo_, reply); 244 auto resultValue = UnmarshalReply(env, ctx, reply); 245 auto isError = false; 246 NAPI_CALL(env, napi_is_error(env, resultValue, &isError)); 247 if (isError) { 248 NAPI_CALL(env, napi_throw(env, resultValue)); 249 NAPI_CALL(env, napi_get_undefined(env, &resultValue)); // return undefined it's error 250 } 251 // notify backend objects deleting 252 if (g_backendObjsAboutToDelete.size() >= BACKEND_OBJ_GC_BATCH) { 253 auto gcCall = ApiCallInfo {.apiId_ = "BackendObjectsCleaner"}; 254 unique_lock<mutex> lock(g_gcQueueMutex); 255 for (size_t count = 0; count < BACKEND_OBJ_GC_BATCH; count++) { 256 gcCall.paramList_.emplace_back(g_backendObjsAboutToDelete.front()); 257 g_backendObjsAboutToDelete.pop(); 258 } 259 lock.unlock(); 260 auto gcReply = ApiReplyInfo(); 261 g_apiTransactClient.Transact(gcCall, gcReply); 262 } 263 return resultValue; 264 } 265 266 /**Encapsulates the data objects needed for async transaction.*/ 267 struct AsyncTransactionCtx { 268 TransactionContext ctx_; 269 ApiReplyInfo reply_; 270 napi_async_work asyncWork_ = nullptr; 271 napi_deferred deferred_ = nullptr; 272 napi_ref jsThisRef_ = nullptr; 273 }; 274 275 /**Call api with parameters out, return a promise.*/ 276 static napi_value TransactAsync(napi_env env, TransactionContext &ctx) 277 { 278 constexpr uint32_t refCount = 1; 279 LOG_D("TargetApi=%{public}s", ctx.callInfo_.apiId_.data()); 280 napi_value resName; 281 NAPI_CALL(env, napi_create_string_latin1(env, __FUNCTION__, NAPI_AUTO_LENGTH, &resName)); 282 auto aCtx = new AsyncTransactionCtx(); 283 aCtx->ctx_ = ctx; 284 napi_value promise; 285 NAPI_CALL(env, napi_create_promise(env, &(aCtx->deferred_), &promise)); 286 NAPI_CALL(env, napi_create_reference(env, ctx.jsThis_, refCount, &(aCtx->jsThisRef_))); 287 napi_create_async_work( 288 env, nullptr, resName, 289 [](napi_env env, void *data) { 290 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data); 291 // NOT:: use 'auto&' rather than 'auto', or the result will be set to copy-constructed temp-object 292 auto &ctx = aCtx->ctx_; 293 g_apiTransactClient.Transact(ctx.callInfo_, aCtx->reply_); 294 }, 295 [](napi_env env, napi_status status, void *data) { 296 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data); 297 napi_handle_scope scope = nullptr; 298 napi_open_handle_scope(env, &scope); 299 if (scope == nullptr) { 300 return; 301 } 302 napi_get_reference_value(env, aCtx->jsThisRef_, &(aCtx->ctx_.jsThis_)); 303 auto resultValue = UnmarshalReply(env, aCtx->ctx_, aCtx->reply_); 304 napi_delete_reference(env, aCtx->jsThisRef_); 305 auto isError = false; 306 napi_is_error(env, resultValue, &isError); 307 if (isError) { 308 napi_reject_deferred(env, aCtx->deferred_, resultValue); 309 } else { 310 napi_resolve_deferred(env, aCtx->deferred_, resultValue); 311 } 312 napi_delete_async_work(env, aCtx->asyncWork_); 313 napi_close_handle_scope(env, scope); 314 delete aCtx; 315 }, 316 (void *)aCtx, &(aCtx->asyncWork_)); 317 napi_queue_async_work(env, aCtx->asyncWork_); 318 return promise; 319 } 320 321 static napi_status GetBackendObjRefProp(napi_env env, napi_value value, napi_value* pOut) 322 { 323 napi_valuetype type = napi_undefined; 324 NAPI_CALL_BASE(env, napi_typeof(env, value, &type), NAPI_ERR); 325 if (type != napi_object) { 326 *pOut = nullptr; 327 return napi_ok; 328 } 329 bool hasRef = false; 330 NAPI_CALL_BASE(env, napi_has_named_property(env, value, PROP_BACKEND_OBJ_REF, &hasRef), NAPI_ERR); 331 if (!hasRef) { 332 *pOut = nullptr; 333 } else { 334 NAPI_CALL_BASE(env, napi_get_named_property(env, value, PROP_BACKEND_OBJ_REF, pOut), NAPI_ERR); 335 } 336 return napi_ok; 337 } 338 339 static void SetPasteBoardData(string_view text) 340 { 341 OHOS::testserver::TestServerClient::GetInstance().SetPasteData(string(text)); 342 } 343 344 static void PreprocessTransaction(napi_env env, TransactionContext &ctx, napi_value &error) 345 { 346 auto ¶mList = ctx.callInfo_.paramList_; 347 const auto &id = ctx.callInfo_.apiId_; 348 if (id == "Component.inputText" && paramList.size() > 0) { 349 if (paramList.at(INDEX_ZERO).type() == nlohmann::detail::value_t::string) { 350 SetPasteBoardData(paramList.at(INDEX_ZERO).get<string>()); 351 } 352 } else if (id == "Driver.inputText" && paramList.size() > 1) { 353 if (paramList.at(INDEX_ONE).type() == nlohmann::detail::value_t::string) { 354 SetPasteBoardData(paramList.at(INDEX_ONE).get<string>()); 355 } 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.*/ 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, ¶mArray)); 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.*/ 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.*/ 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.*/ 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 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 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 565extern const char _binary_uitest_exporter_js_start[]; 566extern const char _binary_uitest_exporter_js_end[]; 567extern const char _binary_uitest_exporter_abc_start[]; 568extern const char _binary_uitest_exporter_abc_end[]; 569 570extern "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 580extern "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