• 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 
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