Move IA2 State handling to AXPlatformNodeWin.

This CL moves all ia2_state handling from BrowserAccessibilityComWin to
AXPlatformNodeWin. In addition, we now forward get_states() to
AXPlatformNodeWin as well.

BUG=703369

Review-Url: https://codereview.chromium.org/2968833002
Cr-Commit-Position: refs/heads/master@{#485862}
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index a6736aa..68f9068 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -914,7 +914,7 @@
   return html_tag == "textarea";
 }
 
-// In general we should use IsEditField() instead if we want to check for
+// In general we should use ui::IsEditField() instead if we want to check for
 // something that has a caret and the user can edit.
 // TODO(aleventhal) this name is confusing because it returns true for combobox,
 // and we should take a look at why a combobox is considered a text control. The
@@ -936,11 +936,6 @@
   }
 }
 
-bool BrowserAccessibility::IsEditField() const {
-  return GetRole() == ui::AX_ROLE_TEXT_FIELD ||
-         GetRole() == ui::AX_ROLE_SEARCH_BOX;
-}
-
 // Indicates if this object is at the root of a rich edit text control.
 bool BrowserAccessibility::IsRichTextControl() const {
   return HasState(ui::AX_STATE_RICHLY_EDITABLE) &&
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 6bee579..7bad359c 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -720,12 +720,7 @@
     return E_FAIL;
   AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes);
 
-  if (!states)
-    return E_INVALIDARG;
-
-  *states = ia2_state();
-
-  return S_OK;
+  return AXPlatformNodeWin::get_states(states);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_uniqueID(LONG* unique_id) {
@@ -3739,6 +3734,8 @@
   // If this is a web area for a presentational iframe, give it a role of
   // something other than DOCUMENT so that the fact that it's a separate doc
   // is not exposed to AT.
+  // TODO(dougt): When we move IA2 Role handling to AXPlatformNodeWin, we can
+  // remove this ia2_role special case.
   if (owner()->IsWebAreaForPresentationalIframe()) {
     win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING;
   }
@@ -4920,46 +4917,6 @@
 
 void BrowserAccessibilityComWin::InitRoleAndState() {
   int32_t ia2_role = 0;
-  int32_t ia2_state = IA2_STATE_OPAQUE;
-
-  const auto checked_state = static_cast<ui::AXCheckedState>(
-      owner()->GetIntAttribute(ui::AX_ATTR_CHECKED_STATE));
-  if (checked_state) {
-    ia2_state |= IA2_STATE_CHECKABLE;
-  }
-
-  if (owner()->HasIntAttribute(ui::AX_ATTR_INVALID_STATE) &&
-      owner()->GetIntAttribute(ui::AX_ATTR_INVALID_STATE) !=
-          ui::AX_INVALID_STATE_FALSE)
-    ia2_state |= IA2_STATE_INVALID_ENTRY;
-  if (owner()->HasState(ui::AX_STATE_REQUIRED))
-    ia2_state |= IA2_STATE_REQUIRED;
-  if (owner()->HasState(ui::AX_STATE_VERTICAL))
-    ia2_state |= IA2_STATE_VERTICAL;
-  if (owner()->HasState(ui::AX_STATE_HORIZONTAL))
-    ia2_state |= IA2_STATE_HORIZONTAL;
-
-  const bool is_editable = owner()->HasState(ui::AX_STATE_EDITABLE);
-  if (is_editable)
-    ia2_state |= IA2_STATE_EDITABLE;
-
-  if (owner()->IsRichTextControl() || owner()->IsEditField()) {
-    // Support multi/single line states if root editable or appropriate role.
-    // We support the edit box roles even if the area is not actually editable,
-    // because it is technically feasible for JS to implement the edit box
-    // by controlling selection.
-    if (owner()->HasState(ui::AX_STATE_MULTILINE)) {
-      ia2_state |= IA2_STATE_MULTI_LINE;
-    } else {
-      ia2_state |= IA2_STATE_SINGLE_LINE;
-    }
-  }
-
-  if (!owner()->GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
-    ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
-
-  if (owner()->GetBoolAttribute(ui::AX_ATTR_MODAL))
-    ia2_state |= IA2_STATE_MODAL;
 
   base::string16 html_tag = owner()->GetString16Attribute(ui::AX_ATTR_HTML_TAG);
   switch (owner()->GetRole()) {
@@ -5038,12 +4995,6 @@
     case ui::AX_ROLE_MENU_ITEM_RADIO:
       ia2_role = IA2_ROLE_RADIO_MENU_ITEM;
       break;
-    case ui::AX_ROLE_MENU_LIST_POPUP:
-      ia2_state &= ~(IA2_STATE_EDITABLE);
-      break;
-    case ui::AX_ROLE_MENU_LIST_OPTION:
-      ia2_state &= ~(IA2_STATE_EDITABLE);
-      break;
     case ui::AX_ROLE_NAVIGATION:
       ia2_role = IA2_ROLE_SECTION;
       break;
@@ -5069,7 +5020,6 @@
       break;
     case ui::AX_ROLE_SCROLL_AREA:
       ia2_role = IA2_ROLE_SCROLL_PANE;
-      ia2_state &= ~(IA2_STATE_EDITABLE);
       break;
     case ui::AX_ROLE_SEARCH:
       ia2_role = IA2_ROLE_SECTION;
@@ -5083,10 +5033,6 @@
     case ui::AX_ROLE_TOGGLE_BUTTON:
       ia2_role = IA2_ROLE_TOGGLE_BUTTON;
       break;
-    case ui::AX_ROLE_TEXT_FIELD:
-    case ui::AX_ROLE_SEARCH_BOX:
-      ia2_state |= IA2_STATE_SELECTABLE_TEXT;
-      break;
     case ui::AX_ROLE_ABBR:
     case ui::AX_ROLE_TIME:
       ia2_role = IA2_ROLE_TEXT_FRAME;
@@ -5105,7 +5051,7 @@
     ia2_role = win_attributes_->ia_role;
 
   win_attributes_->ia2_role = ia2_role;
-  win_attributes_->ia2_state = ia2_state;
+  win_attributes_->ia2_state = IA2State();
 }
 
 BrowserAccessibilityComWin* ToBrowserAccessibilityComWin(
diff --git a/content/browser/accessibility/browser_accessibility_com_win.h b/content/browser/accessibility/browser_accessibility_com_win.h
index 9114591..511ba1f 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.h
+++ b/content/browser/accessibility/browser_accessibility_com_win.h
@@ -728,7 +728,7 @@
  private:
   // Private accessors.
   int32_t ia2_role() const { return win_attributes_->ia2_role; }
-  int32_t ia2_state() const { return win_attributes_->ia2_state; }
+
   const std::vector<base::string16>& ia2_attributes() const {
     return win_attributes_->ia2_attributes;
   }
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index bf66431..34355fe 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -133,4 +133,8 @@
   }
 }
 
+bool IsEditField(ui::AXRole role) {
+  return role == ui::AX_ROLE_TEXT_FIELD || role == ui::AX_ROLE_SEARCH_BOX;
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h
index 22c7c2b..ef6838f 100644
--- a/ui/accessibility/ax_role_properties.h
+++ b/ui/accessibility/ax_role_properties.h
@@ -32,6 +32,9 @@
 // Returns true if this node is a menu or related role.
 AX_EXPORT bool IsMenuRelated(ui::AXRole role);
 
+// Returns true if this node is a edit field.
+AX_EXPORT bool IsEditField(ui::AXRole role);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_ROLE_PROPERTIES_H_
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index cb51a4f..a5907b3b 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -20,6 +20,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/ax_text_utils.h"
 #include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
@@ -747,15 +748,7 @@
 
 STDMETHODIMP AXPlatformNodeWin::get_states(AccessibleStates* states) {
   COM_OBJECT_VALIDATE_1_ARG(states);
-  // There are only a couple of states we need to support
-  // in IAccessible2. If any more are added, we may want to
-  // add a helper function like MSAAState.
-  *states = IA2_STATE_OPAQUE;
-  if (GetData().state & (1 << ui::AX_STATE_EDITABLE))
-    *states |= IA2_STATE_EDITABLE;
-  if (GetData().state & (1 << ui::AX_STATE_VERTICAL))
-    *states |= IA2_STATE_VERTICAL;
-
+  *states = IA2State();
   return S_OK;
 }
 
@@ -1605,6 +1598,74 @@
   return parent->GetData().role == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
 }
 
+int32_t AXPlatformNodeWin::IA2State() {
+  const AXNodeData& data = GetData();
+
+  int32_t ia2_state = IA2_STATE_OPAQUE;
+
+  const auto checked_state = static_cast<ui::AXCheckedState>(
+      GetIntAttribute(ui::AX_ATTR_CHECKED_STATE));
+  if (checked_state) {
+    ia2_state |= IA2_STATE_CHECKABLE;
+  }
+
+  if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) &&
+      GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE)
+    ia2_state |= IA2_STATE_INVALID_ENTRY;
+  if (data.HasState(ui::AX_STATE_REQUIRED))
+    ia2_state |= IA2_STATE_REQUIRED;
+  if (data.HasState(ui::AX_STATE_VERTICAL))
+    ia2_state |= IA2_STATE_VERTICAL;
+  if (data.HasState(ui::AX_STATE_HORIZONTAL))
+    ia2_state |= IA2_STATE_HORIZONTAL;
+
+  const bool is_editable = data.HasState(ui::AX_STATE_EDITABLE);
+  if (is_editable)
+    ia2_state |= IA2_STATE_EDITABLE;
+
+  if (IsRichTextControl() || ui::IsEditField(data.role)) {
+    // Support multi/single line states if root editable or appropriate role.
+    // We support the edit box roles even if the area is not actually editable,
+    // because it is technically feasible for JS to implement the edit box
+    // by controlling selection.
+    if (data.HasState(ui::AX_STATE_MULTILINE)) {
+      ia2_state |= IA2_STATE_MULTI_LINE;
+    } else {
+      ia2_state |= IA2_STATE_SINGLE_LINE;
+    }
+  }
+
+  if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
+    ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
+
+  if (GetBoolAttribute(ui::AX_ATTR_MODAL))
+    ia2_state |= IA2_STATE_MODAL;
+
+  switch (data.role) {
+    case ui::AX_ROLE_MENU_LIST_POPUP:
+      ia2_state &= ~(IA2_STATE_EDITABLE);
+      break;
+    case ui::AX_ROLE_MENU_LIST_OPTION:
+      ia2_state &= ~(IA2_STATE_EDITABLE);
+      break;
+    case ui::AX_ROLE_SCROLL_AREA:
+      ia2_state &= ~(IA2_STATE_EDITABLE);
+      break;
+    case ui::AX_ROLE_TEXT_FIELD:
+    case ui::AX_ROLE_SEARCH_BOX:
+      if (data.HasState(ui::AX_STATE_MULTILINE)) {
+        ia2_state |= IA2_STATE_MULTI_LINE;
+      } else {
+        ia2_state |= IA2_STATE_SINGLE_LINE;
+      }
+      ia2_state |= IA2_STATE_SELECTABLE_TEXT;
+      break;
+    default:
+      break;
+  }
+  return ia2_state;
+}
+
 bool AXPlatformNodeWin::ShouldNodeHaveReadonlyState(
     const AXNodeData& data) const {
   if (data.GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 38ffb05..172bc33 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -274,6 +274,8 @@
   int MSAARole();
   std::string StringOverrideForMSAARole();
 
+  int32_t IA2State();
+
   // AXPlatformNodeBase overrides.
   void Dispose() override;