blob: 8afac6846cecd387fed90d95783b0a829ad1bd50 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/accessibility/AXRadioInput.h"
#include "core/InputTypeNames.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/forms/RadioInputType.h"
#include "core/layout/LayoutObject.h"
#include "modules/accessibility/AXObjectCacheImpl.h"
namespace blink {
using namespace HTMLNames;
AXRadioInput::AXRadioInput(LayoutObject* layoutObject,
AXObjectCacheImpl& axObjectCache)
: AXLayoutObject(layoutObject, axObjectCache) {
// Updates posInSet and setSize for the current object and the next objects.
if (!calculatePosInSet())
return;
// When a new object is inserted, it needs to update setSize for the previous
// objects.
requestUpdateToNextNode(false);
}
AXRadioInput* AXRadioInput::create(LayoutObject* layoutObject,
AXObjectCacheImpl& axObjectCache) {
return new AXRadioInput(layoutObject, axObjectCache);
}
void AXRadioInput::updatePosAndSetSize(int position) {
if (position)
m_posInSet = position;
m_setSize = sizeOfRadioGroup();
}
void AXRadioInput::requestUpdateToNextNode(bool forward) {
HTMLInputElement* nextElement =
RadioInputType::nextRadioButtonInGroup(element(), forward);
AXObject* nextAXobject = axObjectCache().get(nextElement);
if (!nextAXobject || !nextAXobject->isAXRadioInput())
return;
int position = 0;
if (forward)
position = posInSet() + 1;
// If it is backward, it keeps position as positions are already assigned for
// previous objects. updatePosAndSetSize() is called with '0' and it doesn't
// modify m_posInSet and updates m_setSize as size is increased.
toAXRadioInput(nextAXobject)->updatePosAndSetSize(position);
axObjectCache().postNotification(nextAXobject,
AXObjectCacheImpl::AXAriaAttributeChanged);
toAXRadioInput(nextAXobject)->requestUpdateToNextNode(forward);
}
HTMLInputElement* AXRadioInput::findFirstRadioButtonInGroup(
HTMLInputElement* current) const {
while (HTMLInputElement* prevElement =
RadioInputType::nextRadioButtonInGroup(current, false))
current = prevElement;
return current;
}
int AXRadioInput::posInSet() const {
if (hasAttribute(aria_posinsetAttr))
return getAttribute(aria_posinsetAttr).toInt();
return m_posInSet;
}
int AXRadioInput::setSize() const {
if (hasAttribute(aria_setsizeAttr))
return getAttribute(aria_setsizeAttr).toInt();
return m_setSize;
}
bool AXRadioInput::calculatePosInSet() {
// Calculate 'posInSet' attribute when AXRadioInputs need to be updated
// as a new AXRadioInput Object is added or one of objects from RadioGroup is
// removed.
bool needToUpdatePrev = false;
int position = 1;
HTMLInputElement* prevElement =
RadioInputType::nextRadioButtonInGroup(element(), false);
if (prevElement) {
AXObject* object = axObjectCache().get(prevElement);
// If the previous element doesn't have AXObject yet, caculate position from
// the first element. Otherwise, get position from the previous AXObject.
if (!object || !object->isAXRadioInput()) {
position = countFromFirstElement();
} else {
position = object->posInSet() + 1;
// It returns true if previous objects need to be updated.
// When AX tree exists already and a new node is inserted,
// as updating is started from the inserted node,
// we need to update setSize for previous nodes.
if (setSize() != object->setSize())
needToUpdatePrev = true;
}
}
updatePosAndSetSize(position);
// If it is not the last element, request update to the next node.
if (position != setSize())
requestUpdateToNextNode(true);
return needToUpdatePrev;
}
int AXRadioInput::countFromFirstElement() const {
int count = 1;
HTMLInputElement* current = element();
while (HTMLInputElement* prevElement =
RadioInputType::nextRadioButtonInGroup(current, false)) {
current = prevElement;
count++;
}
return count;
}
HTMLInputElement* AXRadioInput::element() const {
return toHTMLInputElement(m_layoutObject->node());
}
int AXRadioInput::sizeOfRadioGroup() const {
int size = element()->sizeOfRadioGroup();
// If it has no size in Group, it means that there is only itself.
if (!size)
return 1;
return size;
}
} // namespace blink