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 <sstream> 17 #include <unistd.h> 18 #include "ui_driver.h" 19 #include "widget_operator.h" 20 #include "window_operator.h" 21 #include "ui_controller.h" 22 #include "frontend_api_handler.h" 23 24 namespace OHOS::uitest { 25 using namespace std; 26 using namespace nlohmann; 27 using namespace nlohmann::detail; 28 29 class UIEventObserver : public BackendClass { 30 public: UIEventObserver()31 UIEventObserver() {}; GetFrontendClassDef() const32 const FrontEndClassDef &GetFrontendClassDef() const override 33 { 34 return UI_EVENT_OBSERVER_DEF; 35 }; 36 }; 37 38 class UiEventFowarder : public UiEventListener { 39 public: UiEventFowarder()40 UiEventFowarder() {}; 41 IncRef(const string & ref)42 void IncRef(const string &ref) 43 { 44 auto find = refCountMap_.find(ref); 45 if (find != refCountMap_.end()) { 46 find->second++; 47 } else { 48 refCountMap_.insert(make_pair(ref, 1)); 49 } 50 } 51 DecAndGetRef(const string & ref)52 uint32_t DecAndGetRef(const string &ref) 53 { 54 auto find = refCountMap_.find(ref); 55 if (find != refCountMap_.end()) { 56 find->second--; 57 if (find->second == 0) { 58 refCountMap_.erase(find); 59 return 0; 60 } 61 return find->second; 62 } 63 return 0; 64 } 65 OnEvent(const std::string & event,const UiEventSourceInfo & source)66 void OnEvent(const std::string &event, const UiEventSourceInfo &source) override 67 { 68 const auto &server = FrontendApiServer::Get(); 69 json uiElementInfo; 70 uiElementInfo["bundleName"] = source.bundleName; 71 uiElementInfo["type"] = source.type; 72 uiElementInfo["text"] = source.text; 73 auto count = callBackInfos_.count(event); 74 auto index = 0; 75 while (index < count) { 76 auto find = callBackInfos_.find(event); 77 if (find == callBackInfos_.end()) { 78 return; 79 } 80 const auto &observerRef = find->second.first; 81 const auto &callbackRef = find->second.second; 82 ApiCallInfo in; 83 ApiReplyInfo out; 84 in.apiId_ = "UIEventObserver.once"; 85 in.callerObjRef_ = observerRef; 86 in.paramList_.push_back(uiElementInfo); 87 in.paramList_.push_back(callbackRef); 88 in.paramList_.push_back(DecAndGetRef(observerRef) == 0); 89 in.paramList_.push_back(DecAndGetRef(callbackRef) == 0); 90 server.Callback(in, out); 91 callBackInfos_.erase(find); 92 index++; 93 } 94 } 95 AddCallbackInfo(const string && event,const string & observerRef,const string && cbRef)96 void AddCallbackInfo(const string &&event, const string &observerRef, const string &&cbRef) 97 { 98 auto count = callBackInfos_.count(event); 99 auto find = callBackInfos_.find(event); 100 for (size_t index = 0; index < count; index++) { 101 if (find != callBackInfos_.end()) { 102 if (find->second.first == observerRef && find->second.second == cbRef) { 103 return; 104 } 105 find++; 106 } 107 } 108 callBackInfos_.insert(make_pair(event, make_pair(observerRef, cbRef))); 109 IncRef(observerRef); 110 IncRef(cbRef); 111 } 112 113 private: 114 multimap<string, pair<string, string>> callBackInfos_; 115 map<string, int> refCountMap_; 116 }; 117 118 /** API argument type list map.*/ 119 static multimap<string, pair<vector<string>, size_t>> sApiArgTypesMap; 120 ParseMethodSignature(string_view signature,vector<string> & types,size_t & defArg)121 static void ParseMethodSignature(string_view signature, vector<string> &types, size_t &defArg) 122 { 123 int charIndex = 0; 124 constexpr size_t BUF_LEN = 32; 125 char buf[BUF_LEN]; 126 size_t tokenLen = 0; 127 size_t defArgCount = 0; 128 string token; 129 for (char ch : signature) { 130 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { 131 buf[tokenLen++] = ch; 132 } else if (ch == '?') { 133 defArgCount++; 134 } else if (ch == ',' || ch == '?' || ch == ')') { 135 if (tokenLen > 0) { 136 token = string_view(buf, tokenLen); 137 DCHECK(find(DATA_TYPE_SCOPE.begin(), DATA_TYPE_SCOPE.end(), token) != DATA_TYPE_SCOPE.end()); 138 types.emplace_back(token); 139 } 140 tokenLen = 0; // consume token and reset buffer 141 if (ch == ')') { 142 // add return value type to the end of types. 143 string retType = string(signature.substr(charIndex + 2)); 144 types.emplace_back(retType); 145 break; // end parsing 146 } 147 } 148 charIndex++; 149 } 150 defArg = defArgCount; 151 } 152 153 /** Parse frontend method definitions to collect type information.*/ ParseFrontendMethodsSignature()154 static void ParseFrontendMethodsSignature() 155 { 156 for (auto classDef : FRONTEND_CLASS_DEFS) { 157 LOG_D("parse class %{public}s", string(classDef->name_).c_str()); 158 if (classDef->methods_ == nullptr || classDef->methodCount_ <= 0) { 159 continue; 160 } 161 for (size_t idx = 0; idx < classDef->methodCount_; idx++) { 162 auto methodDef = classDef->methods_[idx]; 163 auto paramTypes = vector<string>(); 164 size_t hasDefaultArg = 0; 165 ParseMethodSignature(methodDef.signature_, paramTypes, hasDefaultArg); 166 sApiArgTypesMap.insert(make_pair(string(methodDef.name_), make_pair(paramTypes, hasDefaultArg))); 167 } 168 } 169 } 170 GetClassName(const string & apiName,char splitter)171 static string GetClassName(const string &apiName, char splitter) 172 { 173 auto classNameLen = apiName.find(splitter); 174 if (classNameLen == std::string::npos) { 175 return ""; 176 } 177 return apiName.substr(0, classNameLen); 178 } 179 CheckAndDoApiMapping(string_view apiName,char splitter,const map<string,string> & apiMap)180 static string CheckAndDoApiMapping(string_view apiName, char splitter, const map<string, string> &apiMap) 181 { 182 string output = string(apiName); 183 auto classNameLen = output.find(splitter); 184 if (classNameLen == std::string::npos) { 185 return output; 186 } 187 string className = output.substr(0, classNameLen); 188 auto result = apiMap.find(className); 189 if (result == apiMap.end()) { 190 return output; 191 } 192 auto specialMapItem = apiMap.find(output); 193 if (specialMapItem != apiMap.end()) { 194 output = specialMapItem->second; 195 } else { 196 output.replace(0, classNameLen, result->second); 197 } 198 LOG_D("original className: %{public}s, modified to: %{public}s", className.c_str(), output.c_str()); 199 return output; 200 } 201 FrontendApiServer()202 FrontendApiServer::FrontendApiServer() 203 { 204 old2NewApiMap_["By"] = "On"; 205 old2NewApiMap_["UiDriver"] = "Driver"; 206 old2NewApiMap_["UiComponent"] = "Component"; 207 old2NewApiMap_["By.id"] = "On.accessibilityId"; 208 old2NewApiMap_["By.key"] = "On.id"; 209 old2NewApiMap_["UiComponent.getId"] = "Component.getAccessibilityId"; 210 old2NewApiMap_["UiComponent.getKey"] = "Component.getId"; 211 new2OldApiMap_["On"] = "By" ; 212 new2OldApiMap_["Driver"] = "UiDriver" ; 213 new2OldApiMap_["Component"] = "UiComponent" ; 214 } 215 216 /** 217 * map api8 old api call to new api. 218 * return old api name if it's an old api call. 219 * return empty string if it's a new api call. 220 */ 221 static const std::map<ErrCode, std::vector<ErrCode>> ErrCodeMap = { 222 /**Correspondence between old and new error codes*/ 223 {NO_ERROR, {NO_ERROR}}, 224 {ERR_COMPONENT_LOST, {WIDGET_LOST, WINDOW_LOST}}, 225 {ERR_NO_SYSTEM_CAPABILITY, {USAGE_ERROR}}, 226 {ERR_INTERNAL, {INTERNAL_ERROR}}, 227 {ERR_INITIALIZE_FAILED, {USAGE_ERROR}}, 228 {ERR_INVALID_INPUT, {USAGE_ERROR}}, 229 {ERR_ASSERTION_FAILED, {ASSERTION_FAILURE}}, 230 {ERR_OPERATION_UNSUPPORTED, {INTERNAL_ERROR}}, 231 {ERR_API_USAGE, {INTERNAL_ERROR}}, 232 }; 233 ApiMapPre(ApiCallInfo & inModifier) const234 string FrontendApiServer::ApiMapPre(ApiCallInfo &inModifier) const 235 { 236 // 1. map method name 237 const string &className = GetClassName(inModifier.apiId_, '.'); 238 const auto result = old2NewApiMap_.find(className); 239 if (result == old2NewApiMap_.end()) { 240 return ""; 241 } 242 string oldApiName = inModifier.apiId_; 243 inModifier.apiId_ = CheckAndDoApiMapping(inModifier.apiId_, '.', old2NewApiMap_); 244 LOG_D("Modify call name to %{public}s", inModifier.apiId_.c_str()); 245 // 2. map callerObjRef 246 if (inModifier.callerObjRef_.find(className) == 0) { 247 inModifier.callerObjRef_.replace(0, className.length(), result->second); 248 LOG_D("Modify callobjref to %{public}s", inModifier.callerObjRef_.c_str()); 249 } 250 // 3. map parameters 251 // find method signature of old api 252 const auto find = sApiArgTypesMap.find(oldApiName); 253 if (find == sApiArgTypesMap.end()) { 254 return oldApiName; 255 } 256 const auto &argTypes = find->second.first; 257 size_t argCount = inModifier.paramList_.size(); 258 if (argTypes.size() - 1 < argCount) { 259 // parameter number invalid 260 return oldApiName; 261 } 262 for (size_t i = 0; i < argCount; i++) { 263 auto &argItem = inModifier.paramList_.at(i); 264 const string &argType = argTypes.at(i); 265 if (argType != "string" && argItem.type() == value_t::string) { 266 argItem = CheckAndDoApiMapping(argItem.get<string>(), '#', old2NewApiMap_); 267 } 268 } 269 return oldApiName; 270 } 271 ErrCodeMapping(ApiCallErr & apiErr)272 static void ErrCodeMapping(ApiCallErr &apiErr) 273 { 274 ErrCode ec = apiErr.code_; 275 auto msg = apiErr.message_; 276 auto findCode = ErrCodeMap.find(ec); 277 if (findCode != ErrCodeMap.end() && !findCode->second.empty()) { 278 apiErr = ApiCallErr(findCode->second.at(0), msg); 279 } 280 } 281 282 /** map new error code and api Object to old error code and api Object. */ ApiMapPost(const string & oldApiName,ApiReplyInfo & out) const283 void FrontendApiServer::ApiMapPost(const string &oldApiName, ApiReplyInfo &out) const 284 { 285 json &value = out.resultValue_; 286 const auto type = value.type(); 287 // 1. error code conversion 288 ErrCodeMapping(out.exception_); 289 // 2. ret value conversion 290 const auto find = sApiArgTypesMap.find(oldApiName); 291 if (find == sApiArgTypesMap.end()) { 292 return; 293 } 294 string &retType = find->second.first.back(); 295 if ((retType == "string") || (retType == "[string]")) { 296 return; 297 } 298 if (type == value_t::string) { 299 value = CheckAndDoApiMapping(value.get<string>(), '#', new2OldApiMap_); 300 } else if (type == value_t::array) { 301 for (auto &item : value) { 302 if (item.type() == value_t::string) { 303 item = CheckAndDoApiMapping(item.get<string>(), '#', new2OldApiMap_); 304 } 305 } 306 } 307 } 308 Get()309 FrontendApiServer &FrontendApiServer::Get() 310 { 311 static FrontendApiServer singleton; 312 return singleton; 313 } 314 AddHandler(string_view apiId,ApiInvokeHandler handler)315 void FrontendApiServer::AddHandler(string_view apiId, ApiInvokeHandler handler) 316 { 317 if (handler == nullptr) { 318 return; 319 } 320 handlers_.insert(make_pair(apiId, handler)); 321 } 322 SetCallbackHandler(ApiInvokeHandler handler)323 void FrontendApiServer::SetCallbackHandler(ApiInvokeHandler handler) 324 { 325 callbackHandler_ = handler; 326 } 327 Callback(const ApiCallInfo & in,ApiReplyInfo & out) const328 void FrontendApiServer::Callback(const ApiCallInfo& in, ApiReplyInfo& out) const 329 { 330 if (callbackHandler_ == nullptr) { 331 out.exception_ = ApiCallErr(ERR_INTERNAL, "No callback handler set!"); 332 return; 333 } 334 callbackHandler_(in, out); 335 } 336 HasHandlerFor(std::string_view apiId) const337 bool FrontendApiServer::HasHandlerFor(std::string_view apiId) const 338 { 339 string apiIdstr = CheckAndDoApiMapping(apiId, '.', old2NewApiMap_); 340 return handlers_.find(apiIdstr) != handlers_.end(); 341 } 342 RemoveHandler(string_view apiId)343 void FrontendApiServer::RemoveHandler(string_view apiId) 344 { 345 handlers_.erase(string(apiId)); 346 } 347 AddCommonPreprocessor(string_view name,ApiInvokeHandler processor)348 void FrontendApiServer::AddCommonPreprocessor(string_view name, ApiInvokeHandler processor) 349 { 350 if (processor == nullptr) { 351 return; 352 } 353 commonPreprocessors_.insert(make_pair(name, processor)); 354 } 355 RemoveCommonPreprocessor(string_view name)356 void FrontendApiServer::RemoveCommonPreprocessor(string_view name) 357 { 358 commonPreprocessors_.erase(string(name)); 359 } 360 Call(const ApiCallInfo & in,ApiReplyInfo & out) const361 void FrontendApiServer::Call(const ApiCallInfo &in, ApiReplyInfo &out) const 362 { 363 LOG_D("Begin to invoke api '%{public}s'", in.apiId_.data()); 364 auto call = in; 365 // initialize method signature 366 if (sApiArgTypesMap.empty()) { 367 ParseFrontendMethodsSignature(); 368 } 369 string oldApiName = ApiMapPre(call); 370 auto find = handlers_.find(call.apiId_); 371 if (find == handlers_.end()) { 372 out.exception_ = ApiCallErr(ERR_INTERNAL, "No handler found for api '" + call.apiId_ + "'"); 373 return; 374 } 375 try { 376 for (auto &[name, processor] : commonPreprocessors_) { 377 processor(call, out); 378 if (out.exception_.code_ != NO_ERROR) { 379 out.exception_.message_ = out.exception_.message_ + "(PreProcessing: " + name + ")"; 380 if (oldApiName.length() > 0) { 381 ApiMapPost(oldApiName, out); 382 } 383 return; // error during pre-processing, abort 384 } 385 } 386 } catch (std::exception &ex) { 387 out.exception_ = ApiCallErr(ERR_INTERNAL, "Preprocessor failed: " + string(ex.what())); 388 } 389 try { 390 find->second(call, out); 391 } catch (std::exception &ex) { 392 // catch possible json-parsing error 393 out.exception_ = ApiCallErr(ERR_INTERNAL, "Handler failed: " + string(ex.what())); 394 } 395 if (oldApiName.length() > 0) { 396 ApiMapPost(oldApiName, out); 397 } 398 } 399 ApiTransact(const ApiCallInfo & in,ApiReplyInfo & out)400 void ApiTransact(const ApiCallInfo &in, ApiReplyInfo &out) 401 { 402 LOG_D("Begin to invoke api '%{public}s'", in.apiId_.data()); 403 FrontendApiServer::Get().Call(in, out); 404 } 405 406 /** Backend objects cache.*/ 407 static map<string, unique_ptr<BackendClass>> sBackendObjects; 408 /** UiDriver binding map.*/ 409 static map<string, string> sDriverBindingMap; 410 411 412 #define CHECK_CALL_ARG(condition, code, message, error) \ 413 if (!(condition)) { \ 414 error = ApiCallErr((code), (message)); \ 415 return; \ 416 } 417 418 /** Check if the json value represents and illegal data of expected type.*/ CheckCallArgType(string_view expect,const json & value,bool isDefAgc,ApiCallErr & error)419 static void CheckCallArgType(string_view expect, const json &value, bool isDefAgc, ApiCallErr &error) 420 { 421 const auto type = value.type(); 422 if (isDefAgc && type == value_t::null) { 423 return; 424 } 425 const auto isInteger = type == value_t::number_integer || type == value_t::number_unsigned; 426 auto begin0 = FRONTEND_CLASS_DEFS.begin(); 427 auto end0 = FRONTEND_CLASS_DEFS.end(); 428 auto begin1 = FRONTEND_JSON_DEFS.begin(); 429 auto end1 = FRONTEND_JSON_DEFS.end(); 430 auto find0 = find_if(begin0, end0, [&expect](const FrontEndClassDef *def) { return def->name_ == expect; }); 431 auto find1 = find_if(begin1, end1, [&expect](const FrontEndJsonDef *def) { return def->name_ == expect; }); 432 if (expect == "int") { 433 CHECK_CALL_ARG(isInteger, ERR_INVALID_INPUT, "Expect integer", error); 434 } else if (expect == "float") { 435 CHECK_CALL_ARG(isInteger || type == value_t::number_float, ERR_INVALID_INPUT, "Expect float", error); 436 } else if (expect == "bool") { 437 CHECK_CALL_ARG(type == value_t::boolean, ERR_INVALID_INPUT, "Expect boolean", error); 438 } else if (expect == "string") { 439 CHECK_CALL_ARG(type == value_t::string, ERR_INVALID_INPUT, "Expect string", error); 440 } else if (find0 != end0) { 441 CHECK_CALL_ARG(type == value_t::string, ERR_INVALID_INPUT, "Expect " + string(expect), error); 442 const auto findRef = sBackendObjects.find(value.get<string>()); 443 CHECK_CALL_ARG(findRef != sBackendObjects.end(), ERR_INTERNAL, "Bad object ref", error); 444 } else if (find1 != end1) { 445 CHECK_CALL_ARG(type == value_t::object, ERR_INVALID_INPUT, "Expect " + string(expect), error); 446 auto copy = value; 447 for (size_t idx = 0; idx < (*find1)->propCount_; idx++) { 448 auto def = (*find1)->props_ + idx; 449 const auto propName = string(def->name_); 450 if (!value.contains(propName)) { 451 CHECK_CALL_ARG(!(def->required_), ERR_INVALID_INPUT, "Missing property " + propName, error); 452 continue; 453 } 454 copy.erase(propName); 455 // check json property value type recursive 456 CheckCallArgType(def->type_, value[propName], !def->required_, error); 457 if (error.code_ != NO_ERROR) { 458 error.message_ = "Illegal value of property '" + propName + "': " + error.message_; 459 return; 460 } 461 } 462 CHECK_CALL_ARG(copy.empty(), ERR_INVALID_INPUT, "Illegal property of " + string(expect), error); 463 } else { 464 CHECK_CALL_ARG(false, ERR_INTERNAL, "Unknown target type " + string(expect), error); 465 } 466 } 467 468 /** Checks ApiCallInfo data, deliver exception and abort invocation if check fails.*/ APiCallInfoChecker(const ApiCallInfo & in,ApiReplyInfo & out)469 static void APiCallInfoChecker(const ApiCallInfo &in, ApiReplyInfo &out) 470 { 471 auto count = sApiArgTypesMap.count(in.apiId_); 472 // return nullptr by default 473 out.resultValue_ = nullptr; 474 auto find = sApiArgTypesMap.find(in.apiId_); 475 size_t index = 0; 476 while (index < count) { 477 if (find == sApiArgTypesMap.end()) { 478 return; 479 } 480 bool checkArgNum = false; 481 bool checkArgType = true; 482 out.exception_ = {NO_ERROR, "No Error"}; 483 auto &types = find->second.first; 484 auto argSupportDefault = find->second.second; 485 // check argument count.(last item of "types" is return value type) 486 auto maxArgc = types.size() - 1; 487 auto minArgc = maxArgc - argSupportDefault; 488 auto argc = in.paramList_.size(); 489 checkArgNum = argc <= maxArgc && argc >= minArgc; 490 if (!checkArgNum) { 491 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Illegal argument count"); 492 ++find; 493 ++index; 494 continue; 495 } 496 // check argument type 497 for (size_t idx = 0; idx < argc; idx++) { 498 auto isDefArg = (argc - idx <= argSupportDefault) ? true : false; 499 CheckCallArgType(types.at(idx), in.paramList_.at(idx), isDefArg, out.exception_); 500 if (out.exception_.code_ != NO_ERROR) { 501 out.exception_.message_ = "Check arg" + to_string(idx) + " failed: " + out.exception_.message_; 502 checkArgType = false; 503 break; 504 } 505 } 506 if (checkArgType) { 507 return; 508 } 509 ++find; 510 ++index; 511 } 512 } 513 514 /** Store the backend object and return the reference-id.*/ StoreBackendObject(unique_ptr<BackendClass> ptr,string_view ownerRef="")515 static string StoreBackendObject(unique_ptr<BackendClass> ptr, string_view ownerRef = "") 516 { 517 static map<string, uint32_t> sObjectCounts; 518 DCHECK(ptr != nullptr); 519 const auto typeName = string(ptr->GetFrontendClassDef().name_); 520 auto find = sObjectCounts.find(typeName); 521 uint32_t index = 0; 522 if (find != sObjectCounts.end()) { 523 index = find->second; 524 } 525 auto ref = typeName + "#" + to_string(index); 526 sObjectCounts[typeName] = index + 1; 527 sBackendObjects[ref] = move(ptr); 528 if (!ownerRef.empty()) { 529 DCHECK(sBackendObjects.find(string(ownerRef)) != sBackendObjects.end()); 530 sDriverBindingMap[ref] = ownerRef; 531 } 532 return ref; 533 } 534 535 /** Retrieve the stored backend object by reference-id.*/ 536 template <typename T, typename = enable_if<is_base_of_v<BackendClass, T>>> GetBackendObject(string_view ref)537 static T &GetBackendObject(string_view ref) 538 { 539 auto find = sBackendObjects.find(string(ref)); 540 DCHECK(find != sBackendObjects.end() && find->second != nullptr); 541 return *(reinterpret_cast<T *>(find->second.get())); 542 } 543 GetBoundUiDriver(string_view ref)544 static UiDriver &GetBoundUiDriver(string_view ref) 545 { 546 auto find0 = sDriverBindingMap.find(string(ref)); 547 DCHECK(find0 != sDriverBindingMap.end()); 548 auto find1 = sBackendObjects.find(find0->second); 549 DCHECK(find1 != sBackendObjects.end() && find1->second != nullptr); 550 return *(reinterpret_cast<UiDriver *>(find1->second.get())); 551 } 552 553 /** Delete stored backend objects.*/ BackendObjectsCleaner(const ApiCallInfo & in,ApiReplyInfo & out)554 static void BackendObjectsCleaner(const ApiCallInfo &in, ApiReplyInfo &out) 555 { 556 stringstream ss("Deleted objects["); 557 DCHECK(in.paramList_.type() == value_t::array); 558 for (const auto &item : in.paramList_) { 559 DCHECK(item.type() == value_t::string); // must be objRef 560 const auto ref = item.get<string>(); 561 auto findBinding = sDriverBindingMap.find(ref); 562 if (findBinding != sDriverBindingMap.end()) { 563 sDriverBindingMap.erase(findBinding); 564 } 565 auto findObject = sBackendObjects.find(ref); 566 if (findObject == sBackendObjects.end()) { 567 LOG_W("No such object living: %{public}s", ref.c_str()); 568 continue; 569 } 570 sBackendObjects.erase(findObject); 571 ss << ref << ","; 572 } 573 ss << "]"; 574 LOG_D("%{public}s", ss.str().c_str()); 575 } 576 ReadCallArg(const ApiCallInfo & in,size_t index)577 template <typename T> static T ReadCallArg(const ApiCallInfo &in, size_t index) 578 { 579 DCHECK(in.paramList_.type() == value_t::array); 580 DCHECK(index <= in.paramList_.size()); 581 return in.paramList_.at(index).get<T>(); 582 } 583 ReadCallArg(const ApiCallInfo & in,size_t index,T defValue)584 template <typename T> static T ReadCallArg(const ApiCallInfo &in, size_t index, T defValue) 585 { 586 DCHECK(in.paramList_.type() == value_t::array); 587 if (index >= in.paramList_.size()) { 588 return defValue; 589 } 590 auto type = in.paramList_.at(index).type(); 591 if (type == value_t::null) { 592 return defValue; 593 } else { 594 return in.paramList_.at(index).get<T>(); 595 } 596 } 597 GenericOnAttrBuilder(const ApiCallInfo & in,ApiReplyInfo & out)598 template <UiAttr kAttr, typename T> static void GenericOnAttrBuilder(const ApiCallInfo &in, ApiReplyInfo &out) 599 { 600 const auto attrName = ATTR_NAMES[kAttr]; 601 // always create and return a new selector 602 auto selector = make_unique<WidgetSelector>(); 603 if (in.callerObjRef_ != REF_SEED_ON) { // copy-construct from the caller if it's not seed 604 *selector = GetBackendObject<WidgetSelector>(in.callerObjRef_); 605 } 606 string testValue = ""; 607 if constexpr (std::is_same<string, T>::value) { 608 testValue = ReadCallArg<string>(in, INDEX_ZERO); 609 } else if constexpr (std::is_same<bool, T>::value) { // bool testValue can be defaulted to true 610 testValue = ReadCallArg<bool>(in, INDEX_ZERO, true) ? "true" : "false"; 611 } else { 612 testValue = to_string(ReadCallArg<T>(in, INDEX_ZERO)); 613 } 614 auto matchPattern = ReadCallArg<uint8_t>(in, INDEX_ONE, ValueMatchPattern::EQ); // match pattern argument 615 auto matcher = WidgetAttrMatcher(attrName, testValue, static_cast<ValueMatchPattern>(matchPattern)); 616 selector->AddMatcher(matcher); 617 out.resultValue_ = StoreBackendObject(move(selector)); 618 } 619 RegisterOnBuilders()620 static void RegisterOnBuilders() 621 { 622 auto &server = FrontendApiServer::Get(); 623 server.AddHandler("On.accessibilityId", GenericOnAttrBuilder<UiAttr::ACCESSIBILITY_ID, int32_t>); 624 server.AddHandler("On.text", GenericOnAttrBuilder<UiAttr::TEXT, string>); 625 server.AddHandler("On.id", GenericOnAttrBuilder<UiAttr::ID, string>); 626 server.AddHandler("On.type", GenericOnAttrBuilder<UiAttr::TYPE, string>); 627 server.AddHandler("On.enabled", GenericOnAttrBuilder<UiAttr::ENABLED, bool>); 628 server.AddHandler("On.focused", GenericOnAttrBuilder<UiAttr::FOCUSED, bool>); 629 server.AddHandler("On.selected", GenericOnAttrBuilder<UiAttr::SELECTED, bool>); 630 server.AddHandler("On.clickable", GenericOnAttrBuilder<UiAttr::CLICKABLE, bool>); 631 server.AddHandler("On.longClickable", GenericOnAttrBuilder<UiAttr::LONG_CLICKABLE, bool>); 632 server.AddHandler("On.scrollable", GenericOnAttrBuilder<UiAttr::SCROLLABLE, bool>); 633 server.AddHandler("On.checkable", GenericOnAttrBuilder<UiAttr::CHECKABLE, bool>); 634 server.AddHandler("On.checked", GenericOnAttrBuilder<UiAttr::CHECKED, bool>); 635 636 auto genericRelativeBuilder = [](const ApiCallInfo &in, ApiReplyInfo &out) { 637 const auto attrName = in.apiId_.substr(ON_DEF.name_.length() + 1); // On.xxx()->xxx 638 // always create and return a new selector 639 auto selector = make_unique<WidgetSelector>(); 640 if (in.callerObjRef_ != REF_SEED_ON) { // copy-construct from the caller if it's not seed 641 *selector = GetBackendObject<WidgetSelector>(in.callerObjRef_); 642 } 643 if (attrName == "isBefore") { 644 auto &that = GetBackendObject<WidgetSelector>(ReadCallArg<string>(in, INDEX_ZERO)); 645 selector->AddRearLocator(that, out.exception_); 646 } else if (attrName == "isAfter") { 647 auto &that = GetBackendObject<WidgetSelector>(ReadCallArg<string>(in, INDEX_ZERO)); 648 selector->AddFrontLocator(that, out.exception_); 649 } else if (attrName == "within") { 650 auto &that = GetBackendObject<WidgetSelector>(ReadCallArg<string>(in, INDEX_ZERO)); 651 selector->AddParentLocator(that, out.exception_); 652 } else if (attrName == "inWindow") { 653 auto hostApp = ReadCallArg<string>(in, INDEX_ZERO); 654 selector->AddAppLocator(hostApp); 655 } 656 out.resultValue_ = StoreBackendObject(move(selector)); 657 }; 658 server.AddHandler("On.isBefore", genericRelativeBuilder); 659 server.AddHandler("On.isAfter", genericRelativeBuilder); 660 server.AddHandler("On.within", genericRelativeBuilder); 661 server.AddHandler("On.inWindow", genericRelativeBuilder); 662 } 663 RegisterUiDriverComponentFinders()664 static void RegisterUiDriverComponentFinders() 665 { 666 auto &server = FrontendApiServer::Get(); 667 auto genericFindWidgetHandler = [](const ApiCallInfo &in, ApiReplyInfo &out) { 668 const auto driverRef = in.callerObjRef_; 669 auto &driver = GetBackendObject<UiDriver>(driverRef); 670 auto &selector = GetBackendObject<WidgetSelector>(ReadCallArg<string>(in, INDEX_ZERO)); 671 UiOpArgs uiOpArgs; 672 vector<unique_ptr<Widget>> recv; 673 if (in.apiId_ == "Driver.waitForComponent") { 674 uiOpArgs.waitWidgetMaxMs_ = ReadCallArg<uint32_t>(in, INDEX_ONE); 675 auto result = driver.WaitForWidget(selector, uiOpArgs, out.exception_); 676 if (result != nullptr) { 677 recv.emplace_back(move(result)); 678 } 679 } else { 680 driver.FindWidgets(selector, recv, out.exception_); 681 } 682 if (out.exception_.code_ != NO_ERROR) { 683 return; 684 } 685 if (in.apiId_ == "Driver.assertComponentExist") { 686 if (recv.empty()) { // widget-exist assertion failure, deliver exception 687 out.exception_.code_ = ERR_ASSERTION_FAILED; 688 out.exception_.message_ = "Component not exist matching: " + selector.Describe(); 689 } 690 } else if (in.apiId_ == "Driver.findComponents") { // return widget array, maybe empty 691 for (auto &ptr : recv) { 692 out.resultValue_.emplace_back(StoreBackendObject(move(ptr), driverRef)); 693 } 694 } else if (recv.empty()) { // return first one or null 695 out.resultValue_ = nullptr; 696 } else { 697 out.resultValue_ = StoreBackendObject(move(*(recv.begin())), driverRef); 698 } 699 }; 700 server.AddHandler("Driver.findComponent", genericFindWidgetHandler); 701 server.AddHandler("Driver.findComponents", genericFindWidgetHandler); 702 server.AddHandler("Driver.waitForComponent", genericFindWidgetHandler); 703 server.AddHandler("Driver.assertComponentExist", genericFindWidgetHandler); 704 } 705 RegisterUiDriverWindowFinder()706 static void RegisterUiDriverWindowFinder() 707 { 708 auto findWindowHandler = [](const ApiCallInfo &in, ApiReplyInfo &out) { 709 const auto driverRef = in.callerObjRef_; 710 auto &driver = GetBackendObject<UiDriver>(driverRef); 711 auto filterJson = ReadCallArg<json>(in, INDEX_ZERO); 712 if (filterJson.empty()) { 713 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "WindowFilter cannot be empty"); 714 return; 715 } 716 auto matcher = [&filterJson](const Window &window) -> bool { 717 bool match = true; 718 if (filterJson.contains("bundleName")) { 719 match = match && (filterJson["bundleName"].get<string>() == window.bundleName_); 720 } 721 if (filterJson.contains("title")) { 722 match = match && (filterJson["title"].get<string>() == window.title_); 723 } 724 if (filterJson.contains("focused")) { 725 match = match && (filterJson["focused"].get<bool>() == window.focused_); 726 } 727 if (filterJson.contains("actived")) { 728 match = match && (filterJson["actived"].get<bool>() == window.actived_); 729 } 730 return match; 731 }; 732 auto window = driver.FindWindow(matcher, out.exception_); 733 if (window == nullptr) { 734 out.resultValue_ = nullptr; 735 } else { 736 out.resultValue_ = StoreBackendObject(move(window), driverRef); 737 } 738 }; 739 FrontendApiServer::Get().AddHandler("Driver.findWindow", findWindowHandler); 740 } 741 RegisterUiDriverMiscMethods1()742 static void RegisterUiDriverMiscMethods1() 743 { 744 auto &server = FrontendApiServer::Get(); 745 auto create = [](const ApiCallInfo &in, ApiReplyInfo &out) { 746 auto driver = make_unique<UiDriver>(); 747 if (driver->CheckStatus(true, out.exception_)) { 748 out.resultValue_ = StoreBackendObject(move(driver)); 749 } 750 }; 751 server.AddHandler("Driver.create", create); 752 753 auto createUIEventObserver = [](const ApiCallInfo &in, ApiReplyInfo &out) { 754 auto observer = make_unique<UIEventObserver>(); 755 out.resultValue_ = StoreBackendObject(move(observer), in.callerObjRef_); 756 }; 757 server.AddHandler("Driver.createUIEventObserver", createUIEventObserver); 758 759 auto delay = [](const ApiCallInfo &in, ApiReplyInfo &out) { 760 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 761 driver.DelayMs(ReadCallArg<uint32_t>(in, INDEX_ZERO)); 762 }; 763 server.AddHandler("Driver.delayMs", delay); 764 765 auto screenCap = [](const ApiCallInfo &in, ApiReplyInfo &out) { 766 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 767 auto null = json(); 768 auto rectJson = ReadCallArg<json>(in, INDEX_ONE, null); 769 Rect rect = {0, 0, 0, 0}; 770 if (!rectJson.empty()) { 771 rect = Rect(rectJson["left"], rectJson["right"], rectJson["top"], rectJson["bottom"]); 772 } 773 auto fd = ReadCallArg<uint32_t>(in, INDEX_ZERO); 774 driver.TakeScreenCap(fd, out.exception_, rect); 775 out.resultValue_ = (out.exception_.code_ == NO_ERROR); 776 (void) close(fd); 777 }; 778 server.AddHandler("Driver.screenCap", screenCap); 779 server.AddHandler("Driver.screenCapture", screenCap); 780 } 781 RegisterUiDriverMiscMethods2()782 static void RegisterUiDriverMiscMethods2() 783 { 784 auto &server = FrontendApiServer::Get(); 785 auto pressBack = [](const ApiCallInfo &in, ApiReplyInfo &out) { 786 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 787 UiOpArgs uiOpArgs; 788 driver.TriggerKey(Back(), uiOpArgs, out.exception_); 789 }; 790 server.AddHandler("Driver.pressBack", pressBack); 791 auto pressHome = [](const ApiCallInfo &in, ApiReplyInfo &out) { 792 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 793 UiOpArgs uiOpArgs; 794 driver.TriggerKey(Home(), uiOpArgs, out.exception_); 795 }; 796 server.AddHandler("Driver.pressHome", pressHome); 797 auto triggerKey = [](const ApiCallInfo &in, ApiReplyInfo &out) { 798 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 799 UiOpArgs uiOpArgs; 800 auto key = AnonymousSingleKey(ReadCallArg<int32_t>(in, INDEX_ZERO)); 801 driver.TriggerKey(key, uiOpArgs, out.exception_); 802 }; 803 server.AddHandler("Driver.triggerKey", triggerKey); 804 auto triggerCombineKeys = [](const ApiCallInfo &in, ApiReplyInfo &out) { 805 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 806 UiOpArgs uiOpArgs; 807 auto key0 = ReadCallArg<int32_t>(in, INDEX_ZERO); 808 auto key1 = ReadCallArg<int32_t>(in, INDEX_ONE); 809 auto key2 = ReadCallArg<int32_t>(in, INDEX_TWO, KEYCODE_NONE); 810 auto keyAction = CombinedKeys(key0, key1, key2); 811 driver.TriggerKey(keyAction, uiOpArgs, out.exception_); 812 }; 813 server.AddHandler("Driver.triggerCombineKeys", triggerCombineKeys); 814 } 815 RegisterUiEventObserverMethods()816 static void RegisterUiEventObserverMethods() 817 { 818 static bool observerDelegateRegistered = false; 819 static auto fowarder = std::make_shared<UiEventFowarder>(); 820 auto &server = FrontendApiServer::Get(); 821 auto once = [](const ApiCallInfo &in, ApiReplyInfo &out) { 822 auto &driver = GetBoundUiDriver(in.callerObjRef_); 823 auto event = ReadCallArg<string>(in, INDEX_ZERO); 824 auto cbRef = ReadCallArg<string>(in, INDEX_ONE); 825 fowarder->AddCallbackInfo(move(event), in.callerObjRef_, move(cbRef)); 826 if (!observerDelegateRegistered) { 827 driver.RegisterUiEventListener(fowarder); 828 observerDelegateRegistered = true; 829 } 830 }; 831 server.AddHandler("UIEventObserver.once", once); 832 } 833 CheckSwipeVelocityPps(UiOpArgs & args)834 static void CheckSwipeVelocityPps(UiOpArgs& args) 835 { 836 if (args.swipeVelocityPps_ < args.minSwipeVelocityPps_ || args.swipeVelocityPps_ > args.maxSwipeVelocityPps_) { 837 LOG_W("The swipe velocity out of range"); 838 args.swipeVelocityPps_ = args.defaultSwipeVelocityPps_; 839 } 840 } 841 RegisterUiDriverTouchOperators()842 static void RegisterUiDriverTouchOperators() 843 { 844 auto &server = FrontendApiServer::Get(); 845 auto genericClick = [](const ApiCallInfo &in, ApiReplyInfo &out) { 846 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 847 const auto point0 = Point(ReadCallArg<int32_t>(in, INDEX_ZERO), ReadCallArg<int32_t>(in, INDEX_ONE)); 848 auto point1 = Point(0, 0); 849 UiOpArgs uiOpArgs; 850 auto op = TouchOp::CLICK; 851 if (in.apiId_ == "Driver.longClick") { 852 op = TouchOp::LONG_CLICK; 853 } else if (in.apiId_ == "Driver.doubleClick") { 854 op = TouchOp::DOUBLE_CLICK_P; 855 } else if (in.apiId_ == "Driver.swipe") { 856 op = TouchOp::SWIPE; 857 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_FOUR, uiOpArgs.swipeVelocityPps_); 858 CheckSwipeVelocityPps(uiOpArgs); 859 point1 = Point(ReadCallArg<int32_t>(in, INDEX_TWO), ReadCallArg<int32_t>(in, INDEX_THREE)); 860 } else if (in.apiId_ == "Driver.drag") { 861 op = TouchOp::DRAG; 862 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_FOUR, uiOpArgs.swipeVelocityPps_); 863 CheckSwipeVelocityPps(uiOpArgs); 864 point1 = Point(ReadCallArg<int32_t>(in, INDEX_TWO), ReadCallArg<int32_t>(in, INDEX_THREE)); 865 } 866 if (op == TouchOp::SWIPE || op == TouchOp::DRAG) { 867 auto touch = GenericSwipe(op, point0, point1); 868 driver.PerformTouch(touch, uiOpArgs, out.exception_); 869 } else { 870 auto touch = GenericClick(op, point0); 871 driver.PerformTouch(touch, uiOpArgs, out.exception_); 872 } 873 }; 874 server.AddHandler("Driver.click", genericClick); 875 server.AddHandler("Driver.longClick", genericClick); 876 server.AddHandler("Driver.doubleClick", genericClick); 877 server.AddHandler("Driver.swipe", genericClick); 878 server.AddHandler("Driver.drag", genericClick); 879 } 880 CheckMultiPointerOperatorsPoint(const PointerMatrix & pointer)881 static bool CheckMultiPointerOperatorsPoint(const PointerMatrix& pointer) 882 { 883 for (uint32_t fingerIndex = 0; fingerIndex < pointer.GetFingers(); fingerIndex++) { 884 for (uint32_t stepIndex = 0; stepIndex < pointer.GetSteps(); stepIndex++) { 885 if (pointer.At(fingerIndex, stepIndex).flags_ != 1) { 886 return false; 887 } 888 } 889 } 890 return true; 891 } 892 CreateFlingPoint(Point & to,Point & from,Point screenSize,Direction direction)893 static void CreateFlingPoint(Point &to, Point &from, Point screenSize, Direction direction) 894 { 895 to = Point(screenSize.px_ / INDEX_TWO, screenSize.py_ / INDEX_TWO); 896 switch (direction) { 897 case TO_LEFT: 898 from.px_ = to.px_ - screenSize.px_ / INDEX_FOUR; 899 from.py_ = to.py_; 900 break; 901 case TO_RIGHT: 902 from.px_ = to.px_ + screenSize.px_ / INDEX_FOUR; 903 from.py_ = to.py_; 904 break; 905 case TO_UP: 906 from.px_ = to.px_; 907 from.py_ = to.py_ - screenSize.py_ / INDEX_FOUR; 908 break; 909 case TO_DOWN: 910 from.px_ = to.px_; 911 from.py_ = to.py_ + screenSize.py_ / INDEX_FOUR; 912 break; 913 default: 914 break; 915 } 916 } 917 RegisterUiDriverFlingOperators()918 static void RegisterUiDriverFlingOperators() 919 { 920 auto &server = FrontendApiServer::Get(); 921 auto genericFling = [](const ApiCallInfo &in, ApiReplyInfo &out) { 922 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 923 UiOpArgs uiOpArgs; 924 auto op = TouchOp::SWIPE; 925 auto params = in.paramList_; 926 Point from; 927 Point to; 928 if (params.size() == INDEX_TWO) { 929 auto screenSize = driver.GetDisplaySize(out.exception_); 930 auto direction = ReadCallArg<Direction>(in, INDEX_ZERO); 931 CreateFlingPoint(to, from, screenSize, direction); 932 uiOpArgs.swipeStepsCounts_ = INDEX_TWO; 933 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_ONE); 934 } else { 935 auto pointJson0 = ReadCallArg<json>(in, INDEX_ZERO); 936 auto pointJson1 = ReadCallArg<json>(in, INDEX_ONE); 937 if (pointJson0.empty() || pointJson1.empty()) { 938 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Point cannot be empty"); 939 return; 940 } 941 from = Point(pointJson0["x"], pointJson0["y"]); 942 to = Point(pointJson1["x"], pointJson1["y"]); 943 auto stepLength = ReadCallArg<uint32_t>(in, INDEX_TWO); 944 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_THREE); 945 const int32_t distanceX = to.px_ - from.px_; 946 const int32_t distanceY = to.py_ - from.py_; 947 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 948 if (stepLength <= 0 || stepLength > distance) { 949 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "The stepLen is out of range"); 950 return; 951 } 952 uiOpArgs.swipeStepsCounts_ = distance / stepLength; 953 } 954 CheckSwipeVelocityPps(uiOpArgs); 955 auto touch = GenericSwipe(op, from, to); 956 driver.PerformTouch(touch, uiOpArgs, out.exception_); 957 }; 958 server.AddHandler("Driver.fling", genericFling); 959 } 960 RegisterUiDriverMultiPointerOperators()961 static void RegisterUiDriverMultiPointerOperators() 962 { 963 auto &server = FrontendApiServer::Get(); 964 auto multiPointerAction = [](const ApiCallInfo &in, ApiReplyInfo &out) { 965 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 966 auto &pointer = GetBackendObject<PointerMatrix>(ReadCallArg<string>(in, INDEX_ZERO)); 967 auto flag = CheckMultiPointerOperatorsPoint(pointer); 968 if (!flag) { 969 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "There is not all coordinate points are set"); 970 return; 971 } 972 UiOpArgs uiOpArgs; 973 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_ONE, uiOpArgs.swipeVelocityPps_); 974 CheckSwipeVelocityPps(uiOpArgs); 975 auto touch = MultiPointerAction(pointer); 976 driver.PerformTouch(touch, uiOpArgs, out.exception_); 977 out.resultValue_ = (out.exception_.code_ == NO_ERROR); 978 }; 979 server.AddHandler("Driver.injectMultiPointerAction", multiPointerAction); 980 } 981 RegisterUiDriverMouseOperators()982 static void RegisterUiDriverMouseOperators() 983 { 984 auto &server = FrontendApiServer::Get(); 985 auto mouseClick = [](const ApiCallInfo &in, ApiReplyInfo &out) { 986 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 987 MouseOpArgs mouseOpArgs; 988 auto pointJson = ReadCallArg<json>(in, INDEX_ZERO); 989 mouseOpArgs.point_ = Point(pointJson["x"], pointJson["y"]); 990 mouseOpArgs.button_ = ReadCallArg<MouseButton>(in, INDEX_ONE); 991 mouseOpArgs.key1_ = ReadCallArg<int32_t>(in, INDEX_TWO, KEYCODE_NONE); 992 mouseOpArgs.key2_ = ReadCallArg<int32_t>(in, INDEX_THREE, KEYCODE_NONE); 993 mouseOpArgs.action_ = MouseOp::M_CLICK; 994 driver.InjectMouseAction(mouseOpArgs, out.exception_); 995 }; 996 server.AddHandler("Driver.mouseClick", mouseClick); 997 998 auto mouseMove = [](const ApiCallInfo &in, ApiReplyInfo &out) { 999 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 1000 MouseOpArgs mouseOpArgs; 1001 auto pointJson = ReadCallArg<json>(in, INDEX_ZERO); 1002 mouseOpArgs.point_ = Point(pointJson["x"], pointJson["y"]); 1003 mouseOpArgs.action_ = MouseOp::M_MOVETO; 1004 driver.InjectMouseAction(mouseOpArgs, out.exception_); 1005 }; 1006 server.AddHandler("Driver.mouseMoveTo", mouseMove); 1007 1008 auto mouseScroll = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1009 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 1010 MouseOpArgs mouseOpArgs; 1011 auto pointJson = ReadCallArg<json>(in, INDEX_ZERO); 1012 mouseOpArgs.point_ = Point(pointJson["x"], pointJson["y"]); 1013 mouseOpArgs.adown_ = ReadCallArg<bool>(in, INDEX_ONE); 1014 mouseOpArgs.scrollValue_ = ReadCallArg<int32_t>(in, INDEX_TWO); 1015 mouseOpArgs.key1_ = ReadCallArg<int32_t>(in, INDEX_THREE, KEYCODE_NONE); 1016 mouseOpArgs.key2_ = ReadCallArg<int32_t>(in, INDEX_FOUR, KEYCODE_NONE); 1017 mouseOpArgs.action_ = MouseOp::M_SCROLL; 1018 driver.InjectMouseAction(mouseOpArgs, out.exception_); 1019 }; 1020 server.AddHandler("Driver.mouseScroll", mouseScroll); 1021 } 1022 RegisterUiDriverDisplayOperators()1023 static void RegisterUiDriverDisplayOperators() 1024 { 1025 auto &server = FrontendApiServer::Get(); 1026 auto genericDisplayOperator = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1027 auto &driver = GetBackendObject<UiDriver>(in.callerObjRef_); 1028 if (in.apiId_ == "Driver.setDisplayRotation") { 1029 auto rotation = ReadCallArg<DisplayRotation>(in, INDEX_ZERO); 1030 driver.SetDisplayRotation(rotation, out.exception_); 1031 } else if (in.apiId_ == "Driver.getDisplayRotation") { 1032 out.resultValue_ = driver.GetDisplayRotation(out.exception_); 1033 } else if (in.apiId_ == "Driver.setDisplayRotationEnabled") { 1034 auto enabled = ReadCallArg<bool>(in, INDEX_ZERO); 1035 driver.SetDisplayRotationEnabled(enabled, out.exception_); 1036 } else if (in.apiId_ == "Driver.waitForIdle") { 1037 auto idleTime = ReadCallArg<uint32_t>(in, INDEX_ZERO); 1038 auto timeout = ReadCallArg<uint32_t>(in, INDEX_ONE); 1039 out.resultValue_ = driver.WaitForUiSteady(idleTime, timeout, out.exception_); 1040 } else if (in.apiId_ == "Driver.wakeUpDisplay") { 1041 driver.WakeUpDisplay(out.exception_); 1042 } else if (in.apiId_ == "Driver.getDisplaySize") { 1043 auto result = driver.GetDisplaySize(out.exception_); 1044 json data; 1045 data["x"] = result.px_; 1046 data["y"] = result.py_; 1047 out.resultValue_ = data; 1048 } else if (in.apiId_ == "Driver.getDisplayDensity") { 1049 auto result = driver.GetDisplayDensity(out.exception_); 1050 json data; 1051 data["x"] = result.px_; 1052 data["y"] = result.py_; 1053 out.resultValue_ = data; 1054 } 1055 }; 1056 server.AddHandler("Driver.setDisplayRotation", genericDisplayOperator); 1057 server.AddHandler("Driver.getDisplayRotation", genericDisplayOperator); 1058 server.AddHandler("Driver.setDisplayRotationEnabled", genericDisplayOperator); 1059 server.AddHandler("Driver.waitForIdle", genericDisplayOperator); 1060 server.AddHandler("Driver.wakeUpDisplay", genericDisplayOperator); 1061 server.AddHandler("Driver.getDisplaySize", genericDisplayOperator); 1062 server.AddHandler("Driver.getDisplayDensity", genericDisplayOperator); 1063 } 1064 1065 template <UiAttr kAttr, bool kString = false> GenericComponentAttrGetter(const ApiCallInfo & in,ApiReplyInfo & out)1066 static void GenericComponentAttrGetter(const ApiCallInfo &in, ApiReplyInfo &out) 1067 { 1068 constexpr auto attrName = ATTR_NAMES[kAttr]; 1069 auto &image = GetBackendObject<Widget>(in.callerObjRef_); 1070 auto &driver = GetBoundUiDriver(in.callerObjRef_); 1071 auto snapshot = driver.RetrieveWidget(image, out.exception_); 1072 if (out.exception_.code_ != NO_ERROR) { 1073 out.resultValue_ = nullptr; // exception, return null 1074 return; 1075 } 1076 if (attrName == ATTR_NAMES[UiAttr::BOUNDSCENTER]) { // getBoundsCenter 1077 const auto bounds = snapshot->GetBounds(); 1078 json data; 1079 data["x"] = bounds.GetCenterX(); 1080 data["y"] = bounds.GetCenterY(); 1081 out.resultValue_ = data; 1082 return; 1083 } 1084 if (attrName == ATTR_NAMES[UiAttr::BOUNDS]) { // getBounds 1085 const auto bounds = snapshot->GetBounds(); 1086 json data; 1087 data["left"] = bounds.left_; 1088 data["top"] = bounds.top_; 1089 data["right"] = bounds.right_; 1090 data["bottom"] = bounds.bottom_; 1091 out.resultValue_ = data; 1092 return; 1093 } 1094 // convert value-string to json value of target type 1095 auto attrValue = snapshot->GetAttr(attrName, "NA"); 1096 if (attrValue == "NA") { 1097 out.resultValue_ = nullptr; // no such attribute, return null 1098 } else if (kString) { 1099 out.resultValue_ = attrValue; 1100 } else { 1101 out.resultValue_ = nlohmann::json::parse(attrValue); 1102 } 1103 } 1104 RegisterUiComponentAttrGetters()1105 static void RegisterUiComponentAttrGetters() 1106 { 1107 auto &server = FrontendApiServer::Get(); 1108 server.AddHandler("Component.getAccessibilityId", GenericComponentAttrGetter<UiAttr::ACCESSIBILITY_ID>); 1109 server.AddHandler("Component.getText", GenericComponentAttrGetter<UiAttr::TEXT, true>); 1110 server.AddHandler("Component.getId", GenericComponentAttrGetter<UiAttr::ID, true>); 1111 server.AddHandler("Component.getType", GenericComponentAttrGetter<UiAttr::TYPE, true>); 1112 server.AddHandler("Component.isEnabled", GenericComponentAttrGetter<UiAttr::ENABLED>); 1113 server.AddHandler("Component.isFocused", GenericComponentAttrGetter<UiAttr::FOCUSED>); 1114 server.AddHandler("Component.isSelected", GenericComponentAttrGetter<UiAttr::SELECTED>); 1115 server.AddHandler("Component.isClickable", GenericComponentAttrGetter<UiAttr::CLICKABLE>); 1116 server.AddHandler("Component.isLongClickable", GenericComponentAttrGetter<UiAttr::LONG_CLICKABLE>); 1117 server.AddHandler("Component.isScrollable", GenericComponentAttrGetter<UiAttr::SCROLLABLE>); 1118 server.AddHandler("Component.isCheckable", GenericComponentAttrGetter<UiAttr::CHECKABLE>); 1119 server.AddHandler("Component.isChecked", GenericComponentAttrGetter<UiAttr::CHECKED>); 1120 server.AddHandler("Component.getBounds", GenericComponentAttrGetter<UiAttr::BOUNDS>); 1121 server.AddHandler("Component.getBoundsCenter", GenericComponentAttrGetter<UiAttr::BOUNDSCENTER>); 1122 } 1123 RegisterUiComponentOperators()1124 static void RegisterUiComponentOperators() 1125 { 1126 auto &server = FrontendApiServer::Get(); 1127 auto genericOperationHandler = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1128 auto &widget = GetBackendObject<Widget>(in.callerObjRef_); 1129 auto &driver = GetBoundUiDriver(in.callerObjRef_); 1130 UiOpArgs uiOpArgs; 1131 auto wOp = WidgetOperator(driver, widget, uiOpArgs); 1132 if (in.apiId_ == "Component.click") { 1133 wOp.GenericClick(TouchOp::CLICK, out.exception_); 1134 } else if (in.apiId_ == "Component.longClick") { 1135 wOp.GenericClick(TouchOp::LONG_CLICK, out.exception_); 1136 } else if (in.apiId_ == "Component.doubleClick") { 1137 wOp.GenericClick(TouchOp::DOUBLE_CLICK_P, out.exception_); 1138 } else if (in.apiId_ == "Component.scrollToTop") { 1139 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_ZERO, uiOpArgs.swipeVelocityPps_); 1140 CheckSwipeVelocityPps(uiOpArgs); 1141 wOp.ScrollToEnd(true, out.exception_); 1142 } else if (in.apiId_ == "Component.scrollToBottom") { 1143 uiOpArgs.swipeVelocityPps_ = ReadCallArg<uint32_t>(in, INDEX_ZERO, uiOpArgs.swipeVelocityPps_); 1144 CheckSwipeVelocityPps(uiOpArgs); 1145 wOp.ScrollToEnd(false, out.exception_); 1146 } else if (in.apiId_ == "Component.dragTo") { 1147 auto &widgetTo = GetBackendObject<Widget>(ReadCallArg<string>(in, INDEX_ZERO)); 1148 wOp.DragIntoWidget(widgetTo, out.exception_); 1149 } else if (in.apiId_ == "Component.inputText") { 1150 wOp.InputText(ReadCallArg<string>(in, INDEX_ZERO), out.exception_); 1151 } else if (in.apiId_ == "Component.clearText") { 1152 wOp.InputText("", out.exception_); 1153 } else if (in.apiId_ == "Component.scrollSearch") { 1154 auto &selector = GetBackendObject<WidgetSelector>(ReadCallArg<string>(in, INDEX_ZERO)); 1155 auto res = wOp.ScrollFindWidget(selector, out.exception_); 1156 if (res != nullptr) { 1157 out.resultValue_ = StoreBackendObject(move(res), sDriverBindingMap.find(in.callerObjRef_)->second); 1158 } 1159 } else if (in.apiId_ == "Component.pinchOut" || in.apiId_ == "Component.pinchIn") { 1160 auto pinchScale = ReadCallArg<float_t>(in, INDEX_ZERO); 1161 wOp.PinchWidget(pinchScale, out.exception_); 1162 } 1163 }; 1164 server.AddHandler("Component.click", genericOperationHandler); 1165 server.AddHandler("Component.longClick", genericOperationHandler); 1166 server.AddHandler("Component.doubleClick", genericOperationHandler); 1167 server.AddHandler("Component.scrollToTop", genericOperationHandler); 1168 server.AddHandler("Component.scrollToBottom", genericOperationHandler); 1169 server.AddHandler("Component.dragTo", genericOperationHandler); 1170 server.AddHandler("Component.inputText", genericOperationHandler); 1171 server.AddHandler("Component.clearText", genericOperationHandler); 1172 server.AddHandler("Component.scrollSearch", genericOperationHandler); 1173 server.AddHandler("Component.pinchOut", genericOperationHandler); 1174 server.AddHandler("Component.pinchIn", genericOperationHandler); 1175 } 1176 RegisterUiWindowAttrGetters()1177 static void RegisterUiWindowAttrGetters() 1178 { 1179 auto &server = FrontendApiServer::Get(); 1180 auto genericGetter = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1181 auto &window = GetBackendObject<Window>(in.callerObjRef_); 1182 auto &driver = GetBoundUiDriver(in.callerObjRef_); 1183 auto snapshot = driver.RetrieveWindow(window, out.exception_); 1184 if (out.exception_.code_ != NO_ERROR) { 1185 out.resultValue_ = nullptr; // exception, return null 1186 return; 1187 } 1188 if (in.apiId_ == "UiWindow.getBundleName") { 1189 out.resultValue_ = snapshot->bundleName_; 1190 } else if (in.apiId_ == "UiWindow.getBounds") { 1191 json data; 1192 data["left"] = snapshot->bounds_.left_; 1193 data["top"] = snapshot->bounds_.top_; 1194 data["right"] = snapshot->bounds_.right_; 1195 data["bottom"] = snapshot->bounds_.bottom_; 1196 out.resultValue_ = data; 1197 } else if (in.apiId_ == "UiWindow.getTitle") { 1198 out.resultValue_ = snapshot->title_; 1199 } else if (in.apiId_ == "UiWindow.getWindowMode") { 1200 out.resultValue_ = (uint8_t)(snapshot->mode_ - 1); 1201 } else if (in.apiId_ == "UiWindow.isFocused") { 1202 out.resultValue_ = snapshot->focused_; 1203 } else if (in.apiId_ == "UiWindow.isActived") { 1204 out.resultValue_ = snapshot->actived_; 1205 } 1206 }; 1207 server.AddHandler("UiWindow.getBundleName", genericGetter); 1208 server.AddHandler("UiWindow.getBounds", genericGetter); 1209 server.AddHandler("UiWindow.getTitle", genericGetter); 1210 server.AddHandler("UiWindow.getWindowMode", genericGetter); 1211 server.AddHandler("UiWindow.isFocused", genericGetter); 1212 server.AddHandler("UiWindow.isActived", genericGetter); 1213 } 1214 RegisterUiWindowOperators()1215 static void RegisterUiWindowOperators() 1216 { 1217 auto &server = FrontendApiServer::Get(); 1218 auto genericWinOperationHandler = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1219 auto &window = GetBackendObject<Window>(in.callerObjRef_); 1220 auto &driver = GetBoundUiDriver(in.callerObjRef_); 1221 UiOpArgs uiOpArgs; 1222 auto wOp = WindowOperator(driver, window, uiOpArgs); 1223 auto action = in.apiId_; 1224 if (action == "UiWindow.resize") { 1225 auto width = ReadCallArg<uint32_t>(in, INDEX_ZERO); 1226 auto highth = ReadCallArg<uint32_t>(in, INDEX_ONE); 1227 auto direction = ReadCallArg<ResizeDirection>(in, INDEX_TWO); 1228 if ((((direction == LEFT) || (direction == RIGHT)) && highth != window.bounds_.GetHeight()) || 1229 (((direction == D_UP) || (direction == D_DOWN)) && width != window.bounds_.GetWidth())) { 1230 out.exception_ = ApiCallErr(ERR_OPERATION_UNSUPPORTED, "Resize cannot be done in this direction"); 1231 return; 1232 } 1233 wOp.Resize(width, highth, direction, out); 1234 } else if (action == "UiWindow.focus") { 1235 wOp.Focus(out); 1236 } 1237 }; 1238 server.AddHandler("UiWindow.focus", genericWinOperationHandler); 1239 server.AddHandler("UiWindow.resize", genericWinOperationHandler); 1240 } 1241 RegisterUiWinBarOperators()1242 static void RegisterUiWinBarOperators() 1243 { 1244 auto &server = FrontendApiServer::Get(); 1245 auto genericWinBarOperationHandler = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1246 auto &window = GetBackendObject<Window>(in.callerObjRef_); 1247 auto &driver = GetBoundUiDriver(in.callerObjRef_); 1248 UiOpArgs uiOpArgs; 1249 auto wOp = WindowOperator(driver, window, uiOpArgs); 1250 auto action = in.apiId_; 1251 if (window.decoratorEnabled_) { 1252 if (action == "UiWindow.split") { 1253 wOp.Split(out); 1254 } else if (action == "UiWindow.maximize") { 1255 wOp.Maximize(out); 1256 } else if (action == "UiWindow.resume") { 1257 wOp.Resume(out); 1258 } else if (action == "UiWindow.minimize") { 1259 wOp.Minimize(out); 1260 } else if (action == "UiWindow.close") { 1261 wOp.Close(out); 1262 } else if (action == "UiWindow.moveTo") { 1263 auto endX = ReadCallArg<uint32_t>(in, INDEX_ZERO); 1264 auto endY = ReadCallArg<uint32_t>(in, INDEX_ONE); 1265 wOp.MoveTo(endX, endY, out); 1266 } 1267 } else { 1268 out.exception_ = ApiCallErr(ERR_OPERATION_UNSUPPORTED, "this device can not support this action"); 1269 } 1270 }; 1271 server.AddHandler("UiWindow.split", genericWinBarOperationHandler); 1272 server.AddHandler("UiWindow.maximize", genericWinBarOperationHandler); 1273 server.AddHandler("UiWindow.resume", genericWinBarOperationHandler); 1274 server.AddHandler("UiWindow.minimize", genericWinBarOperationHandler); 1275 server.AddHandler("UiWindow.close", genericWinBarOperationHandler); 1276 server.AddHandler("UiWindow.moveTo", genericWinBarOperationHandler); 1277 } 1278 RegisterPointerMatrixOperators()1279 static void RegisterPointerMatrixOperators() 1280 { 1281 auto &server = FrontendApiServer::Get(); 1282 auto create = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1283 UiOpArgs uiOpArgs; 1284 auto finger = ReadCallArg<uint32_t>(in, INDEX_ZERO); 1285 if (finger < 1 || finger > uiOpArgs.maxMultiTouchFingers) { 1286 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Number of illegal fingers"); 1287 return; 1288 } 1289 auto step = ReadCallArg<uint32_t>(in, INDEX_ONE); 1290 if (step < 1 || step > uiOpArgs.maxMultiTouchSteps) { 1291 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Number of illegal steps"); 1292 return; 1293 } 1294 out.resultValue_ = StoreBackendObject(make_unique<PointerMatrix>(finger, step)); 1295 }; 1296 server.AddHandler("PointerMatrix.create", create); 1297 1298 auto setPoint = [](const ApiCallInfo &in, ApiReplyInfo &out) { 1299 auto &pointer = GetBackendObject<PointerMatrix>(in.callerObjRef_); 1300 auto finger = ReadCallArg<uint32_t>(in, INDEX_ZERO); 1301 if (finger < 0 || finger >= pointer.GetFingers()) { 1302 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Number of illegal fingers"); 1303 return; 1304 } 1305 auto step = ReadCallArg<uint32_t>(in, INDEX_ONE); 1306 if (step < 0 || step >= pointer.GetSteps()) { 1307 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Number of illegal steps"); 1308 return; 1309 } 1310 auto pointJson = ReadCallArg<json>(in, INDEX_TWO); 1311 if (pointJson.empty()) { 1312 out.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Point cannot be empty"); 1313 return; 1314 } 1315 const auto point = Point(pointJson["x"], pointJson["y"]); 1316 pointer.At(finger, step).point_ = point; 1317 pointer.At(finger, step).flags_ = 1; 1318 }; 1319 server.AddHandler("PointerMatrix.setPoint", setPoint); 1320 } 1321 1322 /** Register frontendApiHandlers and preprocessors on startup.*/ RegisterApiHandlers()1323 __attribute__((constructor)) static void RegisterApiHandlers() 1324 { 1325 auto &server = FrontendApiServer::Get(); 1326 server.AddCommonPreprocessor("APiCallInfoChecker", APiCallInfoChecker); 1327 server.AddHandler("BackendObjectsCleaner", BackendObjectsCleaner); 1328 RegisterOnBuilders(); 1329 RegisterUiDriverComponentFinders(); 1330 RegisterUiDriverWindowFinder(); 1331 RegisterUiDriverMiscMethods1(); 1332 RegisterUiDriverMiscMethods2(); 1333 RegisterUiDriverTouchOperators(); 1334 RegisterUiComponentAttrGetters(); 1335 RegisterUiComponentOperators(); 1336 RegisterUiWindowAttrGetters(); 1337 RegisterUiWindowOperators(); 1338 RegisterUiWinBarOperators(); 1339 RegisterPointerMatrixOperators(); 1340 RegisterUiDriverFlingOperators(); 1341 RegisterUiDriverMultiPointerOperators(); 1342 RegisterUiDriverDisplayOperators(); 1343 RegisterUiDriverMouseOperators(); 1344 RegisterUiEventObserverMethods(); 1345 } 1346 } // namespace OHOS::uitest 1347