// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/platform/browser_accessibility_win.h"
#include "base/memory/ptr_util.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
#include "ui/accessibility/platform/browser_accessibility_manager_win.h"
#include "ui/base/win/atl_module.h"
namespace ui {
// static
std::unique_ptr<BrowserAccessibility> BrowserAccessibility::Create(
BrowserAccessibilityManager* manager,
AXNode* node) {
return base::WrapUnique(new BrowserAccessibilityWin(manager, node));
}
BrowserAccessibilityWin::BrowserAccessibilityWin(
BrowserAccessibilityManager* manager,
AXNode* node)
: BrowserAccessibility(manager, node) {
win::CreateATLModuleIfNeeded();
HRESULT hr = CComObject<BrowserAccessibilityComWin>::CreateInstance(
&browser_accessibility_com_.AsEphemeralRawAddr());
DCHECK(SUCCEEDED(hr));
browser_accessibility_com_->AddRef();
browser_accessibility_com_->Init(this);
}
BrowserAccessibilityWin::~BrowserAccessibilityWin() {
if (browser_accessibility_com_) {
browser_accessibility_com_.ExtractAsDangling()->Destroy();
}
}
void BrowserAccessibilityWin::UpdatePlatformAttributes() {
GetCOM()->UpdateStep1ComputeWinAttributes();
GetCOM()->UpdateStep2ComputeHypertext();
GetCOM()->UpdateStep3FireEvents();
}
std::wstring BrowserAccessibilityWin::ComputeListItemNameFromContent() const {
DCHECK_EQ(GetRole(), ax::mojom::Role::kListItem);
DCHECK(!HasStringAttribute(ax::mojom::StringAttribute::kName));
std::wstring str;
int offset = 0;
if (node()->GetFirstChild() &&
node()->GetFirstChild()->GetRole() == ax::mojom::Role::kListMarker) {
offset = 1;
}
auto start_position = AXNodePosition::CreatePosition(*node(), offset);
auto end_position = start_position->CreatePositionAtEndOfAnchor();
auto range = AXRange(std::move(start_position), std::move(end_position));
// TODO(accessibility): We're aware that there is an issue with no space being
// generated between descendants' names in some cases when appending their
// names. For instance if we have a <li> with a child <ul> which has <li> as
// children.
str = base::UTF16ToWide(
range.GetText(AXTextConcatenationBehavior::kWithoutParagraphBreaks,
AXEmbeddedObjectBehavior::kSuppressCharacter));
return str;
}
bool BrowserAccessibilityWin::CanFireEvents() const {
// On Windows, we want to hide the subtree of a collapsed <select> element but
// we still need to fire events on those hidden nodes.
if (!IsIgnored() && GetCollapsedMenuListSelectAncestor())
return true;
// If the node changed its ignored state this frame then some events should be
// allowed, such as hide/show/structure events. If a node with no siblings
// changes aria-hidden value, this would affect whether it would be considered
// a "child of leaf" node which affects BrowserAccessibility::CanFireEvents.
if (manager()->ToBrowserAccessibilityManagerWin()->IsIgnoredChangedNode(this))
return true;
return BrowserAccessibility::CanFireEvents();
}
AXPlatformNode* BrowserAccessibilityWin::GetAXPlatformNode() const {
return GetCOM();
}
void BrowserAccessibilityWin::OnLocationChanged() {
GetCOM()->FireNativeEvent(EVENT_OBJECT_LOCATIONCHANGE);
}
std::u16string BrowserAccessibilityWin::GetHypertext() const {
return GetCOM()->AXPlatformNodeWin::GetHypertext();
}
const std::vector<gfx::NativeViewAccessible>
BrowserAccessibilityWin::GetUIADirectChildrenInRange(
AXPlatformNodeDelegate* start,
AXPlatformNodeDelegate* end) {
std::vector<gfx::NativeViewAccessible> descendants;
if (!IsIgnored() && !ShouldHideChildrenForUIA() && PlatformChildCount() > 0) {
BrowserAccessibility* start_wrapper = FromAXPlatformNodeDelegate(start);
DCHECK(start_wrapper);
BrowserAccessibility* end_wrapper = FromAXPlatformNodeDelegate(end);
DCHECK(end_wrapper);
// When either (or both) of the start/end node is the same as the common
// anchor, make them null. A null start node means that all UIA embedded
// objects from the start will be added, and a null end node means that all
// UIA embedded objects past the start of the range will be included. When
// both are null, all UIA embedded objects will be included.
if (this == start_wrapper)
start_wrapper = nullptr;
if (this == end_wrapper)
end_wrapper = nullptr;
// Don't include nodes that are before the start node - they are not in the
// range. If the start node is the one we're on right now (ie. the common
// anchor is the start anchor), include all nodes from the start.
bool in_range = !start_wrapper;
for (auto it = PlatformChildrenBegin(); it != PlatformChildrenEnd(); ++it) {
BrowserAccessibility* child = it.get();
DCHECK(child);
if (!in_range &&
(start_wrapper &&
(child == start_wrapper || start_wrapper->IsDescendantOf(child)))) {
in_range = true;
}
// The only children that should be returned are the ones that are
// unignored UIA embedded objects.
if (in_range && IsUIAEmbeddedObject(child->GetRole())) {
descendants.emplace_back(child->GetNativeViewAccessible());
}
// Don't include the nodes that follow the end of the range.
if (end_wrapper &&
(child == end_wrapper || end_wrapper->IsDescendantOf(child))) {
break;
}
}
}
return descendants;
}
gfx::NativeViewAccessible BrowserAccessibilityWin::GetNativeViewAccessible() {
return GetCOM();
}
BrowserAccessibilityComWin* BrowserAccessibilityWin::GetCOM() const {
DCHECK(browser_accessibility_com_);
return browser_accessibility_com_;
}
BrowserAccessibilityWin* ToBrowserAccessibilityWin(BrowserAccessibility* obj) {
return static_cast<BrowserAccessibilityWin*>(obj);
}
const BrowserAccessibilityWin* ToBrowserAccessibilityWin(
const BrowserAccessibility* obj) {
return static_cast<const BrowserAccessibilityWin*>(obj);
}
TextAttributeList BrowserAccessibilityWin::ComputeTextAttributes() const {
return GetCOM()->AXPlatformNodeWin::ComputeTextAttributes();
}
bool BrowserAccessibilityWin::ShouldHideChildrenForUIA() const {
return GetCOM()->AXPlatformNodeWin::ShouldHideChildrenForUIA();
}
} // namespace ui