summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/accessibility/ax_radio_input.cc
blob: b4f99932a3033cab76915d06ae3d2595b2907a29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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 "third_party/blink/renderer/modules/accessibility/ax_radio_input.h"

#include "third_party/blink/renderer/core/aom/accessible_node.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/radio_input_type.h"
#include "third_party/blink/renderer/core/input_type_names.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"

namespace blink {

AXRadioInput::AXRadioInput(LayoutObject* layout_object,
                           AXObjectCacheImpl& ax_object_cache)
    : AXLayoutObject(layout_object, ax_object_cache) {
  // 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);
}

void AXRadioInput::UpdatePosAndSetSize(int position) {
  if (position)
    pos_in_set_ = position;
  set_size_ = SizeOfRadioGroup();
}

void AXRadioInput::RequestUpdateToNextNode(bool forward) {
  HTMLInputElement* next_element =
      RadioInputType::NextRadioButtonInGroup(GetInputElement(), forward);
  AXObject* next_axobject = AXObjectCache().Get(next_element);
  if (!next_axobject || !next_axobject->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(next_axobject)->UpdatePosAndSetSize(position);
  AXObjectCache().PostNotification(next_axobject,
                                   ax::mojom::Event::kAriaAttributeChanged);
  ToAXRadioInput(next_axobject)->RequestUpdateToNextNode(forward);
}

HTMLInputElement* AXRadioInput::FindFirstRadioButtonInGroup(
    HTMLInputElement* current) const {
  while (HTMLInputElement* prev_element =
             RadioInputType::NextRadioButtonInGroup(current, false))
    current = prev_element;
  return current;
}

int AXRadioInput::PosInSet() const {
  uint32_t pos_in_set;
  if (HasAOMPropertyOrARIAAttribute(AOMUIntProperty::kPosInSet, pos_in_set))
    return pos_in_set;
  return pos_in_set_;
}

int AXRadioInput::SetSize() const {
  int32_t set_size;
  if (HasAOMPropertyOrARIAAttribute(AOMIntProperty::kSetSize, set_size))
    return set_size;
  return set_size_;
}

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 need_to_update_prev = false;
  int position = 1;
  HTMLInputElement* prev_element =
      RadioInputType::NextRadioButtonInGroup(GetInputElement(), false);
  if (prev_element) {
    AXObject* object = AXObjectCache().Get(prev_element);
    // 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())
        need_to_update_prev = true;
    }
  }
  UpdatePosAndSetSize(position);

  // If it is not the last element, request update to the next node.
  if (position != SetSize())
    RequestUpdateToNextNode(true);
  return need_to_update_prev;
}

int AXRadioInput::CountFromFirstElement() const {
  int count = 1;
  HTMLInputElement* current = GetInputElement();
  while (HTMLInputElement* prev_element =
             RadioInputType::NextRadioButtonInGroup(current, false)) {
    current = prev_element;
    count++;
  }
  return count;
}

HTMLInputElement* AXRadioInput::GetInputElement() const {
  return To<HTMLInputElement>(layout_object_->GetNode());
}

int AXRadioInput::SizeOfRadioGroup() const {
  int size = GetInputElement()->SizeOfRadioGroup();
  // If it has no size in Group, it means that there is only itself.
  if (!size)
    return 1;
  return size;
}

}  // namespace blink