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