* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bridge/declarative_frontend/jsview/js_accessibility.h"
#include "bridge/declarative_frontend/engine/functions/js_accessibility_function.h"
#include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_frame_node_bridge.h"
#include "bridge/declarative_frontend/jsview/js_view_abstract.h"
#include "bridge/declarative_frontend/jsview/models/view_abstract_model_impl.h"
#include "core/accessibility/static/accessibility_static_utils.h"
#include "core/components_ng/base/view_abstract_model_ng.h"
#include "frameworks/base/log/ace_scoring_log.h"
namespace OHOS::Ace::Framework {
namespace {
const std::vector<AccessibilitySamePageMode> PAGE_MODE_TYPE = { AccessibilitySamePageMode::SEMI_SILENT,
AccessibilitySamePageMode::FULL_SILENT };
const std::string DEFAULT_STATE_DESCRIPTION = "";
}
void JSViewAbstract::JsAccessibilityGroup(const JSCallbackInfo& info)
{
bool isGroup = false;
if (info[0]->IsBoolean()) {
isGroup = info[0]->ToBoolean();
}
ViewAbstractModel::GetInstance()->SetAccessibilityGroup(isGroup);
if ((info.Length() <= 1) || !info[1]->IsObject()) {
return;
}
auto obj = JSRef<JSObject>::Cast(info[1]);
auto preferAccessibilityTextObj = obj->GetProperty("AccessibilityProferred");
auto preferAccessibilityText =
preferAccessibilityTextObj->IsBoolean() ? preferAccessibilityTextObj->ToBoolean() : false;
auto stateControllerTypeObj = obj->GetProperty("stateControllerRoleType");
auto stateControllerTypeNumber = stateControllerTypeObj->IsNumber() ?
stateControllerTypeObj->ToNumber<int32_t>() : static_cast<int32_t>(AccessibilityRoleType::ROLE_NONE);
auto stateControllerIdObj = obj->GetProperty("stateControllerId");
auto stateControllerId = stateControllerIdObj->IsString() ?
stateControllerIdObj->ToString() : "";
auto actionControllerTypeObj = obj->GetProperty("actionControllerRoleType");
auto actionControllerTypeNumber = actionControllerTypeObj->IsNumber() ?
actionControllerTypeObj->ToNumber<int32_t>() : static_cast<int32_t>(AccessibilityRoleType::ROLE_NONE);
auto actionControllerIdObj = obj->GetProperty("actionControllerId");
auto actionControllerId = actionControllerIdObj->IsString() ? actionControllerIdObj->ToString() : "";
NG::AccessibilityGroupOptions groupOptions = {
.accessibilityTextPreferred = preferAccessibilityText,
.stateControllerByType = static_cast<AccessibilityRoleType>(stateControllerTypeNumber),
.stateControllerByInspector = stateControllerId,
.actionControllerByType = static_cast<AccessibilityRoleType>(actionControllerTypeNumber),
.actionControllerByInspector = actionControllerId,
};
ViewAbstractModel::GetInstance()->SetAccessibilityTextPreferred(preferAccessibilityText);
ViewAbstractModel::GetInstance()->SetAccessibilityGroupOptions(groupOptions);
}
void JSViewAbstract::JsAccessibilityText(const JSCallbackInfo& info)
{
const JSRef<JSVal>& jsValue = info[0];
std::string text;
if (!ParseJsString(jsValue, text)) {
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityText(text);
}
void JSViewAbstract::JsAccessibilityTextHint(const std::string& text)
{
ViewAbstractModel::GetInstance()->SetAccessibilityTextHint(text);
}
void JSViewAbstract::JsAccessibilityNextFocusId(const JSCallbackInfo& info)
{
const JSRef<JSVal>& jsValue = info[0];
std::string nextFocusId;
if (!ParseJsString(jsValue, nextFocusId)) {
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityNextFocusId(nextFocusId);
if (info.Length() > 1 && info[1]->IsObject()) {
auto obj = JSRef<JSObject>::Cast(info[1]);
NG::AccessibilityNextFocusParams params;
auto descendantModeVal = obj->GetProperty("isConsiderDescendants");
if (descendantModeVal->IsBoolean()) {
params.nextFocusInspectorKey = nextFocusId;
params.descendantMode = descendantModeVal->ToBoolean();
}
ViewAbstractModel::GetInstance()->SetAccessibilityNextFocusParams(params);
}
}
void JSViewAbstract::JsAccessibilityDescription(const JSCallbackInfo& info)
{
const JSRef<JSVal>& jsValue = info[0];
std::string description;
if (!ParseJsString(jsValue, description)) {
return;
}
std::pair<bool, std::string> autoEventPair(false, "");
std::pair<bool, std::string> descriptionPair(false, "");
ParseAccessibilityDescriptionJson(description, autoEventPair, descriptionPair);
if (descriptionPair.first) {
ViewAbstractModel::GetInstance()->SetAccessibilityDescription(descriptionPair.second);
} else {
ViewAbstractModel::GetInstance()->SetAccessibilityDescription(description);
}
if (autoEventPair.first) {
ViewAbstractModel::GetInstance()->SetAutoEventParam(autoEventPair.second);
}
}
void JSViewAbstract::JsAccessibilityStateDescription(const JSCallbackInfo& info)
{
const JSRef<JSVal>& jsValue = info[0];
std::string stateDescription;
if (!ParseJsString(jsValue, stateDescription)) {
ViewAbstractModel::GetInstance()->SetAccessibilityStateDescription(DEFAULT_STATE_DESCRIPTION);
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityStateDescription(stateDescription);
}
void JSViewAbstract::ParseAccessibilityDescriptionJson(const std::string& description,
std::pair<bool, std::string>& autoEventPair, std::pair<bool, std::string>& descriptionPair)
{
if (description.empty()) {
return;
}
if (!StartWith(description, "{") || !EndWith(description, "}")) {
return;
}
auto jsonObj = JsonUtil::ParseJsonString(description);
if (!jsonObj || !jsonObj->IsValid() || !jsonObj->IsObject()) {
return;
}
if (jsonObj->Contains("$autoEventParam")) {
auto param = jsonObj->GetValue("$autoEventParam");
if (param) {
autoEventPair = std::make_pair(true, param->ToString());
}
}
if (jsonObj->Contains("$accessibilityDescription")) {
descriptionPair = std::make_pair(true, jsonObj->GetString("$accessibilityDescription"));
} else if (jsonObj->Contains("$autoEventParam")) {
descriptionPair = std::make_pair(true, "");
}
}
void JSViewAbstract::JsAccessibilityImportance(const std::string& importance)
{
ViewAbstractModel::GetInstance()->SetAccessibilityImportance(importance);
}
void JSViewAbstract::JsAccessibilityLevel(const std::string& level)
{
ViewAbstractModel::GetInstance()->SetAccessibilityImportance(level);
}
void JSViewAbstract::JsAccessibilityVirtualNode(const JSCallbackInfo& info)
{
if (!info[0]->IsObject()) {
return;
}
JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
auto builder = obj->GetProperty("builder");
if (builder->IsFunction()) {
auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
CHECK_NULL_VOID(builderFunc);
auto buildFunc = [execCtx = info.GetExecutionContext(), func = std::move(builderFunc)]() {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("AccessibilityVirtualNode");
func->Execute();
};
NG::ViewAbstractModelNG::GetInstance()->SetAccessibilityVirtualNode(std::move(buildFunc));
}
}
void JSViewAbstract::JsAccessibilitySelected(const JSCallbackInfo& info)
{
bool selected = false;
bool resetValue = false;
JSRef<JSVal> arg = info[0];
if (arg->IsUndefined()) {
resetValue = true;
} else if (arg->IsBoolean()) {
selected = arg->ToBoolean();
} else {
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilitySelected(selected, resetValue);
}
void JSViewAbstract::JsAccessibilityChecked(const JSCallbackInfo& info)
{
bool checked = false;
bool resetValue = false;
JSRef<JSVal> arg = info[0];
if (arg->IsUndefined()) {
resetValue = true;
} else if (arg->IsBoolean()) {
checked = arg->ToBoolean();
} else {
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityChecked(checked, resetValue);
}
void JSViewAbstract::JsAccessibilityScrollTriggerable(const JSCallbackInfo& info)
{
bool scrollTriggerable = false;
bool resetValue = false;
JSRef<JSVal> arg = info[0];
if (arg->IsUndefined()) {
resetValue = true;
} else if (arg->IsBoolean()) {
scrollTriggerable = arg->ToBoolean();
} else {
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityScrollTriggerable(scrollTriggerable, resetValue);
}
void JSViewAbstract::JsAccessibilityRole(const JSCallbackInfo& info)
{
bool resetValue = false;
std::string role;
if (info.Length() < 1 || !info[0]->IsNumber()) {
ViewAbstractModel::GetInstance()->SetAccessibilityRole(role, true);
return;
}
auto index = info[0]->ToNumber<int32_t>();
AccessibilityRoleType text = static_cast<AccessibilityRoleType>(index);
role = AccessibilityUtils::GetRoleByType(text);
if (role.empty()) {
resetValue = true;
}
ViewAbstractModel::GetInstance()->SetAccessibilityRole(role, resetValue);
}
void JSViewAbstract::JsOnAccessibilityFocus(const JSCallbackInfo& info)
{
if (info[0]->IsUndefined() || !info[0]->IsFunction()) {
ViewAbstractModel::GetInstance()->ResetOnAccessibilityFocus();
return;
}
RefPtr<JsFunction> jsFoucusFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(info[0]));
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onAccessibilityFoucus = [execCtx = info.GetExecutionContext(), func = std::move(jsFoucusFunc),
node = frameNode](bool isFocus) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("onAccessibilityFoucus");
PipelineContext::SetCallBackNode(node);
JSRef<JSVal> newJSVal = JSRef<JSVal>::Make(ToJSValue(isFocus));
func->ExecuteJS(1, &newJSVal);
};
ViewAbstractModel::GetInstance()->SetOnAccessibilityFocus(std::move(onAccessibilityFoucus));
}
void JSViewAbstract::JsAccessibilityDefaultFocus(const JSCallbackInfo& info)
{
JSRef<JSVal> arg = info[0];
if (arg->IsBoolean()) {
auto isFocus = arg->ToBoolean();
ViewAbstractModel::GetInstance()->SetAccessibilityDefaultFocus(isFocus);
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityDefaultFocus(false);
}
void JSViewAbstract::JsAccessibilityUseSamePage(const JSCallbackInfo& info)
{
JSRef<JSVal> arg = info[0];
if (arg->IsNumber()) {
auto pageModeType = arg->ToNumber<int32_t>();
if (pageModeType >= 0 && pageModeType < static_cast<int32_t>(PAGE_MODE_TYPE.size())) {
auto pageMode = static_cast<bool>(PAGE_MODE_TYPE[pageModeType]) ? "FULL_SILENT" : "SEMI_SILENT";
ViewAbstractModel::GetInstance()->SetAccessibilityUseSamePage(pageMode);
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityUseSamePage("");
return;
}
ViewAbstractModel::GetInstance()->SetAccessibilityUseSamePage("");
}
void JSViewAbstract::JsAccessibilityFocusDrawLevel(const JSCallbackInfo& info)
{
int32_t drawLevel = 0;
JSRef<JSVal> arg = info[0];
do {
if (!arg->IsNumber()) {
break;
}
if (arg->ToNumber<int32_t>() > 1) {
break;
}
drawLevel = arg->ToNumber<int32_t>();
} while (false);
ViewAbstractModel::GetInstance()->SetAccessibilityFocusDrawLevel(drawLevel);
}
void JSViewAbstract::JsOnAccessibilityActionIntercept(const JSCallbackInfo& info)
{
if (info[0]->IsUndefined() || !info[0]->IsFunction()) {
ViewAbstractModel::GetInstance()->SetOnAccessibilityActionIntercept(nullptr);
return;
}
auto jsInterceptFunc = AceType::MakeRefPtr<JsAccessibilityActionInterceptFunction>(JSRef<JSFunc>::Cast(info[0]));
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onAccessibilityActionIntercept = [execCtx = info.GetExecutionContext(), func = std::move(jsInterceptFunc),
node = frameNode](AccessibilityInterfaceAction action) -> AccessibilityActionInterceptResult {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, AccessibilityActionInterceptResult::ACTION_CONTINUE);
ACE_SCORING_EVENT("onAccessibilityActionIntercept");
PipelineContext::SetCallBackNode(node);
return func->Execute(action);
};
ViewAbstractModel::GetInstance()->SetOnAccessibilityActionIntercept(std::move(onAccessibilityActionIntercept));
}
void JSViewAbstract::JsOnAccessibilityHoverTransparent(const JSCallbackInfo& args)
{
if (args[0]->IsUndefined() || !args[0]->IsFunction()) {
ViewAbstractModel::GetInstance()->SetOnAccessibilityHoverTransparent(nullptr);
return;
}
auto jsOnHoverTransparentFunc =
AceType::MakeRefPtr<JsAccessibilityHoverTransparentFunction>(JSRef<JSFunc>::Cast(args[0]));
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onHoverTransparentFunc = [execCtx = args.GetExecutionContext(),
func = jsOnHoverTransparentFunc, node = frameNode](
TouchEventInfo& info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("onHoverTransparent");
PipelineContext::SetCallBackNode(node);
func->Execute(info);
};
ViewAbstractModel::GetInstance()->SetOnAccessibilityHoverTransparent(std::move(onHoverTransparentFunc));
}
void JSViewAbstract::JsAccessibilityActionOptions(const JSCallbackInfo& args)
{
NG::AccessibilityActionOptions options;
if (args.Length() == 0 || args[0]->IsUndefined()) {
ViewAbstractModel::GetInstance()->ResetAccessibilityActionOptions();
return;
}
if (args.Length() > 0 && args[0]->IsObject()) {
auto obj = JSRef<JSObject>::Cast(args[0]);
auto scrollStepVal = obj->GetProperty("scrollStep");
if (scrollStepVal->IsNumber()) {
int32_t stepCount = scrollStepVal->ToNumber<int32_t>();
options.scrollStep = (stepCount >= 1) ? stepCount : 1;
}
ViewAbstractModel::GetInstance()->SetAccessibilityActionOptions(options);
} else {
ViewAbstractModel::GetInstance()->ResetAccessibilityActionOptions();
}
}
void JSViewAbstract::JsAccessibilityCustomActions(const JSCallbackInfo& args)
{
if (args.Length() == 0 || args[0]->IsUndefined()) {
ViewAbstractModel::GetInstance()->ResetAccessibilityCustomActions();
return;
}
if (args.Length() > 0 && args[0]->IsArray()) {
auto array = JSRef<JSArray>::Cast(args[0]);
uint32_t length = array->Length();
std::vector<NG::AccessibilityCustomAction> actions;
for (uint32_t i = 0; i < length; i++) {
auto item = array->GetValueAt(i);
if (!item->IsObject()) {
continue;
}
auto obj = JSRef<JSObject>::Cast(item);
NG::AccessibilityCustomAction action;
auto nameVal = obj->GetProperty("name");
if (!ParseJsString(nameVal, action.actionName)) {
action.actionName = "";
}
auto callbackVal = obj->GetProperty("onAction");
if (callbackVal->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(callbackVal));
WeakPtr<NG::FrameNode> frameNode =
AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
action.customActionCallback =
[execCtx = args.GetExecutionContext(), func = jsFunc, node = frameNode]() {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("customActionCallback");
PipelineContext::SetCallBackNode(node);
func->Execute();
};
}
if (!action.actionName.empty() && action.customActionCallback) {
actions.push_back(action);
}
}
ViewAbstractModel::GetInstance()->SetAccessibilityCustomActions(actions);
} else {
ViewAbstractModel::GetInstance()->ResetAccessibilityCustomActions();
}
}
}