blob: 21ad0a71c6b585f44f41223aeffb20349c1c7ff8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/aom/computed_accessible_node.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/accessibility/ax_context.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "ui/accessibility/ax_mode.h"
namespace blink {
class ComputedAccessibleNodePromiseResolver::RequestAnimationFrameCallback final
: public FrameCallback {
public:
explicit RequestAnimationFrameCallback(
ComputedAccessibleNodePromiseResolver* resolver)
: resolver_(resolver) {}
RequestAnimationFrameCallback(const RequestAnimationFrameCallback&) = delete;
RequestAnimationFrameCallback& operator=(
const RequestAnimationFrameCallback&) = delete;
void Invoke(double) override {
resolver_->continue_callback_request_id_ = 0;
resolver_->UpdateTreeAndResolve();
}
void Trace(Visitor* visitor) const override {
visitor->Trace(resolver_);
FrameCallback::Trace(visitor);
}
private:
Member<ComputedAccessibleNodePromiseResolver> resolver_;
};
ComputedAccessibleNodePromiseResolver::ComputedAccessibleNodePromiseResolver(
ScriptState* script_state,
Document& document,
AXID ax_id)
: ax_id_(ax_id),
resolver_(MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
ax_context_(std::make_unique<AXContext>(document, ui::kAXModeComplete)) {
DCHECK(ax_id);
}
ComputedAccessibleNodePromiseResolver::ComputedAccessibleNodePromiseResolver(
ScriptState* script_state,
Element& element)
: element_(element),
resolver_(MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
ax_context_(std::make_unique<AXContext>(element.GetDocument(),
ui::kAXModeComplete)) {}
ScriptPromise ComputedAccessibleNodePromiseResolver::Promise() {
return resolver_->Promise();
}
void ComputedAccessibleNodePromiseResolver::Trace(Visitor* visitor) const {
visitor->Trace(element_);
visitor->Trace(resolver_);
}
void ComputedAccessibleNodePromiseResolver::ComputeAccessibleNode() {
resolve_with_node_ = true;
EnsureUpToDate();
}
void ComputedAccessibleNodePromiseResolver::EnsureUpToDate() {
DCHECK(RuntimeEnabledFeatures::AccessibilityObjectModelEnabled());
if (continue_callback_request_id_ || !ax_context_->GetDocument())
return;
// TODO(aboxhall): Trigger a call when lifecycle is next at kPrePaintClean.
RequestAnimationFrameCallback* callback =
MakeGarbageCollected<RequestAnimationFrameCallback>(this);
continue_callback_request_id_ =
ax_context_->GetDocument()->RequestAnimationFrame(callback);
}
void ComputedAccessibleNodePromiseResolver::UpdateTreeAndResolve() {
if (!ax_context_->HasActiveDocument()) {
resolver_->Resolve();
return;
}
LocalFrame* local_frame = ax_context_->GetDocument()->GetFrame();
if (!local_frame) {
resolver_->Resolve();
return;
}
WebLocalFrameClient* client =
WebLocalFrameImpl::FromFrame(local_frame)->Client();
WebComputedAXTree* tree = client->GetOrCreateWebComputedAXTree();
tree->ComputeAccessibilityTree();
if (!resolve_with_node_) {
resolver_->Resolve();
return;
}
ax_context_->GetDocument()->View()->UpdateAllLifecyclePhasesExceptPaint(
DocumentUpdateReason::kAccessibility);
AXObjectCache& cache = ax_context_->GetAXObjectCache();
AXID ax_id = ax_id_ ? ax_id_ : cache.GetAXID(element_);
if (!ax_id || !cache.ObjectFromAXID(ax_id)) {
resolver_->Resolve(); // No AXObject exists for this element.
return;
}
ComputedAccessibleNode* accessible_node =
ax_context_->GetDocument()->GetOrCreateComputedAccessibleNode(ax_id);
DCHECK(accessible_node);
resolver_->Resolve(accessible_node);
}
// ComputedAccessibleNode ------------------------------------------------------
ComputedAccessibleNode::ComputedAccessibleNode(AXID ax_id, Document* document)
: ax_id_(ax_id),
ax_context_(std::make_unique<AXContext>(*document, ui::kAXModeComplete)) {
}
ComputedAccessibleNode::~ComputedAccessibleNode() = default;
absl::optional<bool> ComputedAccessibleNode::atomic() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_ATOMIC);
}
absl::optional<bool> ComputedAccessibleNode::busy() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_BUSY);
}
absl::optional<bool> ComputedAccessibleNode::disabled() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_DISABLED);
}
absl::optional<bool> ComputedAccessibleNode::readOnly() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_READONLY);
}
absl::optional<bool> ComputedAccessibleNode::expanded() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_EXPANDED);
}
absl::optional<bool> ComputedAccessibleNode::modal() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_MODAL);
}
absl::optional<bool> ComputedAccessibleNode::multiline() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_MULTILINE);
}
absl::optional<bool> ComputedAccessibleNode::multiselectable() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_MULTISELECTABLE);
}
absl::optional<bool> ComputedAccessibleNode::required() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_REQUIRED);
}
absl::optional<bool> ComputedAccessibleNode::selected() const {
return GetBoolAttribute(WebAOMBoolAttribute::AOM_ATTR_SELECTED);
}
absl::optional<int32_t> ComputedAccessibleNode::colCount() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_COLUMN_COUNT);
}
absl::optional<int32_t> ComputedAccessibleNode::colIndex() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_COLUMN_INDEX);
}
absl::optional<int32_t> ComputedAccessibleNode::colSpan() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_COLUMN_SPAN);
}
absl::optional<int32_t> ComputedAccessibleNode::level() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_HIERARCHICAL_LEVEL);
}
absl::optional<int32_t> ComputedAccessibleNode::posInSet() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_POS_IN_SET);
}
absl::optional<int32_t> ComputedAccessibleNode::rowCount() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_ROW_COUNT);
}
absl::optional<int32_t> ComputedAccessibleNode::rowIndex() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_ROW_INDEX);
}
absl::optional<int32_t> ComputedAccessibleNode::rowSpan() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_ROW_SPAN);
}
absl::optional<int32_t> ComputedAccessibleNode::setSize() const {
return GetIntAttribute(WebAOMIntAttribute::AOM_ATTR_SET_SIZE);
}
absl::optional<float> ComputedAccessibleNode::valueMax() const {
return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_MAX);
}
absl::optional<float> ComputedAccessibleNode::valueMin() const {
return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_MIN);
}
absl::optional<float> ComputedAccessibleNode::valueNow() const {
return GetFloatAttribute(WebAOMFloatAttribute::AOM_ATTR_VALUE_NOW);
}
ScriptPromise ComputedAccessibleNode::ensureUpToDate(
ScriptState* script_state) {
if (!GetDocument())
return ScriptPromise(); // Empty promise.
auto* resolver = MakeGarbageCollected<ComputedAccessibleNodePromiseResolver>(
script_state, *GetDocument(), ax_id_);
ScriptPromise promise = resolver->Promise();
resolver->EnsureUpToDate();
return promise;
}
const String ComputedAccessibleNode::autocomplete() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_AUTOCOMPLETE);
}
const String ComputedAccessibleNode::checked() const {
WebString out;
if (GetTree() && GetTree()->GetCheckedStateForAXNode(ax_id_, &out)) {
return out;
}
return String();
}
const String ComputedAccessibleNode::keyShortcuts() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_KEY_SHORTCUTS);
}
const String ComputedAccessibleNode::name() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_NAME);
}
const String ComputedAccessibleNode::placeholder() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_PLACEHOLDER);
}
const String ComputedAccessibleNode::role() const {
WebString out;
if (GetTree() && GetTree()->GetRoleForAXNode(ax_id_, &out)) {
return out;
}
return String();
}
const String ComputedAccessibleNode::roleDescription() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_ROLE_DESCRIPTION);
}
const String ComputedAccessibleNode::valueText() const {
return GetStringAttribute(WebAOMStringAttribute::AOM_ATTR_VALUE_TEXT);
}
ComputedAccessibleNode* ComputedAccessibleNode::parent() const {
WebComputedAXTree* tree = GetTree();
if (!tree) {
return nullptr;
}
int32_t parent_ax_id;
if (!tree->GetParentIdForAXNode(ax_id_, &parent_ax_id)) {
return nullptr;
}
return GetDocument()->GetOrCreateComputedAccessibleNode(parent_ax_id);
}
ComputedAccessibleNode* ComputedAccessibleNode::firstChild() const {
WebComputedAXTree* tree = GetTree();
if (!tree) {
return nullptr;
}
int32_t child_ax_id;
if (!tree->GetFirstChildIdForAXNode(ax_id_, &child_ax_id)) {
return nullptr;
}
return GetDocument()->GetOrCreateComputedAccessibleNode(child_ax_id);
}
ComputedAccessibleNode* ComputedAccessibleNode::lastChild() const {
WebComputedAXTree* tree = GetTree();
if (!tree) {
return nullptr;
}
int32_t child_ax_id;
if (!tree->GetLastChildIdForAXNode(ax_id_, &child_ax_id)) {
return nullptr;
}
return GetDocument()->GetOrCreateComputedAccessibleNode(child_ax_id);
}
ComputedAccessibleNode* ComputedAccessibleNode::previousSibling() const {
WebComputedAXTree* tree = GetTree();
if (!tree) {
return nullptr;
}
int32_t sibling_ax_id;
if (!tree->GetPreviousSiblingIdForAXNode(ax_id_, &sibling_ax_id)) {
return nullptr;
}
return GetDocument()->GetOrCreateComputedAccessibleNode(sibling_ax_id);
}
ComputedAccessibleNode* ComputedAccessibleNode::nextSibling() const {
WebComputedAXTree* tree = GetTree();
if (!tree) {
return nullptr;
}
int32_t sibling_ax_id;
if (!tree->GetNextSiblingIdForAXNode(ax_id_, &sibling_ax_id)) {
return nullptr;
}
return GetDocument()->GetOrCreateComputedAccessibleNode(sibling_ax_id);
}
Document* ComputedAccessibleNode::GetDocument() const {
return ax_context_->GetDocument();
}
WebComputedAXTree* ComputedAccessibleNode::GetTree() const {
if (!GetDocument())
return nullptr;
LocalFrame* local_frame = GetDocument()->GetFrame();
if (!local_frame)
return nullptr;
WebLocalFrameClient* client =
WebLocalFrameImpl::FromFrame(local_frame)->Client();
return client->GetOrCreateWebComputedAXTree();
}
absl::optional<bool> ComputedAccessibleNode::GetBoolAttribute(
WebAOMBoolAttribute attr) const {
bool value;
if (GetTree() && GetTree()->GetBoolAttributeForAXNode(ax_id_, attr, &value))
return value;
return absl::nullopt;
}
absl::optional<int32_t> ComputedAccessibleNode::GetIntAttribute(
WebAOMIntAttribute attr) const {
int32_t value;
if (GetTree() && GetTree()->GetIntAttributeForAXNode(ax_id_, attr, &value))
return value;
return absl::nullopt;
}
absl::optional<float> ComputedAccessibleNode::GetFloatAttribute(
WebAOMFloatAttribute attr) const {
float value;
if (GetTree() && GetTree()->GetFloatAttributeForAXNode(ax_id_, attr, &value))
return value;
return absl::nullopt;
}
const String ComputedAccessibleNode::GetStringAttribute(
WebAOMStringAttribute attr) const {
WebString out;
if (GetTree() && GetTree()->GetStringAttributeForAXNode(ax_id_, attr, &out)) {
return out;
}
return String();
}
void ComputedAccessibleNode::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
}
} // namespace blink