• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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