1 /*
2 * Copyright (C) 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 "napi_accessibility_extension.h"
17
18 #include <uv.h>
19 #include "accessible_ability_client.h"
20 #include "ability_info.h"
21 #include "hilog_wrapper.h"
22 #include "js_runtime.h"
23 #include "js_runtime_utils.h"
24 #include "napi_accessibility_event_info.h"
25 #include "napi_accessibility_extension_context.h"
26 #include "napi_accessibility_utils.h"
27 #include "napi/native_api.h"
28 #include "napi/native_node_api.h"
29 #include "napi_accessibility_element.h"
30
31 using namespace OHOS::AbilityRuntime;
32 using namespace OHOS::AccessibilityNapi;
33
34 namespace OHOS {
35 namespace Accessibility {
36 namespace {
37 constexpr int32_t VIRTUAL_COMPONENT_ID = -1;
38 }
Create(const std::unique_ptr<AbilityRuntime::Runtime> & runtime)39 NAccessibilityExtension* NAccessibilityExtension::Create(const std::unique_ptr<AbilityRuntime::Runtime>& runtime)
40 {
41 HILOG_INFO();
42 return new(std::nothrow) NAccessibilityExtension(static_cast<AbilityRuntime::JsRuntime&>(*runtime));
43 }
44
NAccessibilityExtension(AbilityRuntime::JsRuntime & jsRuntime)45 NAccessibilityExtension::NAccessibilityExtension(AbilityRuntime::JsRuntime& jsRuntime) : jsRuntime_(jsRuntime)
46 {
47 listener_ = std::make_shared<AbilityListener>(*this);
48
49 HandleScope handleScope(jsRuntime_);
50 engine_ = &jsRuntime_.GetNativeEngine();
51 }
52
53 NAccessibilityExtension::~NAccessibilityExtension() = default;
54
Init(const std::shared_ptr<AppExecFwk::AbilityLocalRecord> & record,const std::shared_ptr<AppExecFwk::OHOSApplication> & application,std::shared_ptr<AppExecFwk::AbilityHandler> & handler,const sptr<IRemoteObject> & token)55 void NAccessibilityExtension::Init(const std::shared_ptr<AppExecFwk::AbilityLocalRecord> &record,
56 const std::shared_ptr<AppExecFwk::OHOSApplication> &application,
57 std::shared_ptr<AppExecFwk::AbilityHandler> &handler, const sptr<IRemoteObject> &token)
58 {
59 HILOG_INFO();
60 AccessibilityExtension::Init(record, application, handler, token);
61 std::string srcPath = "";
62 GetSrcPath(srcPath);
63 if (srcPath.empty()) {
64 HILOG_ERROR("Failed to get srcPath");
65 return;
66 }
67
68 if (!abilityInfo_) {
69 HILOG_ERROR("abilityInfo_ is nullptr.");
70 return;
71 }
72 std::string moduleName(Extension::abilityInfo_->moduleName);
73 moduleName.append("::").append(abilityInfo_->name);
74 HILOG_INFO("moduleName:%{public}s, srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
75
76 jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath);
77 if (!jsObj_) {
78 HILOG_ERROR("Failed to get jsObj_");
79 return;
80 }
81 NativeObject* obj = ConvertNativeValueTo<NativeObject>(jsObj_->Get());
82 if (!obj) {
83 HILOG_ERROR("Failed to get NAccessibilityExtension object");
84 return;
85 }
86
87 auto context = GetContext();
88 if (!context) {
89 HILOG_ERROR("Failed to get context");
90 return;
91 }
92 NativeValue* contextObj = CreateJsAccessibilityExtensionContext(*engine_, context);
93 auto shellContextRef = jsRuntime_.LoadSystemModule("application.AccessibilityExtensionContext", &contextObj, 1);
94 if (!shellContextRef) {
95 HILOG_ERROR("shellContextRef is nullptr.");
96 return;
97 }
98 contextObj = shellContextRef->Get();
99 context->Bind(jsRuntime_, shellContextRef.release());
100 obj->SetProperty("context", contextObj);
101
102 auto nativeObj = ConvertNativeValueTo<NativeObject>(contextObj);
103 if (!nativeObj) {
104 HILOG_ERROR("Failed to get accessibility extension native object");
105 return;
106 }
107 nativeObj->SetNativePointer(new std::weak_ptr<AbilityRuntime::Context>(context),
108 [](NativeEngine*, void* data, void*) {
109 delete static_cast<std::weak_ptr<AbilityRuntime::Context>*>(data);
110 }, nullptr);
111 NAccessibilityElement::DefineJSAccessibilityElement(reinterpret_cast<napi_env>(engine_));
112 }
113
OnConnect(const AAFwk::Want & want)114 sptr<IRemoteObject> NAccessibilityExtension::OnConnect(const AAFwk::Want &want)
115 {
116 HILOG_INFO();
117 Extension::OnConnect(want);
118 sptr<AccessibleAbilityClient> aaClient = AccessibleAbilityClient::GetInstance();
119 if (!aaClient) {
120 HILOG_ERROR("aaClient is nullptr");
121 return nullptr;
122 }
123 aaClient->RegisterAbilityListener(listener_);
124 return aaClient->GetRemoteObject();
125 }
126
OnAbilityConnected()127 void NAccessibilityExtension::OnAbilityConnected()
128 {
129 HILOG_INFO();
130 uv_loop_t *loop = engine_->GetUVLoop();
131 ExtensionCallbackInfo *callbackInfo = new(std::nothrow) ExtensionCallbackInfo();
132 if (!callbackInfo) {
133 HILOG_ERROR("Failed to create callbackInfo.");
134 return;
135 }
136 callbackInfo->extension_ = this;
137 uv_work_t *work = new(std::nothrow) uv_work_t;
138 if (!work) {
139 HILOG_ERROR("Failed to create data.");
140 delete callbackInfo;
141 callbackInfo = nullptr;
142 return;
143 }
144 work->data = static_cast<void*>(callbackInfo);
145
146 int ret = uv_queue_work(
147 loop,
148 work,
149 [](uv_work_t *work) {},
150 [](uv_work_t *work, int status) {
151 ExtensionCallbackInfo *data = static_cast<ExtensionCallbackInfo*>(work->data);
152 data->extension_->CallObjectMethod("onConnect");
153 delete data;
154 data = nullptr;
155 delete work;
156 work = nullptr;
157 });
158 if (ret) {
159 HILOG_ERROR("Failed to execute OnAbilityConnected work queue");
160 delete callbackInfo;
161 callbackInfo = nullptr;
162 delete work;
163 work = nullptr;
164 }
165 HILOG_INFO("end.");
166 }
167
OnAbilityDisconnected()168 void NAccessibilityExtension::OnAbilityDisconnected()
169 {
170 HILOG_INFO();
171 uv_loop_t *loop = engine_->GetUVLoop();
172 ExtensionCallbackInfo *callbackInfo = new(std::nothrow) ExtensionCallbackInfo();
173 if (!callbackInfo) {
174 HILOG_ERROR("Failed to create callbackInfo.");
175 return;
176 }
177 callbackInfo->extension_ = this;
178 uv_work_t *work = new(std::nothrow) uv_work_t;
179 if (!work) {
180 HILOG_ERROR("Failed to create data.");
181 delete callbackInfo;
182 callbackInfo = nullptr;
183 return;
184 }
185 work->data = static_cast<void*>(callbackInfo);
186
187 int ret = uv_queue_work(
188 loop,
189 work,
190 [](uv_work_t *work) {},
191 [](uv_work_t *work, int status) {
192 ExtensionCallbackInfo *data = static_cast<ExtensionCallbackInfo*>(work->data);
193 data->extension_->CallObjectMethod("onDisconnect");
194 delete data;
195 data = nullptr;
196 delete work;
197 work = nullptr;
198 });
199 if (ret) {
200 HILOG_ERROR("Failed to execute OnAbilityDisconnected work queue");
201 delete callbackInfo;
202 callbackInfo = nullptr;
203 delete work;
204 work = nullptr;
205 }
206 HILOG_INFO("end.");
207 }
208
GetElement(const AccessibilityEventInfo & eventInfo)209 std::shared_ptr<AccessibilityElement> NAccessibilityExtension::GetElement(const AccessibilityEventInfo& eventInfo)
210 {
211 HILOG_DEBUG();
212
213 sptr<AccessibleAbilityClient> aaClient = AccessibleAbilityClient::GetInstance();
214 if (!aaClient) {
215 return nullptr;
216 }
217 int32_t componentId = eventInfo.GetAccessibilityId();
218 int32_t windowId = eventInfo.GetWindowId();
219 std::shared_ptr<AccessibilityElement> element = nullptr;
220 if (componentId > 0) {
221 std::shared_ptr<AccessibilityElementInfo> elementInfo = std::make_shared<AccessibilityElementInfo>();
222 if (aaClient->GetSource(eventInfo, *elementInfo) == RET_OK) {
223 element = std::make_shared<AccessibilityElement>(elementInfo);
224 }
225 } else if (windowId > 0) {
226 std::shared_ptr<AccessibilityWindowInfo> windowInfo = std::make_shared<AccessibilityWindowInfo>();
227 if (aaClient->GetWindow(windowId, *windowInfo) == RET_OK) {
228 element = std::make_shared<AccessibilityElement>(windowInfo);
229 }
230 } else {
231 std::shared_ptr<AccessibilityElementInfo> elementInfo = std::make_shared<AccessibilityElementInfo>();
232 CreateElementInfoByEventInfo(eventInfo, elementInfo);
233 element = std::make_shared<AccessibilityElement>(elementInfo);
234 }
235 return element;
236 }
237
CreateElementInfoByEventInfo(const AccessibilityEventInfo & eventInfo,const std::shared_ptr<AccessibilityElementInfo> & elementInfo)238 void NAccessibilityExtension::CreateElementInfoByEventInfo(const AccessibilityEventInfo& eventInfo,
239 const std::shared_ptr<AccessibilityElementInfo> &elementInfo)
240 {
241 HILOG_DEBUG();
242 if (!elementInfo) {
243 return;
244 }
245 elementInfo->SetComponentId(VIRTUAL_COMPONENT_ID);
246 elementInfo->SetBundleName(eventInfo.GetBundleName());
247 elementInfo->SetComponentType(eventInfo.GetComponentType());
248 elementInfo->SetPageId(eventInfo.GetPageId());
249 elementInfo->SetDescriptionInfo(eventInfo.GetDescription());
250 elementInfo->SetTriggerAction(eventInfo.GetTriggerAction());
251 elementInfo->SetTextMovementStep(eventInfo.GetTextMovementStep());
252 elementInfo->SetContentList(eventInfo.GetContentList());
253 elementInfo->SetLatestContent(eventInfo.GetLatestContent());
254 elementInfo->SetBeginIndex(eventInfo.GetBeginIndex());
255 elementInfo->SetCurrentIndex(eventInfo.GetCurrentIndex());
256 elementInfo->SetEndIndex(eventInfo.GetEndIndex());
257 elementInfo->SetItemCounts(eventInfo.GetItemCounts());
258 }
259
ConvertAccessibilityElementToJS(napi_env env,napi_value objEventInfo,const std::shared_ptr<AccessibilityElement> & element)260 void ConvertAccessibilityElementToJS(napi_env env, napi_value objEventInfo,
261 const std::shared_ptr<AccessibilityElement>& element)
262 {
263 HILOG_DEBUG();
264 if (!element) {
265 HILOG_DEBUG("No element information.");
266 return;
267 }
268 AccessibilityElement* pAccessibilityElement = new(std::nothrow) AccessibilityElement(*element);
269 if (!pAccessibilityElement) {
270 HILOG_ERROR("Failed to create AccessibilityElement.");
271 return;
272 }
273 napi_value nTargetObject = nullptr;
274 napi_value constructor = nullptr;
275 napi_get_reference_value(env, NAccessibilityElement::consRef_, &constructor);
276 napi_new_instance(env, constructor, 0, nullptr, &nTargetObject);
277 // Bind js object to a Native object
278 napi_status sts = napi_wrap(
279 env,
280 nTargetObject,
281 pAccessibilityElement,
282 [](napi_env env, void* data, void* hint) {
283 AccessibilityElement* info = static_cast<AccessibilityElement*>(data);
284 delete info;
285 info = nullptr;
286 },
287 nullptr,
288 nullptr);
289 HILOG_DEBUG("napi_wrap status: %{public}d", (int)sts);
290 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, objEventInfo, "target", nTargetObject));
291 }
292
OnAccessibilityEvent(const AccessibilityEventInfo & eventInfo)293 void NAccessibilityExtension::OnAccessibilityEvent(const AccessibilityEventInfo& eventInfo)
294 {
295 HILOG_INFO();
296 std::string strType = "";
297 ConvertEventTypeToString(eventInfo, strType);
298 if (strType.empty()) {
299 HILOG_DEBUG("eventType is invalid.");
300 return;
301 }
302 uv_loop_t *loop = engine_->GetUVLoop();
303 AccessibilityEventInfoCallbackInfo *callbackInfo = new(std::nothrow) AccessibilityEventInfoCallbackInfo();
304 if (!callbackInfo) {
305 HILOG_ERROR("Failed to create callbackInfo.");
306 return;
307 }
308 callbackInfo->engine_ = engine_;
309 callbackInfo->extension_ = this;
310 callbackInfo->eventType_ = strType;
311 callbackInfo->timeStamp_ = eventInfo.GetTimeStamp();
312 callbackInfo->element_ = GetElement(eventInfo);
313 uv_work_t *work = new(std::nothrow) uv_work_t;
314 if (!work) {
315 HILOG_ERROR("Failed to create data.");
316 delete callbackInfo;
317 callbackInfo = nullptr;
318 return;
319 }
320 work->data = static_cast<void*>(callbackInfo);
321 int ret = uv_queue_work(
322 loop,
323 work,
324 [](uv_work_t *work) {},
325 [](uv_work_t *work, int status) {
326 AccessibilityEventInfoCallbackInfo *data = static_cast<AccessibilityEventInfoCallbackInfo*>(work->data);
327 napi_value napiEventInfo = nullptr;
328 napi_create_object(reinterpret_cast<napi_env>(data->engine_), &napiEventInfo);
329
330 napi_value nType;
331 NAPI_CALL_RETURN_VOID(reinterpret_cast<napi_env>(data->engine_),
332 napi_create_string_utf8(reinterpret_cast<napi_env>(data->engine_), data->eventType_.c_str(),
333 NAPI_AUTO_LENGTH, &nType));
334 NAPI_CALL_RETURN_VOID(reinterpret_cast<napi_env>(data->engine_),
335 napi_set_named_property(reinterpret_cast<napi_env>(data->engine_), napiEventInfo, "eventType", nType));
336 HILOG_DEBUG("eventType[%{public}s]", data->eventType_.c_str());
337
338 napi_value nTimeStamp;
339 NAPI_CALL_RETURN_VOID(reinterpret_cast<napi_env>(data->engine_),
340 napi_create_int64(reinterpret_cast<napi_env>(data->engine_), data->timeStamp_, &nTimeStamp));
341 NAPI_CALL_RETURN_VOID(reinterpret_cast<napi_env>(data->engine_),
342 napi_set_named_property(reinterpret_cast<napi_env>(data->engine_),
343 napiEventInfo, "timeStamp", nTimeStamp));
344
345 ConvertAccessibilityElementToJS(reinterpret_cast<napi_env>(data->engine_), napiEventInfo, data->element_);
346 NativeValue* nativeEventInfo = reinterpret_cast<NativeValue*>(napiEventInfo);
347 NativeValue* argv[] = {nativeEventInfo};
348 data->extension_->CallObjectMethod("onAccessibilityEvent", argv, 1);
349
350 delete data;
351 data = nullptr;
352 delete work;
353 work = nullptr;
354 });
355 if (ret) {
356 HILOG_ERROR("Failed to execute OnAccessibilityEvent work queue");
357 delete callbackInfo;
358 callbackInfo = nullptr;
359 delete work;
360 work = nullptr;
361 }
362 }
363
OnKeyPressEvent(const std::shared_ptr<MMI::KeyEvent> & keyEvent)364 bool NAccessibilityExtension::OnKeyPressEvent(const std::shared_ptr<MMI::KeyEvent> &keyEvent)
365 {
366 HILOG_INFO();
367 uv_loop_t *loop = engine_->GetUVLoop();
368 KeyEventCallbackInfo *callbackInfo = new(std::nothrow) KeyEventCallbackInfo();
369 if (!callbackInfo) {
370 HILOG_ERROR("Failed to create callbackInfo.");
371 return false;
372 }
373 callbackInfo->engine_ = engine_;
374 callbackInfo->keyEvent_ = MMI::KeyEvent::Clone(keyEvent);
375 callbackInfo->extension_ = this;
376 uv_work_t *work = new(std::nothrow) uv_work_t;
377 if (!work) {
378 HILOG_ERROR("Failed to create data.");
379 delete callbackInfo;
380 callbackInfo = nullptr;
381 return false;
382 }
383 work->data = static_cast<void*>(callbackInfo);
384 std::future syncFuture = callbackInfo->syncPromise_.get_future();
385
386 int ret = uv_queue_work(
387 loop,
388 work,
389 [](uv_work_t *work) {},
390 [](uv_work_t *work, int status) {
391 KeyEventCallbackInfo *data = static_cast<KeyEventCallbackInfo*>(work->data);
392 napi_value napiEventInfo = nullptr;
393 if (napi_create_object(reinterpret_cast<napi_env>(data->engine_), &napiEventInfo) != napi_ok) {
394 HILOG_ERROR("Create keyEvent object failed.");
395 data->syncPromise_.set_value(false);
396 delete data;
397 data = nullptr;
398 delete work;
399 work = nullptr;
400 return;
401 }
402 ConvertKeyEventToJS(reinterpret_cast<napi_env>(data->engine_), napiEventInfo, data->keyEvent_);
403 NativeValue* nativeEventInfo = reinterpret_cast<NativeValue*>(napiEventInfo);
404 NativeValue* argv[] = {nativeEventInfo};
405 NativeValue* nativeResult = data->extension_->CallObjectMethod("onKeyEvent", argv, 1);
406
407 // Unwrap result
408 bool result = false;
409 if (!ConvertFromJsValue(*data->engine_, nativeResult, result)) {
410 HILOG_ERROR("ConvertFromJsValue failed");
411 data->syncPromise_.set_value(false);
412 delete data;
413 data = nullptr;
414 delete work;
415 work = nullptr;
416 return;
417 }
418 HILOG_INFO("OnKeyPressEvent result = %{public}d", result);
419 data->syncPromise_.set_value(result);
420
421 delete data;
422 data = nullptr;
423 delete work;
424 work = nullptr;
425 });
426 if (ret) {
427 HILOG_ERROR("Failed to execute OnKeyPressEvent work queue");
428 callbackInfo->syncPromise_.set_value(false);
429 delete callbackInfo;
430 callbackInfo = nullptr;
431 delete work;
432 work = nullptr;
433 }
434 bool callbackResult = syncFuture.get();
435 HILOG_INFO("OnKeyPressEvent callbackResult = %{public}d", callbackResult);
436 return callbackResult;
437 }
438
CallObjectMethod(const char * name,NativeValue * const * argv,size_t argc)439 NativeValue* NAccessibilityExtension::CallObjectMethod(const char* name, NativeValue* const* argv, size_t argc)
440 {
441 HILOG_INFO("name:%{public}s", name);
442 if (!jsObj_) {
443 HILOG_ERROR("jsObj_ is nullptr");
444 return nullptr;
445 }
446
447 NativeValue* value = jsObj_->Get();
448 NativeObject* obj = ConvertNativeValueTo<NativeObject>(value);
449 if (!obj) {
450 HILOG_ERROR("Failed to get AccessibilityExtension object");
451 return nullptr;
452 }
453
454 NativeValue* method = obj->GetProperty(name);
455 if (!method) {
456 HILOG_ERROR("Failed to get '%{public}s' from AccessibilityExtension object", name);
457 return nullptr;
458 }
459 HILOG_INFO("CallFunction(%{public}s), success", name);
460 return engine_->CallFunction(value, method, argv, argc);
461 }
462
GetSrcPath(std::string & srcPath)463 void NAccessibilityExtension::GetSrcPath(std::string &srcPath)
464 {
465 if (!Extension::abilityInfo_) {
466 HILOG_ERROR("abilityInfo_ is nullptr");
467 return;
468 }
469 if (!Extension::abilityInfo_->isModuleJson) {
470 srcPath.append(Extension::abilityInfo_->package);
471 srcPath.append("/assets/js/");
472 if (!Extension::abilityInfo_->srcPath.empty()) {
473 srcPath.append(Extension::abilityInfo_->srcPath);
474 }
475 srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
476 return;
477 }
478
479 if (!Extension::abilityInfo_->srcEntrance.empty()) {
480 srcPath.append(Extension::abilityInfo_->moduleName + "/");
481 srcPath.append(Extension::abilityInfo_->srcEntrance);
482 srcPath.erase(srcPath.rfind('.'));
483 srcPath.append(".abc");
484 }
485 }
486 } // namespace Accessibility
487 } // namespace OHOS
488