#include "ui/accessibility/platform/automation/automation_v8_bindings.h"
#include <string>
#include <string_view>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "gin/arguments.h"
#include "gin/converter.h"
#include "gin/data_object_builder.h"
#include "gin/handle.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_event_generator.h"
#include "ui/accessibility/ax_language_detection.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_selection.h"
#include "ui/accessibility/ax_text_utils.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/mojom/ax_action_data.mojom.h"
#include "ui/accessibility/platform/automation/automation_api_util.h"
#include "ui/accessibility/platform/automation/automation_position.h"
#include "ui/accessibility/platform/automation/automation_tree_manager_owner.h"
#include "ui/accessibility/platform/automation/automation_v8_router.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
#include "v8/include/v8-function-callback.h"
namespace ui {
namespace {
v8::Local<v8::String> CreateV8String(v8::Isolate* isolate,
std::string_view str) {
return gin::StringToSymbol(isolate, str);
}
v8::Local<v8::Object> RectToV8Object(v8::Isolate* isolate,
const gfx::Rect& rect) {
return gin::DataObjectBuilder(isolate)
.Set("left", rect.x())
.Set("top", rect.y())
.Set("width", rect.width())
.Set("height", rect.height())
.Build();
}
class GenericHandlerFunctionWrapper : public V8HandlerFunctionWrapper {
public:
explicit GenericHandlerFunctionWrapper(
base::RepeatingCallback<void(const v8::FunctionCallbackInfo<v8::Value>&)>
handler_function)
: handler_function_(handler_function) {}
void Run(gin::Arguments* arguments) override {
const v8::FunctionCallbackInfo<v8::Value>* args =
arguments->GetFunctionCallbackInfo();
DCHECK(args);
handler_function_.Run(*args);
}
private:
~GenericHandlerFunctionWrapper() override = default;
base::RepeatingCallback<void(const v8::FunctionCallbackInfo<v8::Value>&)>
handler_function_;
};
typedef void (*TreeIDFunction)(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper);
class TreeIDWrapper : public V8HandlerFunctionWrapper {
public:
TreeIDWrapper(AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
TreeIDFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() != 1 || !args[0]->IsString())
automation_router_->ThrowInvalidArgumentsException();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
if (!tree_wrapper->ax_tree()->root())
return;
function_(isolate, args.GetReturnValue(), tree_wrapper);
}
private:
~TreeIDWrapper() override = default;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
TreeIDFunction function_;
};
typedef base::RepeatingCallback<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node)>
NodeIDFunction;
class NodeIDWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDWrapper(AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsNumber())
automation_router_->ThrowInvalidArgumentsException();
v8::Local<v8::Context> context = automation_router_->GetContext();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1]->Int32Value(context).FromMaybe(0);
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_.Run(isolate, args.GetReturnValue(), tree_wrapper, node);
}
private:
~NodeIDWrapper() override = default;
friend class base::RefCountedThreadSafe<NodeIDWrapper>;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDFunction function_;
};
typedef void (*NodeIDPlusAttributeFunction)(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AXTree* tree,
AXNode* node,
const std::string& attribute);
class NodeIDPlusAttributeWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDPlusAttributeWrapper(
AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDPlusAttributeFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsNumber() ||
!args[2]->IsString()) {
automation_router_->ThrowInvalidArgumentsException();
}
v8::Local<v8::Context> context = automation_router_->GetContext();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1]->Int32Value(context).FromMaybe(0);
std::string attribute = *v8::String::Utf8Value(isolate, args[2]);
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_(isolate, args.GetReturnValue(), tree_wrapper->ax_tree(), node,
attribute);
}
private:
~NodeIDPlusAttributeWrapper() override = default;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDPlusAttributeFunction function_;
};
typedef base::RepeatingCallback<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
int start,
int end,
bool clipped)>
NodeIDPlusRangeFunction;
class NodeIDPlusRangeWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDPlusRangeWrapper(
AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDPlusRangeFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 5 || !args[0]->IsString() || !args[1]->IsNumber() ||
!args[2]->IsNumber() || !args[3]->IsNumber() || !args[4]->IsBoolean()) {
automation_router_->ThrowInvalidArgumentsException();
}
v8::Local<v8::Context> context = automation_router_->GetContext();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1]->Int32Value(context).FromMaybe(0);
int start = args[2]->Int32Value(context).FromMaybe(0);
int end = args[3]->Int32Value(context).FromMaybe(0);
bool clipped = args[4]->BooleanValue(isolate);
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_.Run(isolate, args.GetReturnValue(), tree_wrapper, node, start,
end, clipped);
}
private:
~NodeIDPlusRangeWrapper() override = default;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDPlusRangeFunction function_;
};
typedef base::RepeatingCallback<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
const std::string& strVal,
bool boolVal)>
NodeIDPlusStringBoolFunction;
class NodeIDPlusStringBoolWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDPlusStringBoolWrapper(
AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDPlusStringBoolFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 4 || !args[0]->IsString() || !args[1]->IsNumber() ||
!args[2]->IsString() || !args[3]->IsBoolean()) {
automation_router_->ThrowInvalidArgumentsException();
}
v8::Local<v8::Context> context = automation_router_->GetContext();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1]->Int32Value(context).FromMaybe(0);
std::string str_val = *v8::String::Utf8Value(isolate, args[2]);
bool bool_val = args[3].As<v8::Boolean>()->Value();
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_.Run(isolate, args.GetReturnValue(), tree_wrapper, node, str_val,
bool_val);
}
private:
~NodeIDPlusStringBoolWrapper() override = default;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDPlusStringBoolFunction function_;
};
using NodeIDPlusDimensionsFunction =
base::RepeatingCallback<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
int x,
int y,
int width,
int height)>;
class NodeIDPlusDimensionsWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDPlusDimensionsWrapper(
AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDPlusDimensionsFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 6 || !args[0]->IsString() || !args[1]->IsInt32() ||
!args[2]->IsInt32() || !args[3]->IsInt32() || !args[4]->IsInt32() ||
!args[5]->IsInt32()) {
automation_router_->ThrowInvalidArgumentsException(true);
}
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1].As<v8::Int32>()->Value();
int x = args[2].As<v8::Int32>()->Value();
int y = args[3].As<v8::Int32>()->Value();
int width = args[4].As<v8::Int32>()->Value();
int height = args[5].As<v8::Int32>()->Value();
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_.Run(isolate, args.GetReturnValue(), tree_wrapper, node, x, y,
width, height);
}
private:
~NodeIDPlusDimensionsWrapper() override = default;
friend class base::RefCountedThreadSafe<NodeIDPlusDimensionsWrapper>;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDPlusDimensionsFunction function_;
};
typedef base::RepeatingCallback<void(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
const std::tuple<ax::mojom::Event, AXEventGenerator::Event>& event_type)>
NodeIDPlusEventFunction;
class NodeIDPlusEventWrapper : public V8HandlerFunctionWrapper {
public:
NodeIDPlusEventWrapper(
AutomationTreeManagerOwner* automation_tree_manager_owner,
AutomationV8Router* automation_router,
NodeIDPlusEventFunction function)
: automation_tree_manager_owner_(automation_tree_manager_owner),
automation_router_(automation_router),
function_(function) {}
void Run(gin::Arguments* arguments) override {
DCHECK(arguments->GetFunctionCallbackInfo());
const v8::FunctionCallbackInfo<v8::Value>& args =
*arguments->GetFunctionCallbackInfo();
v8::Isolate* isolate = automation_router_->GetIsolate();
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsInt32() ||
!args[2]->IsString()) {
automation_router_->ThrowInvalidArgumentsException(false);
return;
}
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1].As<v8::Int32>()->Value();
std::tuple<ax::mojom::Event, AXEventGenerator::Event> event_type =
AutomationEventTypeToAXEventTuple(
*v8::String::Utf8Value(isolate, args[2]));
const ax::mojom::Event ax_event = std::get<0>(event_type);
const AXEventGenerator::Event generated_event = std::get<1>(event_type);
if ((ax_event == ax::mojom::Event::kNone &&
generated_event == AXEventGenerator::Event::NONE)) {
automation_router_->ThrowInvalidArgumentsException(false);
return;
}
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id);
if (!node)
return;
function_.Run(isolate, args.GetReturnValue(), tree_wrapper, node,
event_type);
}
private:
~NodeIDPlusEventWrapper() override = default;
raw_ptr<AutomationTreeManagerOwner> automation_tree_manager_owner_;
raw_ptr<AutomationV8Router> automation_router_;
NodeIDPlusEventFunction function_;
};
}
AutomationV8Bindings::AutomationV8Bindings(AutomationTreeManagerOwner* owner,
AutomationV8Router* router)
: automation_tree_manager_owner_(owner), automation_v8_router_(router) {}
AutomationV8Bindings::~AutomationV8Bindings() = default;
void AutomationV8Bindings::SendTreeChangeEvent(
int observer_id,
const AXTreeID& tree_id,
int node_id,
ax::mojom::Mutation change_type) {
base::Value::List args;
args.Append(observer_id);
args.Append(tree_id.ToString());
args.Append(node_id);
args.Append(automation_v8_router_->GetTreeChangeTypeString(change_type));
automation_v8_router_->DispatchEvent("automationInternal.onTreeChange", args);
}
void AutomationV8Bindings::SendNodesRemovedEvent(const AXTreeID& tree_id,
const std::vector<int>& ids) {
base::Value::List args;
args.Append(tree_id.ToString());
{
base::Value::List nodes;
for (auto id : ids)
nodes.Append(id);
args.Append(std::move(nodes));
}
automation_v8_router_->DispatchEvent("automationInternal.onNodesRemoved",
args);
}
void AutomationV8Bindings::SendChildTreeIDEvent(const AXTreeID& child_tree_id) {
base::Value::List args;
args.Append(child_tree_id.ToString());
automation_v8_router_->DispatchEvent("automationInternal.onChildTreeID",
args);
}
void AutomationV8Bindings::SendTreeDestroyedEvent(const AXTreeID& tree_id) {
base::Value::List args;
args.Append(tree_id.ToString());
automation_v8_router_->DispatchEvent(
"automationInternal.onAccessibilityTreeDestroyed", args);
}
void AutomationV8Bindings::SendGetTextLocationResult(
const AXActionData& data,
const std::optional<gfx::Rect>& rect) {
base::Value::Dict params;
params.Set("treeID", data.target_tree_id.ToString());
params.Set("childTreeID", data.child_tree_id.ToString());
params.Set("nodeID", data.target_node_id);
params.Set("result", false);
if (rect) {
params.Set("left", rect.value().x());
params.Set("top", rect.value().y());
params.Set("width", rect.value().width());
params.Set("height", rect.value().height());
params.Set("result", true);
}
params.Set("requestID", data.request_id);
base::Value::List args;
args.Append(std::move(params));
automation_v8_router_->DispatchEvent(
"automationInternal.onGetTextLocationResult", args);
}
void AutomationV8Bindings::SendActionResultEvent(const AXActionData& data,
bool result) {
base::Value::List args;
args.Append(data.target_tree_id.ToString());
args.Append(data.request_id);
args.Append(result);
automation_v8_router_->DispatchEvent("automationInternal.onActionResult",
args);
}
void AutomationV8Bindings::SendAutomationEvent(
const AXTreeID& tree_id,
const AXEvent& event,
const gfx::Point& mouse_location,
const std::tuple<ax::mojom::Event, AXEventGenerator::Event>& event_type) {
const std::string automation_event_type_str =
automation_v8_router_->GetEventTypeString(event_type);
base::Value::Dict event_params;
event_params.Set("treeID", base::Value(tree_id.ToString()));
event_params.Set("targetID", base::Value(event.id));
event_params.Set("eventType", base::Value(automation_event_type_str));
event_params.Set("eventFrom", base::Value(ToString(event.event_from)));
event_params.Set("eventFromAction",
base::Value(ToString(event.event_from_action)));
event_params.Set("actionRequestID", base::Value(event.action_request_id));
event_params.Set("mouseX", base::Value(mouse_location.x()));
event_params.Set("mouseY", base::Value(mouse_location.y()));
base::Value::List value_intents;
for (const auto& intent : event.event_intents) {
base::Value::Dict dict;
dict.Set("command", base::Value(ToString(intent.command)));
dict.Set("inputEventType", base::Value(ToString(intent.input_event_type)));
dict.Set("textBoundary", base::Value(ToString(intent.text_boundary)));
dict.Set("moveDirection", base::Value(ToString(intent.move_direction)));
value_intents.Append(std::move(dict));
}
event_params.Set("intents", std::move(value_intents));
base::Value::List args;
args.Append(std::move(event_params));
automation_v8_router_->DispatchEvent(
"automationInternal.onAccessibilityEvent", args);
}
void AutomationV8Bindings::SendTreeSerializationError(const AXTreeID& tree_id) {
base::Value::List args;
args.Append(tree_id.ToString());
automation_v8_router_->DispatchEvent(
"automationInternal.onAccessibilityTreeSerializationError", args);
}
void AutomationV8Bindings::SendOnAllEventListenersRemoved() {
automation_v8_router_->DispatchEvent(
"automationInternal.onAllAutomationEventListenersRemoved",
base::Value::List());
}
void AutomationV8Bindings::AddV8Routes() {
scoped_refptr<GenericHandlerFunctionWrapper> wrapper;
#define ROUTE_FUNCTION(FN) \
wrapper = base::MakeRefCounted<GenericHandlerFunctionWrapper>( \
base::BindRepeating(&AutomationV8Bindings::FN, base::Unretained(this))); \
automation_v8_router_->RouteHandlerFunction(#FN, wrapper);
ROUTE_FUNCTION(GetChildIDAtIndex);
ROUTE_FUNCTION(GetFocus);
ROUTE_FUNCTION(CreateAutomationPosition);
ROUTE_FUNCTION(GetAccessibilityFocus);
ROUTE_FUNCTION(StringAXTreeIDToUnguessableToken);
ROUTE_FUNCTION(SetDesktopID);
ROUTE_FUNCTION(DestroyAccessibilityTree);
ROUTE_FUNCTION(AddTreeChangeObserver);
ROUTE_FUNCTION(RemoveTreeChangeObserver);
ROUTE_FUNCTION(GetState);
ROUTE_FUNCTION(StartCachingAccessibilityTrees);
ROUTE_FUNCTION(StopCachingAccessibilityTrees);
RouteTreeIDFunction(
"GetRootID", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(
v8::Integer::New(isolate, tree_wrapper->ax_tree()->root()->id()));
});
RouteTreeIDFunction(
"GetPublicRoot",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
tree_wrapper = tree_wrapper->GetTreeWrapperWithUnignoredRoot();
if (!tree_wrapper)
return;
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_wrapper->GetTreeID().ToString());
response.Set("nodeId", tree_wrapper->ax_tree()->root()->id());
result.Set(response.Build());
});
RouteTreeIDFunction(
"GetDocURL", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8(
isolate, tree_wrapper->ax_tree()->data().url.c_str())
.ToLocalChecked());
});
RouteTreeIDFunction(
"GetDocTitle", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8(
isolate, tree_wrapper->ax_tree()->data().title.c_str())
.ToLocalChecked());
});
RouteTreeIDFunction(
"GetDocLoaded",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(tree_wrapper->ax_tree()->data().loaded);
});
RouteTreeIDFunction(
"GetDocLoadingProgress",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Number::New(
isolate, tree_wrapper->ax_tree()->data().loading_progress));
});
RouteTreeIDFunction(
"GetIsSelectionBackward",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
const AXNode* anchor = tree_wrapper->GetNode(
tree_wrapper->GetUnignoredSelection().anchor_object_id);
if (!anchor)
return;
result.Set(tree_wrapper->ax_tree()->data().sel_is_backward);
});
RouteTreeIDFunction(
"GetAnchorObjectID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Number::New(
isolate, tree_wrapper->GetUnignoredSelection().anchor_object_id));
});
RouteTreeIDFunction(
"GetAnchorOffset",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Number::New(
isolate, tree_wrapper->GetUnignoredSelection().anchor_offset));
});
RouteTreeIDFunction(
"GetAnchorAffinity",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(CreateV8String(
isolate,
ToString(tree_wrapper->GetUnignoredSelection().anchor_affinity)));
});
RouteTreeIDFunction(
"GetFocusObjectID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Number::New(
isolate, tree_wrapper->GetUnignoredSelection().focus_object_id));
});
RouteTreeIDFunction(
"GetFocusOffset",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Number::New(
isolate, tree_wrapper->GetUnignoredSelection().focus_offset));
});
RouteTreeIDFunction(
"GetFocusAffinity",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(CreateV8String(
isolate,
ToString(tree_wrapper->GetUnignoredSelection().focus_affinity)));
});
RouteTreeIDFunction(
"GetSelectionStartObjectID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
int32_t start_object_id = unignored_selection.is_backward
? unignored_selection.focus_object_id
: unignored_selection.anchor_object_id;
result.Set(v8::Number::New(isolate, start_object_id));
});
RouteTreeIDFunction(
"GetSelectionStartOffset",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
int start_offset = unignored_selection.is_backward
? unignored_selection.focus_offset
: unignored_selection.anchor_offset;
result.Set(v8::Number::New(isolate, start_offset));
});
RouteTreeIDFunction(
"GetSelectionStartAffinity",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
ax::mojom::TextAffinity start_affinity =
unignored_selection.is_backward
? unignored_selection.focus_affinity
: unignored_selection.anchor_affinity;
result.Set(CreateV8String(isolate, ToString(start_affinity)));
});
RouteTreeIDFunction(
"GetSelectionEndObjectID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
int32_t end_object_id = unignored_selection.is_backward
? unignored_selection.anchor_object_id
: unignored_selection.focus_object_id;
result.Set(v8::Number::New(isolate, end_object_id));
});
RouteTreeIDFunction(
"GetSelectionEndOffset",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
int end_offset = unignored_selection.is_backward
? unignored_selection.anchor_offset
: unignored_selection.focus_offset;
result.Set(v8::Number::New(isolate, end_offset));
});
RouteTreeIDFunction(
"GetSelectionEndAffinity",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
AXSelection unignored_selection = tree_wrapper->GetUnignoredSelection();
ax::mojom::TextAffinity end_affinity =
unignored_selection.is_backward
? unignored_selection.anchor_affinity
: unignored_selection.focus_affinity;
result.Set(CreateV8String(isolate, ToString(end_affinity)));
});
RouteNodeIDFunction("GetParentID",
base::BindRepeating(&AutomationV8Bindings::GetParentID,
base::Unretained(this)));
RouteNodeIDFunction("GetChildCount",
base::BindRepeating(&AutomationV8Bindings::GetChildCount,
base::Unretained(this)));
RouteNodeIDFunction(
"GetIndexInParent",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
result.Set(v8::Integer::New(
isolate, static_cast<int32_t>(node->GetUnignoredIndexInParent())));
}));
RouteNodeIDFunction(
"GetRole",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
const std::string& role_name = ToString(node->GetRole());
result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction("GetLocation",
base::BindRepeating(&AutomationV8Bindings::GetLocation,
base::Unretained(this)));
RouteNodeIDFunction(
"GetUnclippedLocation",
base::BindRepeating(&AutomationV8Bindings::GetUnclippedLocation,
base::Unretained(this)));
RouteNodeIDFunction(
"GetLineStartOffsets",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
const std::vector<int> line_starts =
node->GetIntListAttribute(ax::mojom::IntListAttribute::kLineStarts);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, line_starts.size()));
for (size_t i = 0; i < line_starts.size(); ++i) {
array_result
->CreateDataProperty(context, static_cast<uint32_t>(i),
v8::Integer::New(isolate, line_starts[i]))
.Check();
}
result.Set(array_result);
}));
RouteNodeIDFunction("GetChildIDs",
base::BindRepeating(&AutomationV8Bindings::GetChildIDs,
base::Unretained(this)));
RouteNodeIDFunction(
"GetWordStartOffsets",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
std::vector<int> word_starts = GetWordStartOffsets(
node->GetString16Attribute(ax::mojom::StringAttribute::kName));
result.Set(gin::ConvertToV8(isolate, word_starts));
}));
RouteNodeIDFunction(
"GetWordEndOffsets",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
std::vector<int> word_ends = GetWordEndOffsets(
node->GetString16Attribute(ax::mojom::StringAttribute::kName));
result.Set(gin::ConvertToV8(isolate, word_ends));
}));
RouteNodeIDFunction(
"GetSentenceStartOffsets",
base::BindRepeating(&AutomationV8Bindings::GetSentenceStartOffsets,
base::Unretained(this)));
RouteNodeIDFunction(
"GetSentenceEndOffsets",
base::BindRepeating(&AutomationV8Bindings::GetSentenceEndOffsets,
base::Unretained(this)));
RouteNodeIDPlusRangeFunction(
"GetBoundsForRange",
base::BindRepeating(&AutomationV8Bindings::GetBoundsForRange,
base::Unretained(this)));
RouteNodeIDPlusDimensionsFunction(
"ComputeGlobalBounds",
base::BindRepeating(&AutomationV8Bindings::ComputeGlobalBounds,
base::Unretained(this)));
RouteNodeIDPlusAttributeFunction(
"GetStringAttribute",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::StringAttribute>(attribute_name.c_str());
const char* attr_value;
if (attribute == ax::mojom::StringAttribute::kFontFamily ||
attribute == ax::mojom::StringAttribute::kLanguage) {
attr_value = node->GetInheritedStringAttribute(attribute).c_str();
} else if (!node->HasStringAttribute(attribute)) {
return;
} else {
attr_value = node->GetStringAttribute(attribute).c_str();
}
result.Set(
v8::String::NewFromUtf8(isolate, attr_value).ToLocalChecked());
});
RouteNodeIDPlusAttributeFunction(
"GetBoolAttribute",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::BoolAttribute>(attribute_name.c_str());
if (!node->HasBoolAttribute(attribute)) {
return;
}
result.Set(node->GetBoolAttribute(attribute));
});
RouteNodeIDPlusAttributeFunction(
"GetIntAttribute",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::IntAttribute>(attribute_name.c_str());
int attr_value;
if (attribute == ax::mojom::IntAttribute::kPosInSet &&
node->GetPosInSet()) {
attr_value = *node->GetPosInSet();
} else if (attribute == ax::mojom::IntAttribute::kSetSize &&
node->GetSetSize()) {
attr_value = *node->GetSetSize();
} else if (!node->HasIntAttribute(attribute)) {
return;
} else {
attr_value = node->GetIntAttribute(attribute);
}
result.Set(v8::Integer::New(isolate, attr_value));
});
RouteNodeIDPlusAttributeFunction(
"GetIntAttributeReverseRelations",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::IntAttribute>(attribute_name.c_str());
std::set<int32_t> ids =
tree->GetReverseRelations(attribute, node->id());
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(v8::Array::New(isolate, ids.size()));
size_t count = 0;
for (int32_t id : ids) {
array_result
->CreateDataProperty(context, static_cast<uint32_t>(count++),
v8::Integer::New(isolate, id))
.Check();
}
result.Set(array_result);
});
RouteNodeIDPlusAttributeFunction(
"GetFloatAttribute",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::FloatAttribute>(attribute_name.c_str());
if (!node->HasFloatAttribute(attribute)) {
return;
}
float attr_value = node->GetFloatAttribute(attribute);
double intpart, fracpart;
fracpart = modf(attr_value, &intpart);
double value_precision_2 =
intpart + std::round(fracpart * 100) / 100.0f;
result.Set(v8::Number::New(isolate, value_precision_2));
});
RouteNodeIDPlusAttributeFunction(
"GetIntListAttribute",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::IntListAttribute>(attribute_name.c_str());
if (!node->HasIntListAttribute(attribute))
return;
const std::vector<int32_t>& attr_value =
node->GetIntListAttribute(attribute);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, attr_value.size()));
for (size_t i = 0; i < attr_value.size(); ++i)
array_result
->CreateDataProperty(context, static_cast<uint32_t>(i),
v8::Integer::New(isolate, attr_value[i]))
.Check();
result.Set(array_result);
});
RouteNodeIDPlusAttributeFunction(
"GetIntListAttributeReverseRelations",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, AXTree* tree,
AXNode* node, const std::string& attribute_name) {
auto attribute =
ParseAXEnum<ax::mojom::IntListAttribute>(attribute_name.c_str());
std::set<int32_t> ids =
tree->GetReverseRelations(attribute, node->id());
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(v8::Array::New(isolate, ids.size()));
size_t count = 0;
for (int32_t id : ids) {
array_result
->CreateDataProperty(context, static_cast<uint32_t>(count++),
v8::Integer::New(isolate, id))
.Check();
}
result.Set(array_result);
});
RouteNodeIDFunction(
"GetNameFrom",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::NameFrom name_from = node->data().GetNameFrom();
const std::string& name_from_str = ToString(name_from);
result.Set(v8::String::NewFromUtf8(isolate, name_from_str.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction("GetName",
base::BindRepeating(&AutomationV8Bindings::GetName,
base::Unretained(this)));
RouteNodeIDFunction(
"GetDescriptionFrom",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::DescriptionFrom description_from =
static_cast<ax::mojom::DescriptionFrom>(node->GetIntAttribute(
ax::mojom::IntAttribute::kDescriptionFrom));
std::string description_from_str = ToString(description_from);
result.Set(
v8::String::NewFromUtf8(isolate, description_from_str.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetSubscript",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value =
node->GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) ==
static_cast<int32_t>(ax::mojom::TextPosition::kSubscript);
result.Set(value);
}));
RouteNodeIDFunction(
"GetSuperscript",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value =
node->GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) ==
static_cast<int32_t>(ax::mojom::TextPosition::kSuperscript);
result.Set(value);
}));
RouteNodeIDFunction(
"GetBold",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value = node->data().HasTextStyle(ax::mojom::TextStyle::kBold);
result.Set(value);
}));
RouteNodeIDFunction(
"GetItalic", base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
bool value = node->data().HasTextStyle(ax::mojom::TextStyle::kItalic);
result.Set(value);
}));
RouteNodeIDFunction(
"GetUnderline",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value =
node->data().HasTextStyle(ax::mojom::TextStyle::kUnderline);
result.Set(value);
}));
RouteNodeIDFunction(
"GetLineThrough",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value =
node->data().HasTextStyle(ax::mojom::TextStyle::kLineThrough);
result.Set(value);
}));
RouteNodeIDFunction(
"GetDetectedLanguage",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
const std::string& detectedLanguage = node->GetLanguage();
result.Set(v8::String::NewFromUtf8(isolate, detectedLanguage.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetCustomActions",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
const std::vector<int32_t>& custom_action_ids =
node->GetIntListAttribute(
ax::mojom::IntListAttribute::kCustomActionIds);
if (custom_action_ids.empty()) {
result.SetUndefined();
return;
}
const std::vector<std::string>& custom_action_descriptions =
node->GetStringListAttribute(
ax::mojom::StringListAttribute::kCustomActionDescriptions);
if (custom_action_ids.size() != custom_action_descriptions.size()) {
NOTREACHED();
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> custom_actions(
v8::Array::New(isolate, custom_action_ids.size()));
for (size_t i = 0; i < custom_action_ids.size(); i++) {
gin::DataObjectBuilder custom_action(isolate);
custom_action.Set("id", custom_action_ids[i]);
custom_action.Set("description", custom_action_descriptions[i]);
custom_actions
->CreateDataProperty(context, static_cast<uint32_t>(i),
custom_action.Build())
.Check();
}
result.Set(custom_actions);
}));
RouteNodeIDFunction(
"GetStandardActions",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
std::vector<std::string> standard_actions;
for (uint32_t action = static_cast<uint32_t>(ax::mojom::Action::kNone);
action <= static_cast<uint32_t>(ax::mojom::Action::kMaxValue);
++action) {
if (node->data().HasAction(static_cast<ax::mojom::Action>(action))) {
standard_actions.push_back(
ToString(static_cast<ax::mojom::Action>(action)));
}
}
int default_action_verb = static_cast<int>(
node->GetIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb));
if (node->HasIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb) &&
default_action_verb !=
static_cast<int>(ax::mojom::DefaultActionVerb::kNone)) {
standard_actions.push_back(ToString(
static_cast<ax::mojom::Action>(ax::mojom::Action::kDoDefault)));
}
auto role = node->GetRole();
if (role == ax::mojom::Role::kSlider ||
role == ax::mojom::Role::kSpinButton) {
standard_actions.push_back(ToString(
static_cast<ax::mojom::Action>(ax::mojom::Action::kIncrement)));
standard_actions.push_back(ToString(
static_cast<ax::mojom::Action>(ax::mojom::Action::kDecrement)));
}
auto actions_result = v8::Array::New(isolate, standard_actions.size());
for (size_t i = 0; i < standard_actions.size(); i++) {
const v8::Maybe<bool>& did_set_value = actions_result->Set(
isolate->GetCurrentContext(), i,
v8::String::NewFromUtf8(isolate, standard_actions[i].c_str())
.ToLocalChecked());
bool did_set_value_result = false;
if (!did_set_value.To(&did_set_value_result) || !did_set_value_result)
return;
}
result.Set(actions_result);
}));
RouteNodeIDFunction(
"GetChecked",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
const ax::mojom::CheckedState checked_state =
static_cast<ax::mojom::CheckedState>(node->GetIntAttribute(
ax::mojom::IntAttribute::kCheckedState));
if (checked_state != ax::mojom::CheckedState::kNone) {
const std::string& checked_str = ToString(checked_state);
result.Set(v8::String::NewFromUtf8(isolate, checked_str.c_str())
.ToLocalChecked());
}
}));
RouteNodeIDFunction(
"GetRestriction",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
const ax::mojom::Restriction restriction =
node->data().GetRestriction();
if (restriction != ax::mojom::Restriction::kNone) {
const std::string& restriction_str = ToString(restriction);
result.Set(v8::String::NewFromUtf8(isolate, restriction_str.c_str())
.ToLocalChecked());
}
}));
RouteNodeIDFunction(
"GetDefaultActionVerb",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::DefaultActionVerb default_action_verb =
static_cast<ax::mojom::DefaultActionVerb>(node->GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb));
if (default_action_verb == ax::mojom::DefaultActionVerb::kNone)
return;
const std::string& default_action_verb_str =
ToString(default_action_verb);
result.Set(v8::String::NewFromUtf8(isolate,
default_action_verb_str.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetHasPopup",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::HasPopup has_popup = node->data().GetHasPopup();
const std::string& has_popup_str = ToString(has_popup);
result.Set(v8::String::NewFromUtf8(isolate, has_popup_str.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetAriaCurrentState",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::AriaCurrentState current_state =
static_cast<ax::mojom::AriaCurrentState>(node->GetIntAttribute(
ax::mojom::IntAttribute::kAriaCurrentState));
if (current_state == ax::mojom::AriaCurrentState::kNone)
return;
const std::string& current_state_string = ToString(current_state);
result.Set(
v8::String::NewFromUtf8(isolate, current_state_string.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetInvalidState",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
ax::mojom::InvalidState invalid_state = node->GetInvalidState();
if (invalid_state == ax::mojom::InvalidState::kNone)
return;
const std::string& invalid_state_string = ToString(invalid_state);
result.Set(
v8::String::NewFromUtf8(isolate, invalid_state_string.c_str())
.ToLocalChecked());
}));
RouteNodeIDFunction(
"GetIsButton",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value = IsButton(node->GetRole());
result.Set(value);
}));
RouteNodeIDFunction(
"GetIsCheckBox",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value = IsCheckBox(node->GetRole());
result.Set(value);
}));
RouteNodeIDFunction(
"GetIsComboBox",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value = IsComboBox(node->GetRole());
result.Set(value);
}));
RouteNodeIDFunction(
"GetIsImage",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
bool value = IsImage(node->GetRole());
result.Set(value);
}));
RouteNodeIDPlusStringBoolFunction(
"GetNextTextMatch",
base::BindRepeating(&AutomationV8Bindings::GetNextTextMatch,
base::Unretained(this)));
RouteNodeIDFunction(
"GetTableColumnCount",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableColCount())
result.Set(*node->GetTableColCount());
}));
RouteNodeIDFunction(
"GetTableRowCount",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableRowCount())
result.Set(*node->GetTableRowCount());
}));
RouteNodeIDFunction(
"GetTableCellColumnHeaders",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
std::vector<int32_t> col_headers = node->GetTableCellColHeaderNodeIds();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, col_headers.size()));
for (size_t i = 0; i < col_headers.size(); ++i)
array_result
->CreateDataProperty(context, static_cast<uint32_t>(i),
v8::Integer::New(isolate, col_headers[i]))
.Check();
result.Set(array_result);
}));
RouteNodeIDFunction(
"GetTableCellRowHeaders",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
std::vector<int32_t> row_headers = node->GetTableCellRowHeaderNodeIds();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, row_headers.size()));
for (size_t i = 0; i < row_headers.size(); ++i)
array_result
->CreateDataProperty(context, static_cast<uint32_t>(i),
v8::Integer::New(isolate, row_headers[i]))
.Check();
result.Set(array_result);
}));
RouteNodeIDFunction(
"GetTableCellColumnIndex",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableCellColIndex())
result.Set(*node->GetTableCellColIndex());
}));
RouteNodeIDFunction(
"GetTableCellRowIndex",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableCellRowIndex())
result.Set(*node->GetTableCellRowIndex());
}));
RouteNodeIDFunction(
"GetTableCellAriaColumnIndex",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableCellAriaColIndex())
result.Set(*node->GetTableCellAriaColIndex());
}));
RouteNodeIDFunction(
"GetTableCellAriaRowIndex",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
if (node->GetTableCellAriaRowIndex())
result.Set(*node->GetTableCellAriaRowIndex());
}));
RouteNodeIDFunction(
"SetAccessibilityFocus",
base::BindRepeating(&AutomationV8Bindings::SetAccessibilityFocus,
base::Unretained(this)));
RouteNodeIDFunction(
"GetSortDirection",
base::BindRepeating([](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
if (node->HasIntAttribute(ax::mojom::IntAttribute::kSortDirection)) {
const std::string& sort_direction_str = ToString(
static_cast<ax::mojom::SortDirection>(node->GetIntAttribute(
ax::mojom::IntAttribute::kSortDirection)));
result.Set(
v8::String::NewFromUtf8(isolate, sort_direction_str.c_str())
.ToLocalChecked());
}
}));
RouteNodeIDFunction(
"GetValue",
base::BindRepeating(
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AXNode* node) {
const std::string value_str = node->GetValueForControl();
result.Set(v8::String::NewFromUtf8(isolate, value_str.c_str())
.ToLocalChecked());
}));
RouteNodeIDPlusEventFunction(
"EventListenerAdded",
base::BindRepeating(&AutomationV8Bindings::EventListenerAdded,
base::Unretained(this)));
RouteNodeIDPlusEventFunction(
"EventListenerRemoved",
base::BindRepeating(&AutomationV8Bindings::EventListenerRemoved,
base::Unretained(this)));
RouteNodeIDFunction("GetMarkers",
base::BindRepeating(&AutomationV8Bindings::GetMarkers,
base::Unretained(this)));
RouteNodeIDFunction(
"GetImageAnnotation",
base::BindRepeating(&AutomationV8Bindings::GetImageAnnotation,
base::Unretained(this)));
}
void AutomationV8Bindings::RouteTreeIDFunction(const std::string& name,
TreeIDFunction callback) {
auto wrapper = base::MakeRefCounted<TreeIDWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDFunction(const std::string& name,
NodeIDFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDPlusAttributeFunction(
const std::string& name,
NodeIDPlusAttributeFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDPlusAttributeWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDPlusRangeFunction(
const std::string& name,
NodeIDPlusRangeFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDPlusRangeWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDPlusStringBoolFunction(
const std::string& name,
NodeIDPlusStringBoolFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDPlusStringBoolWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDPlusDimensionsFunction(
const std::string& name,
NodeIDPlusDimensionsFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDPlusDimensionsWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::RouteNodeIDPlusEventFunction(
const std::string& name,
NodeIDPlusEventFunction callback) {
auto wrapper = base::MakeRefCounted<NodeIDPlusEventWrapper>(
automation_tree_manager_owner_, automation_v8_router_, callback);
automation_v8_router_->RouteHandlerFunction(name, wrapper);
}
void AutomationV8Bindings::GetFocus(
const v8::FunctionCallbackInfo<v8::Value>& args) const {
if (args.Length() != 0) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
int node_id;
AXTreeID focused_tree_id;
if (!automation_tree_manager_owner_->GetFocus(&focused_tree_id, &node_id))
return;
args.GetReturnValue().Set(
gin::DataObjectBuilder(automation_v8_router_->GetIsolate())
.Set("treeId", focused_tree_id.ToString())
.Set("nodeId", node_id)
.Build());
}
void AutomationV8Bindings::GetAccessibilityFocus(
const v8::FunctionCallbackInfo<v8::Value>& args) const {
AXTreeID tree_id;
int node_id;
if (!automation_tree_manager_owner_->GetAccessibilityFocus(&tree_id,
&node_id))
return;
args.GetReturnValue().Set(
gin::DataObjectBuilder(automation_v8_router_->GetIsolate())
.Set("treeId", tree_id.ToString())
.Set("nodeId", node_id)
.Build());
}
void AutomationV8Bindings::StringAXTreeIDToUnguessableToken(
const v8::FunctionCallbackInfo<v8::Value>& args) const {
if (args.Length() != 1 || !args[0]->IsString()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
const AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(args.GetIsolate(), args[0]));
const std::optional<base::UnguessableToken>& token = tree_id.token();
if (!token || token->is_empty()) {
return;
}
const std::string high_str =
base::NumberToString(token->GetHighForSerialization());
const std::string low_str =
base::NumberToString(token->GetLowForSerialization());
gin::DataObjectBuilder response(automation_v8_router_->GetIsolate());
response.Set("high", high_str);
response.Set("low", low_str);
args.GetReturnValue().Set(response.Build());
}
void AutomationV8Bindings::SetDesktopID(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsString()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
automation_tree_manager_owner_->SetDesktopTreeId(
AXTreeID::FromString(*v8::String::Utf8Value(args.GetIsolate(), args[0])));
}
void AutomationV8Bindings::GetChildIDAtIndex(
const v8::FunctionCallbackInfo<v8::Value>& args) const {
if (args.Length() < 3 || !args[2]->IsNumber()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(args.GetIsolate(), args[0]));
int node_id =
args[1]->Int32Value(automation_v8_router_->GetContext()).FromMaybe(0);
int index =
args[2]->Int32Value(automation_v8_router_->GetContext()).FromMaybe(0);
int child_node_id;
AXTreeID child_tree_id;
if (!automation_tree_manager_owner_->GetChildIDAtIndex(
tree_id, node_id, index, &child_tree_id, &child_node_id))
return;
gin::DataObjectBuilder response(automation_v8_router_->GetIsolate());
response.Set("treeId", child_tree_id.ToString());
response.Set("nodeId", child_node_id);
args.GetReturnValue().Set(response.Build());
}
void AutomationV8Bindings::CreateAutomationPosition(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = automation_v8_router_->GetIsolate();
if (args.Length() < 5 || !args[0]->IsString() ||
!args[1]->IsInt32() || !args[2]->IsString() ||
!args[3]->IsInt32() ||
!args[4]->IsBoolean() ) {
automation_v8_router_->ThrowInvalidArgumentsException();
}
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id =
args[1]->Int32Value(automation_v8_router_->GetContext()).ToChecked();
AutomationAXTreeWrapper* tree_wrapper =
automation_tree_manager_owner_->GetAutomationAXTreeWrapperFromTreeID(
tree_id);
if (!tree_wrapper)
return;
AXNode* node = tree_wrapper->ax_tree()->GetFromId(node_id);
if (!node)
return;
AXPositionKind kind =
StringToAXPositionKind(*v8::String::Utf8Value(isolate, args[2]));
int offset =
args[3]->Int32Value(automation_v8_router_->GetContext()).ToChecked();
bool is_upstream = args[3]->BooleanValue(isolate);
AutomationPosition* cpp_result =
cppgc::MakeGarbageCollected<AutomationPosition>(
isolate->GetCppHeap()->GetAllocationHandle(), *node, kind, offset,
is_upstream);
args.GetReturnValue().Set(cpp_result->GetWrapper(isolate).ToLocalChecked());
}
void AutomationV8Bindings::DestroyAccessibilityTree(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsString()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(args.GetIsolate(), args[0]));
automation_tree_manager_owner_->DestroyAccessibilityTree(tree_id);
}
void AutomationV8Bindings::AddTreeChangeObserver(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 2 || !args[0]->IsNumber() || !args[1]->IsString()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
int id =
args[0]->Int32Value(automation_v8_router_->GetContext()).FromMaybe(0);
std::string filter_str = *v8::String::Utf8Value(args.GetIsolate(), args[1]);
automation_tree_manager_owner_->AddTreeChangeObserver(
id, automation_v8_router_->ParseTreeChangeObserverFilter(filter_str));
}
void AutomationV8Bindings::RemoveTreeChangeObserver(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsString()) {
automation_v8_router_->ThrowInvalidArgumentsException();
return;
}
int observer_id =
args[0]->Int32Value(automation_v8_router_->GetContext()).FromMaybe(0);
automation_tree_manager_owner_->RemoveTreeChangeObserver(observer_id);
}
void AutomationV8Bindings::GetParentID(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
AXNode* parent =
automation_tree_manager_owner_->GetParent(node, &tree_wrapper);
if (parent) {
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_wrapper->GetTreeID().ToString());
response.Set("nodeId", parent->id());
result.Set(response.Build());
}
}
void AutomationV8Bindings::GetChildCount(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
size_t child_count = automation_tree_manager_owner_->GetChildCount(node);
result.Set(v8::Integer::New(isolate, static_cast<int32_t>(child_count)));
}
void AutomationV8Bindings::GetLocation(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
gfx::Rect global_clipped_bounds =
automation_tree_manager_owner_->ComputeGlobalNodeBounds(tree_wrapper,
node);
result.Set(RectToV8Object(isolate, global_clipped_bounds));
}
void AutomationV8Bindings::GetUnclippedLocation(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
bool offscreen = false;
gfx::Rect global_unclipped_bounds =
automation_tree_manager_owner_->ComputeGlobalNodeBounds(
tree_wrapper, node, gfx::RectF(), &offscreen,
false );
result.Set(RectToV8Object(isolate, global_unclipped_bounds));
}
void AutomationV8Bindings::GetChildIDs(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
AXTreeID tree_id;
std::vector<int> child_ids =
automation_tree_manager_owner_->GetChildIDs(node, &tree_id);
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_id.ToString());
response.Set("nodeIds", child_ids);
result.Set(response.Build());
}
void AutomationV8Bindings::GetSentenceStartOffsets(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
const std::vector<int>& sentence_starts =
automation_tree_manager_owner_->CalculateSentenceBoundary(
tree_wrapper, node, true );
result.Set(gin::ConvertToV8(isolate, sentence_starts));
}
void AutomationV8Bindings::GetSentenceEndOffsets(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
const std::vector<int>& sentence_ends =
automation_tree_manager_owner_->CalculateSentenceBoundary(
tree_wrapper, node, false );
result.Set(gin::ConvertToV8(isolate, sentence_ends));
}
void AutomationV8Bindings::GetBoundsForRange(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
int start,
int end,
bool clipped) const {
gfx::Rect global_bounds;
if (!automation_tree_manager_owner_->GetBoundsForRange(
tree_wrapper, node, start, end, clipped, &global_bounds)) {
return;
}
result.Set(RectToV8Object(isolate, global_bounds));
}
void AutomationV8Bindings::ComputeGlobalBounds(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
int x,
int y,
int width,
int height) const {
gfx::RectF local_bounds(x, y, width, height);
gfx::Rect global_bounds =
automation_tree_manager_owner_->ComputeGlobalNodeBounds(
tree_wrapper, node, local_bounds, nullptr, false );
result.Set(RectToV8Object(isolate, global_bounds));
}
void AutomationV8Bindings::GetName(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
const char* name = automation_tree_manager_owner_->GetName(node);
if (name)
result.Set(v8::String::NewFromUtf8(isolate, name).ToLocalChecked());
}
void AutomationV8Bindings::GetNextTextMatch(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
const std::string& search_str,
bool backward) const {
AXTreeID tree_id;
int node_id;
if (!automation_tree_manager_owner_->GetNextTextMatch(
tree_wrapper, node, search_str, backward, &tree_id, &node_id)) {
return;
}
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_id.ToString());
response.Set("nodeId", node_id);
result.Set(response.Build());
}
void AutomationV8Bindings::SetAccessibilityFocus(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) {
AXTreeID tree_id = tree_wrapper->GetTreeID();
automation_tree_manager_owner_->SetAccessibilityFocus(tree_id);
tree_wrapper->SetAccessibilityFocus(node->id());
}
void AutomationV8Bindings::EventListenerAdded(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
const std::tuple<ax::mojom::Event, AXEventGenerator::Event>& event_type) {
tree_wrapper->EventListenerAdded(event_type, node);
automation_tree_manager_owner_->TreeEventListenersChanged(tree_wrapper);
}
void AutomationV8Bindings::EventListenerRemoved(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node,
const std::tuple<ax::mojom::Event, AXEventGenerator::Event>& event_type) {
tree_wrapper->EventListenerRemoved(event_type, node);
automation_tree_manager_owner_->TreeEventListenersChanged(tree_wrapper);
}
void AutomationV8Bindings::GetMarkers(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
if (!node->HasIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts) ||
!node->HasIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds) ||
!node->HasIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes)) {
return;
}
const std::vector<int32_t>& marker_starts =
node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts);
const std::vector<int32_t>& marker_ends =
node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds);
const std::vector<int32_t>& marker_types =
node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes);
v8::LocalVector<v8::Object> markers(isolate);
for (size_t i = 0; i < marker_types.size(); ++i) {
gin::DataObjectBuilder marker_obj(isolate);
marker_obj.Set("startOffset", marker_starts[i]);
marker_obj.Set("endOffset", marker_ends[i]);
gin::DataObjectBuilder flags(isolate);
int32_t marker_type = marker_types[i];
int32_t marker_pos = 1;
while (marker_type) {
if (marker_type & 1) {
flags.Set(automation_v8_router_->GetMarkerTypeString(
static_cast<ax::mojom::MarkerType>(marker_pos)),
true);
}
marker_type = marker_type >> 1;
marker_pos = marker_pos << 1;
}
marker_obj.Set("flags", flags.Build());
markers.push_back(marker_obj.Build());
}
result.Set(gin::ConvertToV8(isolate, markers));
}
void AutomationV8Bindings::GetState(
const v8::FunctionCallbackInfo<v8::Value>& args) const {
v8::Isolate* isolate = automation_v8_router_->GetIsolate();
if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsNumber())
automation_v8_router_->ThrowInvalidArgumentsException();
AXTreeID tree_id =
AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id =
args[1]->Int32Value(automation_v8_router_->GetContext()).FromMaybe(0);
bool offscreen = false;
bool focused = false;
uint32_t node_state;
if (!automation_tree_manager_owner_->CalculateNodeState(
tree_id, node_id, &node_state, &offscreen, &focused)) {
return;
}
gin::DataObjectBuilder state(isolate);
uint32_t state_pos = 0, state_shifter = node_state;
while (state_shifter) {
if (state_shifter & 1)
state.Set(ToString(static_cast<ax::mojom::State>(state_pos)), true);
state_shifter = state_shifter >> 1;
state_pos++;
}
if (focused) {
state.Set(automation_v8_router_->GetFocusedStateString(), true);
}
if (offscreen)
state.Set(automation_v8_router_->GetOffscreenStateString(), true);
args.GetReturnValue().Set(state.Build());
}
void AutomationV8Bindings::GetImageAnnotation(
v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
AXNode* node) const {
std::string status_string = std::string();
auto status = node->data().GetImageAnnotationStatus();
switch (status) {
case ax::mojom::ImageAnnotationStatus::kNone:
case ax::mojom::ImageAnnotationStatus::kWillNotAnnotateDueToScheme:
case ax::mojom::ImageAnnotationStatus::kIneligibleForAnnotation:
case ax::mojom::ImageAnnotationStatus::kSilentlyEligibleForAnnotation:
break;
case ax::mojom::ImageAnnotationStatus::kEligibleForAnnotation:
case ax::mojom::ImageAnnotationStatus::kAnnotationPending:
case ax::mojom::ImageAnnotationStatus::kAnnotationEmpty:
case ax::mojom::ImageAnnotationStatus::kAnnotationAdult:
case ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed:
status_string =
automation_v8_router_->GetLocalizedStringForImageAnnotationStatus(
status);
break;
case ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded:
status_string = node->GetStringAttribute(
ax::mojom::StringAttribute::kImageAnnotation);
break;
}
if (status_string.empty())
return;
result.Set(
v8::String::NewFromUtf8(isolate, status_string.c_str()).ToLocalChecked());
}
void AutomationV8Bindings::StartCachingAccessibilityTrees(
const v8::FunctionCallbackInfo<v8::Value>& args) {
automation_v8_router_->StartCachingAccessibilityTrees();
}
void AutomationV8Bindings::StopCachingAccessibilityTrees(
const v8::FunctionCallbackInfo<v8::Value>& args) {
automation_v8_router_->StopCachingAccessibilityTrees();
automation_tree_manager_owner_->ClearCachedAccessibilityTrees();
}
}