cefclient: OSR accessibility enhancements (see issue #2604)

- Implement multi-tree support (e.g. page with iframes contains several trees)
- Implement OnAccessibilityLocationChange support
- Add scroll offset calculation
- Fix new Chromium tree layout parsing
- Fix uninitialized OsrAXNode::offset_container_id_
- Fix Windows non ascii AxName, AxValue, AxDescription representation
This commit is contained in:
Andrei Kurushin 2019-03-21 16:56:06 +00:00 committed by Marshall Greenblatt
parent b3468451f5
commit af349ade33
13 changed files with 411 additions and 137 deletions

View File

@ -1362,6 +1362,11 @@ void BrowserWindowOsrGtk::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
CEF_REQUIRE_UI_THREAD(); CEF_REQUIRE_UI_THREAD();
} }
void BrowserWindowOsrGtk::UpdateAccessibilityLocation(
CefRefPtr<CefValue> value) {
CEF_REQUIRE_UI_THREAD();
}
void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) { void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
REQUIRE_MAIN_THREAD(); REQUIRE_MAIN_THREAD();
DCHECK(!glarea_); DCHECK(!glarea_);

View File

@ -89,6 +89,7 @@ class BrowserWindowOsrGtk : public BrowserWindow,
const CefRange& selection_range, const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE; const CefRenderHandler::RectList& character_bounds) OVERRIDE;
void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE; void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
private: private:
~BrowserWindowOsrGtk(); ~BrowserWindowOsrGtk();

View File

@ -85,6 +85,7 @@ class BrowserWindowOsrMac : public BrowserWindow,
const CefRenderHandler::RectList& character_bounds) OVERRIDE; const CefRenderHandler::RectList& character_bounds) OVERRIDE;
void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE; void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
private: private:
// Create the NSView. // Create the NSView.

View File

@ -109,6 +109,7 @@ CefTextInputClientOSRMac* GetInputClientFromContext(
character_bounds: character_bounds:
(const CefRenderHandler::RectList&)character_bounds; (const CefRenderHandler::RectList&)character_bounds;
- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value; - (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value;
- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value;
@end @end
namespace { namespace {
@ -1306,6 +1307,18 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
} }
return; return;
} }
- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value {
if (accessibility_helper_) {
accessibility_helper_->UpdateAccessibilityLocation(value);
}
if (accessibility_helper_) {
NSAccessibilityPostNotification(self,
NSAccessibilityValueChangedNotification);
}
return;
}
@end @end
namespace client { namespace client {

View File

@ -167,4 +167,12 @@ void ClientHandlerOsr::OnAccessibilityTreeChange(CefRefPtr<CefValue> value) {
osr_delegate_->UpdateAccessibilityTree(value); osr_delegate_->UpdateAccessibilityTree(value);
} }
void ClientHandlerOsr::OnAccessibilityLocationChange(
CefRefPtr<CefValue> value) {
CEF_REQUIRE_UI_THREAD();
if (!osr_delegate_)
return;
osr_delegate_->UpdateAccessibilityLocation(value);
}
} // namespace client } // namespace client

View File

@ -68,6 +68,8 @@ class ClientHandlerOsr : public ClientHandler,
virtual void UpdateAccessibilityTree(CefRefPtr<CefValue> value) = 0; virtual void UpdateAccessibilityTree(CefRefPtr<CefValue> value) = 0;
virtual void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) = 0;
protected: protected:
virtual ~OsrDelegate() {} virtual ~OsrDelegate() {}
}; };
@ -130,7 +132,7 @@ class ClientHandlerOsr : public ClientHandler,
// CefAccessibilityHandler methods. // CefAccessibilityHandler methods.
void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) OVERRIDE; void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) OVERRIDE;
void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) OVERRIDE {} void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) OVERRIDE;
private: private:
// Only accessed on the UI thread. // Only accessed on the UI thread.

View File

@ -7,134 +7,260 @@
namespace client { namespace client {
OsrAXTree::OsrAXTree() : parent_tree_id_(-1), root_node_id_(-1) {}
OsrAXNode* OsrAXTree::GetNode(int nodeId) const {
auto result = node_map_.find(nodeId);
if (result != node_map_.end()) {
return result->second;
}
return NULL;
}
void OsrAXTree::EraseNode(int nodeId) {
node_map_.erase(nodeId);
}
void OsrAXTree::AddNode(OsrAXNode* node) {
node_map_[node->OsrAXNodeId()] = node;
}
void OsrAXTree::UpdateTreeData(CefRefPtr<CefDictionaryValue> value) {
if (value->HasKey("parent_tree_id")) {
parent_tree_id_ =
OsrAccessibilityHelper::CastToInt(value->GetValue("parent_tree_id"));
} else {
parent_tree_id_ = -1;
}
// may also update following:
// doctype, title, url, mimetype
}
OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value, OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
CefRefPtr<CefBrowser> browser) CefRefPtr<CefBrowser> browser)
: root_node_id_(-1), focused_node_id_(-1), browser_(browser) { : root_tree_id_(-1),
focused_tree_id_(-1),
focused_node_id_(-1),
browser_(browser) {
UpdateAccessibilityTree(value); UpdateAccessibilityTree(value);
} }
int OsrAccessibilityHelper::CastToInt(CefRefPtr<CefValue> value) {
if (value->GetType() == VTYPE_STRING) {
const std::string& str = value->GetString();
return atoi(str.c_str());
} else {
return value->GetInt();
}
}
void OsrAccessibilityHelper::UpdateAccessibilityLocation(
CefRefPtr<CefValue> value) {
if (!value || value->GetType() != VTYPE_LIST) {
return;
}
CefRefPtr<CefListValue> locationChangeList = value->GetList();
size_t locationChangeCount = locationChangeList->GetSize();
if (locationChangeCount == 0) {
return;
}
for (size_t i = 0; i < locationChangeCount; i++) {
CefRefPtr<CefDictionaryValue> locationChangeDict =
locationChangeList->GetDictionary(i);
if (!locationChangeDict->HasKey("ax_tree_id") ||
!locationChangeDict->HasKey("new_location") ||
!locationChangeDict->HasKey("id")) {
continue;
}
int treeId = CastToInt(locationChangeDict->GetValue("ax_tree_id"));
int nodeId = CastToInt(locationChangeDict->GetValue("id"));
CefRefPtr<CefDictionaryValue> newLocationDict =
locationChangeDict->GetDictionary("new_location");
if (!newLocationDict) {
continue;
}
OsrAXNode* node = GetNode(treeId, nodeId);
if (!node) {
continue;
}
node->UpdateLocation(newLocationDict);
}
}
void OsrAccessibilityHelper::UpdateAccessibilityTree( void OsrAccessibilityHelper::UpdateAccessibilityTree(
CefRefPtr<CefValue> value) { CefRefPtr<CefValue> value) {
if (value && value->GetType() == VTYPE_LIST) { if (!value || value->GetType() != VTYPE_DICTIONARY) {
CefRefPtr<CefListValue> list = value->GetList(); return;
size_t numEvents = list->GetSize();
if (numEvents > 0) {
for (size_t i = 0; i < numEvents; i++) {
CefRefPtr<CefDictionaryValue> event = list->GetDictionary(i);
if (event && event->HasKey("event_type") && event->HasKey("update")) {
std::string event_type = event->GetString("event_type");
CefRefPtr<CefDictionaryValue> update = event->GetDictionary("update");
if (event_type == "layoutComplete")
UpdateLayout(update);
if (event_type == "focus" && event->HasKey("id")) {
// Update focused node id
focused_node_id_ = event->GetInt("id");
UpdateFocusedNode(update);
}
}
}
}
} }
CefRefPtr<CefDictionaryValue> mainDict = value->GetDictionary();
if (!mainDict->HasKey("ax_tree_id") || !mainDict->HasKey("updates")) {
return;
}
int treeId = CastToInt(mainDict->GetValue("ax_tree_id"));
CefRefPtr<CefListValue> updatesList = mainDict->GetList("updates");
size_t updatesCount = updatesList->GetSize();
if (updatesCount == 0) {
return;
}
for (size_t i = 0; i < updatesCount; i++) {
CefRefPtr<CefDictionaryValue> updateDict = updatesList->GetDictionary(i);
UpdateLayout(treeId, updateDict);
}
}
OsrAXNode* OsrAccessibilityHelper::GetRootNode() const {
return GetTreeRootNode(root_tree_id_);
}
OsrAXNode* OsrAccessibilityHelper::GetFocusedNode() const {
auto tree = accessibility_node_map_.find(focused_tree_id_);
if (tree != accessibility_node_map_.end()) {
return tree->second.GetNode(focused_node_id_);
}
return NULL;
}
OsrAXNode* OsrAccessibilityHelper::GetTreeRootNode(int treeId) const {
auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
return tree->second.GetNode(tree->second.GetRootNodeId());
}
return NULL;
} }
void OsrAccessibilityHelper::UpdateLayout( void OsrAccessibilityHelper::UpdateLayout(
int treeId,
CefRefPtr<CefDictionaryValue> update) { CefRefPtr<CefDictionaryValue> update) {
if (update) { if (!update) {
CefRefPtr<CefDictionaryValue> tree_data; return;
// get tree data }
if (update->HasKey("has_tree_data") && update->GetBool("has_tree_data"))
tree_data = update->GetDictionary("tree_data");
// If a node is to be cleared // If a node is to be cleared
if (update->HasKey("node_id_to_clear")) { if (update->HasKey("node_id_to_clear")) {
int node_id_to_clear = update->GetInt("node_id_to_clear"); int nodeId = CastToInt(update->GetValue("node_id_to_clear"));
// reset root node if that is to be cleared // reset root node if that is to be cleared
if (node_id_to_clear == root_node_id_) auto tree = accessibility_node_map_.find(treeId);
root_node_id_ = -1; if (tree != accessibility_node_map_.end()) {
OsrAXNode* node = GetNode(node_id_to_clear); if (tree->second.GetRootNodeId() == nodeId) {
DestroyNode(node); root_tree_id_ = -1;
} tree->second.SetRootNodeId(-1);
if (update->HasKey("root_id"))
root_node_id_ = update->GetInt("root_id");
if (tree_data && tree_data->HasKey("focus_id"))
focused_node_id_ = tree_data->GetInt("focus_id");
// Now initialize/update the node data.
if (update->HasKey("nodes")) {
CefRefPtr<CefListValue> nodes = update->GetList("nodes");
for (size_t index = 0; index < nodes->GetSize(); index++) {
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
if (node) {
int node_id = node->GetInt("id");
OsrAXNode* axNode = GetNode(node_id);
// Create if it is a new one
if (axNode) {
axNode->UpdateValue(node);
} else {
axNode = OsrAXNode::CreateNode(node, this);
accessibility_node_map_[node_id] = axNode;
}
}
} }
} }
if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
UpdateFocusedNode(-1, -1);
}
OsrAXNode* node = GetNode(treeId, nodeId);
DestroyNode(node);
} }
}
void OsrAccessibilityHelper::UpdateFocusedNode( // get tree data
CefRefPtr<CefDictionaryValue> update) { if (update->HasKey("tree_data") && update->HasKey("has_tree_data") &&
if (update && update->HasKey("nodes")) { update->GetBool("has_tree_data")) {
CefRefPtr<CefDictionaryValue> tree_data =
update->GetDictionary("tree_data");
auto& tree = accessibility_node_map_[treeId];
tree.UpdateTreeData(tree_data);
if (tree.GetParentTreeId() == -1) {
root_tree_id_ = treeId;
}
if (tree_data->HasKey("focus_id") && tree_data->HasKey("focused_tree_id")) {
UpdateFocusedNode(CastToInt(tree_data->GetValue("focused_tree_id")),
CastToInt(tree_data->GetValue("focus_id")));
}
}
// Now initialize/update the node data.
if (update->HasKey("nodes")) {
CefRefPtr<CefListValue> nodes = update->GetList("nodes"); CefRefPtr<CefListValue> nodes = update->GetList("nodes");
for (size_t index = 0; index < nodes->GetSize(); index++) { for (size_t index = 0; index < nodes->GetSize(); index++) {
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index); CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
if (node) { if (node) {
int node_id = node->GetInt("id"); auto& tree = accessibility_node_map_[treeId];
OsrAXNode* axNode = GetNode(node_id); int nodeId = CastToInt(node->GetValue("id"));
OsrAXNode* axNode = tree.GetNode(nodeId);
// Create if it is a new one // Create if it is a new one
if (axNode) { if (axNode) {
axNode->UpdateValue(node); axNode->UpdateValue(node);
} else { } else {
axNode = OsrAXNode::CreateNode(node, this); axNode = OsrAXNode::CreateNode(treeId, nodeId, node, this);
accessibility_node_map_[node_id] = axNode; tree.AddNode(axNode);
} }
} }
} }
} }
if (update->HasKey("root_id")) {
int nodeId = CastToInt(update->GetValue("root_id"));
OsrAXNode* node = GetNode(treeId, nodeId);
if (node != NULL) {
auto& tree = accessibility_node_map_[treeId];
tree.SetRootNodeId(nodeId);
}
}
}
void OsrAccessibilityHelper::UpdateFocusedNode(int treeId, int nodeId) {
if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
return;
}
focused_tree_id_ = treeId;
focused_node_id_ = nodeId;
// Now Notify Screen Reader // Now Notify Screen Reader
OsrAXNode* axNode = GetFocusedNode(); OsrAXNode* axNode = GetFocusedNode();
// Fallback to Root if (axNode) {
if (!axNode) axNode->NotifyAccessibilityEvent("focus");
axNode = GetRootNode(); }
axNode->NotifyAccessibilityEvent("focus");
} }
void OsrAccessibilityHelper::Reset() { void OsrAccessibilityHelper::Reset() {
accessibility_node_map_.clear(); accessibility_node_map_.clear();
root_node_id_ = focused_node_id_ = -1; root_tree_id_ = -1;
focused_tree_id_ = focused_node_id_ = -1;
} }
void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) { void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
if (node) { if (node) {
int treeId = node->OsrAXTreeId();
int numChilds = node->GetChildCount(); int numChilds = node->GetChildCount();
if (numChilds > 0) { if (numChilds > 0) {
for (int i = 0; i < numChilds; i++) { for (int i = 0; i < numChilds; i++) {
DestroyNode(node->ChildAtIndex(i)); OsrAXNode* childNode = node->ChildAtIndex(i);
if (!childNode) {
continue;
}
childNode->SetParent(NULL);
if (childNode->OsrAXTreeId() == treeId) {
DestroyNode(childNode);
}
} }
} }
accessibility_node_map_.erase(node->OsrAXNodeId()); auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
tree->second.EraseNode(node->OsrAXNodeId());
}
node->Destroy(); node->Destroy();
} }
} }
OsrAXNode* OsrAccessibilityHelper::GetNode(int nodeId) const { OsrAXNode* OsrAccessibilityHelper::GetNode(int treeId, int nodeId) const {
if (nodeId != -1 && auto tree = accessibility_node_map_.find(treeId);
accessibility_node_map_.find(nodeId) != accessibility_node_map_.end()) { if (tree != accessibility_node_map_.end()) {
return accessibility_node_map_.at(nodeId); return tree->second.GetNode(nodeId);
} }
return NULL; return NULL;

View File

@ -12,6 +12,24 @@
namespace client { namespace client {
class OsrAXNode; class OsrAXNode;
class OsrAccessibilityHelper;
class OsrAXTree {
public:
OsrAXTree();
OsrAXNode* GetNode(int nodeId) const;
void EraseNode(int nodeId);
void UpdateTreeData(CefRefPtr<CefDictionaryValue> value);
void AddNode(OsrAXNode* node);
int GetParentTreeId() const { return parent_tree_id_; }
int GetRootNodeId() const { return root_node_id_; }
void SetRootNodeId(int nodeId) { root_node_id_ = nodeId; }
private:
int parent_tree_id_;
int root_node_id_;
std::map<int, OsrAXNode*> node_map_;
};
// Helper class that abstracts Renderer Accessibility tree and provides a // Helper class that abstracts Renderer Accessibility tree and provides a
// uniform interface to be consumed by IAccessible interface on Windows and // uniform interface to be consumed by IAccessible interface on Windows and
@ -23,9 +41,11 @@ class OsrAccessibilityHelper {
void UpdateAccessibilityTree(CefRefPtr<CefValue> value); void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
OsrAXNode* GetRootNode() const { return GetNode(root_node_id_); } void UpdateAccessibilityLocation(CefRefPtr<CefValue> value);
OsrAXNode* GetFocusedNode() const { return GetNode(focused_node_id_); } OsrAXNode* GetRootNode() const;
OsrAXNode* GetFocusedNode() const;
CefWindowHandle GetWindowHandle() const { CefWindowHandle GetWindowHandle() const {
return browser_->GetHost()->GetWindowHandle(); return browser_->GetHost()->GetWindowHandle();
@ -33,23 +53,26 @@ class OsrAccessibilityHelper {
CefRefPtr<CefBrowser> GetBrowser() const { return browser_; } CefRefPtr<CefBrowser> GetBrowser() const { return browser_; }
OsrAXNode* GetNode(int nodeId) const; OsrAXNode* GetNode(int treeId, int nodeId) const;
OsrAXNode* GetTreeRootNode(int treeId) const;
static int CastToInt(CefRefPtr<CefValue> value);
private: private:
OsrAXNode* CreateNode(OsrAXNode* parent, CefRefPtr<CefDictionaryValue> value);
void Reset(); void Reset();
void UpdateLayout(CefRefPtr<CefDictionaryValue> update); void UpdateLayout(int treeId, CefRefPtr<CefDictionaryValue> update);
void UpdateFocusedNode(CefRefPtr<CefDictionaryValue> update); void UpdateFocusedNode(int treeId, int nodeId);
// Destroy the node and remove from Map // Destroy the node and remove from Map
void DestroyNode(OsrAXNode* node); void DestroyNode(OsrAXNode* node);
int root_node_id_; int root_tree_id_;
int focused_tree_id_;
int focused_node_id_; int focused_node_id_;
CefRefPtr<CefBrowser> browser_; CefRefPtr<CefBrowser> browser_;
std::map<int, OsrAXNode*> accessibility_node_map_; std::map<int, OsrAXTree> accessibility_node_map_;
}; };
} // namespace client } // namespace client

View File

@ -11,52 +11,88 @@
namespace client { namespace client {
OsrAXNode::OsrAXNode(CefRefPtr<CefDictionaryValue> value, OsrAXNode::OsrAXNode(int treeId,
int nodeId,
CefRefPtr<CefDictionaryValue> value,
OsrAccessibilityHelper* helper) OsrAccessibilityHelper* helper)
: node_id_(-1), : tree_id_(treeId),
node_id_(nodeId),
child_tree_id_(-1),
platform_accessibility_(NULL), platform_accessibility_(NULL),
parent_(NULL), parent_(NULL),
offset_container_id_(-1),
accessibility_helper_(helper) { accessibility_helper_(helper) {
UpdateValue(value); UpdateValue(value);
} }
void OsrAXNode::UpdateLocation(CefRefPtr<CefDictionaryValue> value) {
// Update Bounds
if (value->HasKey("bounds")) {
CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("bounds");
if (loc) {
location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
loc->GetDouble("width"), loc->GetDouble("height"));
}
}
// Update offsets
if (value->HasKey("offset_container_id")) {
offset_container_id_ = OsrAccessibilityHelper::CastToInt(
value->GetValue("offset_container_id"));
}
}
void OsrAXNode::UpdateValue(CefRefPtr<CefDictionaryValue> value) { void OsrAXNode::UpdateValue(CefRefPtr<CefDictionaryValue> value) {
if (value && value->HasKey("id")) { if (value->HasKey("role"))
node_id_ = value->GetInt("id"); role_ = value->GetString("role");
if (value->HasKey("role")) if (value->HasKey("child_ids")) {
role_ = value->GetString("role"); CefRefPtr<CefListValue> childs = value->GetList("child_ids");
// Reset child Ids
child_ids_.clear();
for (size_t idx = 0; idx < childs->GetSize(); idx++)
child_ids_.push_back(
OsrAccessibilityHelper::CastToInt(childs->GetValue(idx)));
}
// Update Location
if (value->HasKey("location")) {
CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("location");
if (loc) {
location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
loc->GetDouble("width"), loc->GetDouble("height"));
}
}
// Update offsets
if (value->HasKey("offset_container_id")) {
offset_container_id_ = OsrAccessibilityHelper::CastToInt(
value->GetValue("offset_container_id"));
}
// Update attributes
if (value->HasKey("attributes")) {
child_tree_id_ = -1;
if (value->HasKey("child_ids")) { attributes_ = value->GetDictionary("attributes");
CefRefPtr<CefListValue> childs = value->GetList("child_ids");
// Reset child Ids
child_ids_.clear();
for (size_t idx = 0; idx < childs->GetSize(); idx++)
child_ids_.push_back(childs->GetInt(idx));
}
// Update Location
if (value->HasKey("location")) {
CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("location");
if (loc) {
location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
loc->GetDouble("width"), loc->GetDouble("height"));
}
}
// Update offsets
if (value->HasKey("offset_container_id")) {
offset_container_id_ = value->GetInt("offset_container_id");
}
// Update attributes
if (value->HasKey("attributes")) {
attributes_ = value->GetDictionary("attributes");
if (attributes_ && attributes_->HasKey("name")) if (attributes_) {
name_ = attributes_->GetString("name"); scroll_.x = attributes_->HasKey("scrollX")
if (attributes_ && attributes_->HasKey("value")) ? OsrAccessibilityHelper::CastToInt(
value_ = attributes_->GetString("value"); attributes_->GetValue("scrollX"))
if (attributes_ && attributes_->HasKey("description")) : 0;
description_ = attributes_->GetString("description"); scroll_.y = attributes_->HasKey("scrollY")
? OsrAccessibilityHelper::CastToInt(
attributes_->GetValue("scrollY"))
: 0;
} }
if (attributes_ && attributes_->HasKey("childTreeId")) {
child_tree_id_ = OsrAccessibilityHelper::CastToInt(
attributes_->GetValue("childTreeId"));
}
if (attributes_ && attributes_->HasKey("name"))
name_ = attributes_->GetString("name");
if (attributes_ && attributes_->HasKey("value"))
value_ = attributes_->GetString("value");
if (attributes_ && attributes_->HasKey("description"))
description_ = attributes_->GetString("description");
} }
} }
@ -78,8 +114,21 @@ void OsrAXNode::SetParent(OsrAXNode* parent) {
CefRect OsrAXNode::AxLocation() const { CefRect OsrAXNode::AxLocation() const {
CefRect loc = location_; CefRect loc = location_;
OsrAXNode* offsetNode = accessibility_helper_->GetNode(offset_container_id_); loc.x -= scroll_.x;
// Add offset from parent Lcoation loc.y -= scroll_.y;
OsrAXNode* offsetNode =
accessibility_helper_->GetNode(OsrAXTreeId(), offset_container_id_);
if (!offsetNode) {
OsrAXNode* p = parent_;
while (p) {
if (p->OsrAXTreeId() != OsrAXTreeId()) {
offsetNode = p;
break;
}
p = p->parent_;
}
}
// Add offset from parent Location
if (offsetNode) { if (offsetNode) {
CefRect offset = offsetNode->AxLocation(); CefRect offset = offsetNode->AxLocation();
loc.x += offset.x; loc.x += offset.x;
@ -88,17 +137,39 @@ CefRect OsrAXNode::AxLocation() const {
return loc; return loc;
} }
int OsrAXNode::GetChildCount() const {
int count = static_cast<int>(child_ids_.size());
if (child_tree_id_ >= 0) {
OsrAXNode* childTreeRootNode =
accessibility_helper_->GetTreeRootNode(child_tree_id_);
if (childTreeRootNode) {
count++;
}
}
return count;
}
OsrAXNode* OsrAXNode::ChildAtIndex(int index) const { OsrAXNode* OsrAXNode::ChildAtIndex(int index) const {
if (index < GetChildCount()) int count = static_cast<int>(child_ids_.size());
return accessibility_helper_->GetNode(child_ids_[index]); if (index < count)
else return accessibility_helper_->GetNode(OsrAXTreeId(), child_ids_[index]);
return NULL; if ((index == count) && (child_tree_id_ >= 0)) {
OsrAXNode* childTreeRootNode =
accessibility_helper_->GetTreeRootNode(child_tree_id_);
if (childTreeRootNode) {
return childTreeRootNode;
}
}
return NULL;
} }
// Create and return the platform specific OsrAXNode Object // Create and return the platform specific OsrAXNode Object
OsrAXNode* OsrAXNode::CreateNode(CefRefPtr<CefDictionaryValue> value, OsrAXNode* OsrAXNode::CreateNode(int treeId,
int nodeId,
CefRefPtr<CefDictionaryValue> value,
OsrAccessibilityHelper* helper) { OsrAccessibilityHelper* helper) {
return new OsrAXNode(value, helper); return new OsrAXNode(treeId, nodeId, value, helper);
} }
} // namespace client } // namespace client

View File

@ -33,12 +33,17 @@ class OsrAccessibilityHelper;
class OsrAXNode { class OsrAXNode {
public: public:
// Create and return the platform specific OsrAXNode Object. // Create and return the platform specific OsrAXNode Object.
static OsrAXNode* CreateNode(CefRefPtr<CefDictionaryValue> value, static OsrAXNode* CreateNode(int treeId,
int nodeId,
CefRefPtr<CefDictionaryValue> value,
OsrAccessibilityHelper* helper); OsrAccessibilityHelper* helper);
// Update Value. // Update Value.
void UpdateValue(CefRefPtr<CefDictionaryValue> value); void UpdateValue(CefRefPtr<CefDictionaryValue> value);
// UpdateLocation
void UpdateLocation(CefRefPtr<CefDictionaryValue> value);
// Fire a platform-specific notification that an event has occurred on // Fire a platform-specific notification that an event has occurred on
// this object. // this object.
void NotifyAccessibilityEvent(std::string event_type) const; void NotifyAccessibilityEvent(std::string event_type) const;
@ -58,13 +63,15 @@ class OsrAXNode {
return accessibility_helper_; return accessibility_helper_;
} }
int GetChildCount() const { return static_cast<int>(child_ids_.size()); } int GetChildCount() const;
// Return the Child at the specified index // Return the Child at the specified index
OsrAXNode* ChildAtIndex(int index) const; OsrAXNode* ChildAtIndex(int index) const;
const CefString& AxRole() const { return role_; } const CefString& AxRole() const { return role_; }
int OsrAXTreeId() const { return tree_id_; }
int OsrAXNodeId() const { return node_id_; } int OsrAXNodeId() const { return node_id_; }
const CefString& AxValue() const { return value_; } const CefString& AxValue() const { return value_; }
@ -82,15 +89,20 @@ class OsrAXNode {
void SetParent(OsrAXNode* parent); void SetParent(OsrAXNode* parent);
protected: protected:
OsrAXNode(CefRefPtr<CefDictionaryValue> value, OsrAXNode(int treeId,
int nodeId,
CefRefPtr<CefDictionaryValue> value,
OsrAccessibilityHelper* helper); OsrAccessibilityHelper* helper);
int tree_id_;
int node_id_; int node_id_;
int child_tree_id_;
CefString role_; CefString role_;
CefString value_; CefString value_;
CefString name_; CefString name_;
CefString description_; CefString description_;
CefRect location_; CefRect location_;
CefPoint scroll_;
std::vector<int> child_ids_; std::vector<int> child_ids_;
CefNativeAccessible* platform_accessibility_; CefNativeAccessible* platform_accessibility_;
OsrAXNode* parent_; OsrAXNode* parent_;

View File

@ -347,7 +347,7 @@ STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR* pszName) {
HRESULT retCode = DATACHECK(node_); HRESULT retCode = DATACHECK(node_);
if (SUCCEEDED(retCode)) { if (SUCCEEDED(retCode)) {
if (pszName && VALID_CHILDID(varChild)) { if (pszName && VALID_CHILDID(varChild)) {
std::string name = node_->AxName(); std::wstring name = node_->AxName();
CComBSTR bstrResult(name.c_str()); CComBSTR bstrResult(name.c_str());
*pszName = bstrResult.Detach(); *pszName = bstrResult.Detach();
} }
@ -362,7 +362,7 @@ STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) {
HRESULT retCode = DATACHECK(node_); HRESULT retCode = DATACHECK(node_);
if (SUCCEEDED(retCode)) { if (SUCCEEDED(retCode)) {
if (pszValue && VALID_CHILDID(varChild)) { if (pszValue && VALID_CHILDID(varChild)) {
std::string name = node_->AxValue(); std::wstring name = node_->AxValue();
CComBSTR bstrResult(name.c_str()); CComBSTR bstrResult(name.c_str());
*pszValue = bstrResult.Detach(); *pszValue = bstrResult.Detach();
} }
@ -378,7 +378,7 @@ STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild,
HRESULT retCode = DATACHECK(node_); HRESULT retCode = DATACHECK(node_);
if (SUCCEEDED(retCode)) { if (SUCCEEDED(retCode)) {
if (pszDescription && VALID_CHILDID(varChild)) { if (pszDescription && VALID_CHILDID(varChild)) {
std::string name = node_->AxDescription(); std::wstring name = node_->AxDescription();
CComBSTR bstrResult(name.c_str()); CComBSTR bstrResult(name.c_str());
*pszDescription = bstrResult.Detach(); *pszDescription = bstrResult.Detach();
} }

View File

@ -1058,6 +1058,16 @@ void OsrWindowWin::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
#endif // defined(CEF_USE_ATL) #endif // defined(CEF_USE_ATL)
} }
void OsrWindowWin::UpdateAccessibilityLocation(CefRefPtr<CefValue> value) {
CEF_REQUIRE_UI_THREAD();
#if defined(CEF_USE_ATL)
if (accessibility_handler_) {
accessibility_handler_->UpdateAccessibilityLocation(value);
}
#endif // defined(CEF_USE_ATL)
}
#if defined(CEF_USE_ATL) #if defined(CEF_USE_ATL)
CefBrowserHost::DragOperationsMask OsrWindowWin::OnDragEnter( CefBrowserHost::DragOperationsMask OsrWindowWin::OnDragEnter(

View File

@ -146,7 +146,9 @@ class OsrWindowWin
const CefRange& selection_range, const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE; const CefRenderHandler::RectList& character_bounds) OVERRIDE;
void UpdateAccessibilityTree(CefRefPtr<CefValue> value); void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
void UpdateAccessibilityLocation(CefRefPtr<CefValue> value) OVERRIDE;
#if defined(CEF_USE_ATL) #if defined(CEF_USE_ATL)
// OsrDragEvents methods. // OsrDragEvents methods.