blob: b4d8e5c86c04a0e98fd3f279bf6134b9956cb181 [file] [log] [blame]
// Copyright 2019 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/modules/accessibility/ax_validation_message.h"
#include "third_party/blink/renderer/core/html/forms/listed_element.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/gfx/geometry/transform.h"
namespace blink {
AXValidationMessage::AXValidationMessage(AXObjectCacheImpl& ax_object_cache)
: AXMockObject(ax_object_cache) {}
AXValidationMessage::~AXValidationMessage() {}
bool AXValidationMessage::ComputeAccessibilityIsIgnored(
IgnoredReasons* ignored_reasons) const {
return false;
}
// TODO(accessibility) Currently we return the bounds of the focused form
// control. If this becomes an issue, return the bounds of the alert itself.
void AXValidationMessage::GetRelativeBounds(
AXObject** out_container,
gfx::RectF& out_bounds_in_container,
gfx::Transform& out_container_transform,
bool* clips_children) const {
DCHECK(out_container);
*out_container = nullptr;
out_bounds_in_container = gfx::RectF();
out_container_transform.MakeIdentity();
if (clips_children)
*clips_children = false;
ListedElement* listed_element = RelatedFormControlIfVisible();
if (!listed_element)
return;
HTMLElement& form_control = listed_element->ToHTMLElement();
if (!form_control.GetLayoutObject())
return;
*out_container = ParentObject();
if (form_control.GetLayoutObject()) {
out_bounds_in_container =
gfx::RectF(form_control.GetLayoutObject()->AbsoluteBoundingBoxRect());
}
}
bool AXValidationMessage::IsOffScreen() const {
return false;
}
bool AXValidationMessage::IsVisible() const {
bool is_visible = RelatedFormControlIfVisible();
DCHECK(!is_visible || CachedParentObject() == AXObjectCache().Root())
<< "A visible validation message's parent must be the root object'.";
return is_visible;
}
const AtomicString& AXValidationMessage::LiveRegionStatus() const {
DEFINE_STATIC_LOCAL(const AtomicString, live_region_status_assertive,
("assertive"));
return live_region_status_assertive;
}
const AtomicString& AXValidationMessage::LiveRegionRelevant() const {
DEFINE_STATIC_LOCAL(const AtomicString, live_region_relevant_additions,
("additions"));
return live_region_relevant_additions;
}
ax::mojom::blink::Role AXValidationMessage::NativeRoleIgnoringAria() const {
return ax::mojom::blink::Role::kAlert;
}
ListedElement* AXValidationMessage::RelatedFormControlIfVisible() const {
AXObject* focused_object = AXObjectCache().FocusedObject();
if (!focused_object)
return nullptr;
Element* element = focused_object->GetElement();
if (!element)
return nullptr;
ListedElement* form_control = ListedElement::From(*element);
if (!form_control || !form_control->IsValidationMessageVisible())
return nullptr;
// The method IsValidationMessageVisible() is a superset of
// IsNotCandidateOrValid(), but has the benefit of not being true until user
// has tried to submit data. Waiting until the error message is visible
// before presenting to screen reader is preferable over hearing about the
// error while the user is still attempting to input data in the first place.
return form_control->IsValidationMessageVisible() ? form_control : nullptr;
}
String AXValidationMessage::TextAlternative(
bool recursive,
const AXObject* aria_label_or_description_root,
AXObjectSet& visited,
ax::mojom::NameFrom& name_from,
AXRelatedObjectVector* related_objects,
NameSources* name_sources) const {
// If nameSources is non-null, relatedObjects is used in filling it in, so it
// must be non-null as well.
if (name_sources)
DCHECK(related_objects);
ListedElement* form_control_element = RelatedFormControlIfVisible();
if (!form_control_element)
return String();
StringBuilder message;
message.Append(form_control_element->validationMessage());
if (form_control_element->ValidationSubMessage()) {
message.Append(' ');
message.Append(form_control_element->ValidationSubMessage());
}
if (name_sources) {
name_sources->push_back(NameSource(true));
name_sources->back().type = ax::mojom::NameFrom::kContents;
name_sources->back().text = message.ToString();
}
return message.ToString();
}
} // namespace blink