[go: nahoru, domu]

Add kComboBoxSelect internal role

Blink exposes two completely things as kPopupButton, and we need to
disambiguate.
1. <select size=1>
2. <button aria-haspopup> via AXObject::ButtonRoleType()
Currently this disambiguation (in order to expose <select> as combobox
and <button> as button role) occurs on the platform side by checking
the tag or testing the internal children structure, which is a hack.

One negative side-effect of this is that the reverse role mappings in
Blink end up reporting buttons with aria-haspopup as 'combobox' in
devtools which is very confusing to developers ensuring they have the
right roles present in their accessibility trees.

This change splits out kPopUpButton, and allows us to more closely
match the role mapping through the entire pipeline.

There are a handful of places where the new kComboBoxSelect and
kPopUpButton are not treated as equivalent in code:
- checks for popupbutton then menulistpopup and related children.
These were just converted to check for the new kComboBoxSelect role.
Generic popup buttons can't ever have this structure anyways.
- A couple of locations in Blink that used to check for kPopUpButton
then test for HTMLSelect element specifically.
- Readonly support for kPopUpButton, since that was the cause of some
more special case code.
However, in these cases, there should not be any behavioral change.

In addition I had to add some output mappings on ChromeVox, since
it is directly coupled with the Chromium internal roles. I opted to
have these remain the same for now and we can adjust in a follow up
CL as appropriate.

Bug: 1147422, 1362834

Change-Id: I0f9ca8cb9e7ad2a7aea7ebfeda4931593ca11721
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3868204
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Reviewed-by: David Tseng <dtseng@chromium.org>
Reviewed-by: Joe Mason <joenotcharles@google.com>
Commit-Queue: Daniel Libby <dlibby@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1047719}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 6b302a4..4e4e6b98 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -791,7 +791,7 @@
     </select>
   `;
   const root = await this.runWithLoadedTree(site);
-  const select = root.find({role: RoleType.POP_UP_BUTTON});
+  const select = root.find({role: RoleType.COMBO_BOX_SELECT});
   const selectLastOption = () => {
     const options = select.findAll({role: RoleType.LIST_BOX_OPTION});
     options[options.length - 1].doDefault();
@@ -1655,7 +1655,7 @@
     </select>
   `;
       const root = await this.runWithLoadedTree(site);
-      const select = root.find({role: RoleType.POP_UP_BUTTON});
+      const select = root.find({role: RoleType.COMBO_BOX_SELECT});
       mockFeedback.expectSpeech('Button', 'has pop up', 'Collapsed')
           .call(doDefault(select))
           .expectSpeech('Expanded')
@@ -2899,7 +2899,7 @@
       await mockFeedback.replay();
     });
 
-AX_TEST_F('ChromeVoxBackgroundTest', 'PopupButtonCollapsed', async function() {
+AX_TEST_F('ChromeVoxBackgroundTest', 'SelectCollapsed', async function() {
   const mockFeedback = this.createMockFeedback();
   const site = `
     <select id="button">
@@ -3263,7 +3263,7 @@
   `;
       const root = await this.runWithLoadedTree(site);
       const application = root.find({role: RoleType.APPLICATION});
-      const popUpButton = root.find({role: RoleType.POP_UP_BUTTON});
+      const popUpButton = root.find({role: RoleType.COMBO_BOX_SELECT});
       const p = root.find({role: RoleType.PARAGRAPH});
 
       // Move focus to the select, which honors value changes through
@@ -3275,7 +3275,7 @@
       p.doDefault();
       await TestUtils.waitForSpeech('grape');
       assertEquals(
-          RoleType.POP_UP_BUTTON,
+          RoleType.COMBO_BOX_SELECT,
           ChromeVoxState.instance.currentRange.start.node.role);
 
       // Now, move focus to the application which is a parent of the select.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
index a8dcf4b..fcd96b7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview Handles automation events on the currently focused node.
  */
+import {AutomationPredicate} from '../../common/automation_predicate.js';
 import {constants} from '../../common/constants.js';
 import {CursorRange} from '../../common/cursors/range.js';
 import {ChromeVoxEvent} from '../common/custom_automation_event.js';
@@ -76,7 +77,7 @@
 
     let skipFocusCheck = false;
     chrome.automation.getFocus(focus => {
-      if (focus.role === RoleType.POP_UP_BUTTON) {
+      if (AutomationPredicate.popUpButton(focus)) {
         skipFocusCheck = true;
       }
     });
@@ -139,7 +140,7 @@
    * @param {!ChromeVoxEvent} evt
    */
   onSelectedValueChanged_(evt) {
-    if (evt.target.role !== RoleType.POP_UP_BUTTON ||
+    if (!AutomationPredicate.popUpButton(evt.target) ||
         evt.target.state.editable) {
       return;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
index 91569a4..259cb07 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
@@ -97,7 +97,7 @@
     let node = range.start.node;
     while (node) {
       if (AutomationPredicate.menuItem(node) ||
-          (node.role === RoleType.POP_UP_BUTTON && node.state.expanded)) {
+          (AutomationPredicate.popUpButton(node) && node.state.expanded)) {
         inMenu = true;
         break;
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
index 4c150a4..6a027b6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
@@ -41,6 +41,11 @@
   comboBoxMenuButton: {msgId: 'role_combobox', earconId: 'LISTBOX'},
   comboBoxGrouping:
       {msgId: 'role_combobox', inherits: 'abstractFormFieldContainer'},
+  comboBoxSelect: {
+    msgId: 'role_button',
+    earconId: 'POP_UP_BUTTON',
+    inherits: 'comboBoxMenuButton',
+  },
   complementary: {msgId: 'role_complementary', inherits: 'abstractContainer'},
   comment: {
     msgId: 'role_comment',
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index 618ecbf..df3e1cc 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -1002,3 +1002,15 @@
   Role.LINE_BREAK,
   Role.LIST_MARKER,
 ]);
+
+/**
+ * Matches against pop-up button like nodes.
+ * Historically, single value <select> controls were represented as a
+ * popup button, but they are distinct from <button aria-haspopup='menu'>.
+ * @param {!AutomationNode} node
+ * @return {boolean}
+ */
+AutomationPredicate.popUpButton = AutomationPredicate.roles([
+  Role.COMBO_BOX_SELECT,
+  Role.POP_UP_BUTTON,
+]);
diff --git a/components/services/screen_ai/proto/proto_convertor.cc b/components/services/screen_ai/proto/proto_convertor.cc
index b918d151..508a2b1 100644
--- a/components/services/screen_ai/proto/proto_convertor.cc
+++ b/components/services/screen_ai/proto/proto_convertor.cc
@@ -459,6 +459,7 @@
       roles_with_different_name = {
           // Aria Roles
           {ax::mojom::Role::kComboBoxGrouping, "combobox"},
+          {ax::mojom::Role::kComboBoxSelect, "combobox"},
           {ax::mojom::Role::kContentDeletion, "deletion"},
           {ax::mojom::Role::kDocAbstract, "doc-abstract"},
           {ax::mojom::Role::kDocAcknowledgments, "doc-acknowledgments"},
diff --git a/content/browser/accessibility/accessibility_action_browsertest.cc b/content/browser/accessibility/accessibility_action_browsertest.cc
index 5128047..c76de76a 100644
--- a/content/browser/accessibility/accessibility_action_browsertest.cc
+++ b/content/browser/accessibility/accessibility_action_browsertest.cc
@@ -1224,7 +1224,8 @@
       </body>
       )HTML");
 
-  BrowserAccessibility* target = FindNode(ax::mojom::Role::kPopUpButton, "One");
+  BrowserAccessibility* target =
+      FindNode(ax::mojom::Role::kComboBoxSelect, "One");
   ASSERT_NE(nullptr, target);
 
   EXPECT_EQ(0U, target->PlatformChildCount());
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index dd080c8..a50ec15 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -2000,6 +2000,11 @@
       return content_client->GetLocalizedString(IDS_AX_ROLE_COLOR_WELL);
     case ax::mojom::Role::kColumnHeader:
       return content_client->GetLocalizedString(IDS_AX_ROLE_COLUMN_HEADER);
+    case ax::mojom::Role::kComboBoxSelect:
+      // TODO(crbug.com/1362834): This is used for Mac AXRoleDescription. This
+      // should be changed at the same time we map this role to
+      // NSAccessibilityComboBoxRole.
+      return content_client->GetLocalizedString(IDS_AX_ROLE_POP_UP_BUTTON);
     case ax::mojom::Role::kComment:
       return content_client->GetLocalizedString(IDS_AX_ROLE_COMMENT);
     case ax::mojom::Role::kComplementary:
@@ -2252,13 +2257,9 @@
   return node()->IsInListMarker();
 }
 
-bool BrowserAccessibility::IsCollapsedMenuListPopUpButton() const {
-  return node()->IsCollapsedMenuListPopUpButton();
-}
-
-BrowserAccessibility*
-BrowserAccessibility::GetCollapsedMenuListPopUpButtonAncestor() const {
-  ui::AXNode* popup_button = node()->GetCollapsedMenuListPopUpButtonAncestor();
+BrowserAccessibility* BrowserAccessibility::GetCollapsedMenuListSelectAncestor()
+    const {
+  ui::AXNode* popup_button = node()->GetCollapsedMenuListSelectAncestor();
   return manager()->GetFromAXNode(popup_button);
 }
 
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 34d977f9..795488d 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -578,14 +578,10 @@
   // of a list marker node. Returns false otherwise.
   bool IsInListMarker() const;
 
-  // Returns true if this node is a collapsed popup button that is parent to a
-  // menu list popup.
-  bool IsCollapsedMenuListPopUpButton() const;
-
   // Returns the popup button ancestor of this current node if any. The popup
   // button needs to be the parent of a menu list popup and needs to be
   // collapsed.
-  BrowserAccessibility* GetCollapsedMenuListPopUpButtonAncestor() const;
+  BrowserAccessibility* GetCollapsedMenuListSelectAncestor() const;
 
   // Returns true if:
   // 1. This node is a list, AND
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index c6dd2d7..7e4b76c 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -832,8 +832,12 @@
 
   // For multiselectable state, generate a state description. We do not set a
   // state description for pop up/<select> to prevent double utterances.
-  if (IsMultiselectable() && GetRole() != ax::mojom::Role::kPopUpButton)
+  // TODO(crbug.com/1362834): Consider whether other combobox roles should be
+  // accounted for.
+  if (IsMultiselectable() && GetRole() != ax::mojom::Role::kPopUpButton &&
+      GetRole() != ax::mojom::Role::kComboBoxSelect) {
     state_descs.push_back(GetMultiselectableStateDescription());
+  }
 
   // For Checkboxes, if we are in a kMixed state, we will communicate
   // "partially checked" through the state description. This is mutually
@@ -1137,8 +1141,10 @@
     }
   }
 
-  // For pop up buttons, we want to return property value specific roles.
-  if (GetRole() == ax::mojom::Role::kPopUpButton) {
+  // For pop up buttons, we want to return more specific roles based on various
+  // values of the kHasPopup attribute.
+  if (GetRole() == ax::mojom::Role::kPopUpButton ||
+      GetRole() == ax::mojom::Role::kComboBoxSelect) {
     switch (static_cast<ax::mojom::HasPopup>(
         GetIntAttribute(ax::mojom::IntAttribute::kHasPopup))) {
       case ax::mojom::HasPopup::kTrue:
diff --git a/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc b/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
index 465d8ba..c02a9a49 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
@@ -616,7 +616,7 @@
 
   ui::AXNodeData combo_box;
   combo_box.id = 6;
-  combo_box.role = ax::mojom::Role::kPopUpButton;
+  combo_box.role = ax::mojom::Role::kComboBoxSelect;
   combo_box.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "select");
   combo_box.AddState(ax::mojom::State::kCollapsed);
   combo_box.SetValue("1");
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 4efb52f..46300d3 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1708,7 +1708,7 @@
   // list popup is not part of the tree when its parent is collapsed but events
   // should be fired anyway.
   if (owner()->IsChildOfLeaf() &&
-      !owner()->GetCollapsedMenuListPopUpButtonAncestor()) {
+      !owner()->GetCollapsedMenuListSelectAncestor()) {
     return;
   }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 9ee5253..1f24e10 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -807,7 +807,9 @@
     active_descendant = node->manager()->GetFromID(active_descendant_id);
   }
 
-  if (node->GetRole() == ax::mojom::Role::kPopUpButton) {
+  // TODO(crbug.com/1363353): This code should be removed in favor of the right
+  // computation of the active descendant in Blink.
+  if (node->GetRole() == ax::mojom::Role::kComboBoxSelect) {
     BrowserAccessibility* child = node->InternalGetFirstChild();
     if (child && child->GetRole() == ax::mojom::Role::kMenuListPopup &&
         !child->IsInvisibleOrIgnored()) {
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index aec13617..a1f86c36 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -48,7 +48,7 @@
 bool BrowserAccessibilityWin::CanFireEvents() const {
   // On Windows, we want to hide the subtree of a collapsed <select> element but
   // we still need to fire events on those hidden nodes.
-  if (!IsIgnored() && GetCollapsedMenuListPopUpButtonAncestor())
+  if (!IsIgnored() && GetCollapsedMenuListSelectAncestor())
     return true;
 
   // If the node changed its ignored state this frame then some events should be
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index 0aadd73..730520d 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -830,7 +830,7 @@
   ASSERT_NE(body, nullptr);
   BrowserAccessibility* select = body->PlatformGetChild(0);
   ASSERT_NE(select, nullptr);
-  EXPECT_EQ(ax::mojom::Role::kPopUpButton, select->GetRole());
+  EXPECT_EQ(ax::mojom::Role::kComboBoxSelect, select->GetRole());
 
   {
     // Get popup via InternalGetChild so that hidden nodes are included.
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
index acb1a25..88c9272 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search.cc
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
@@ -267,7 +267,7 @@
   return (node->GetRole() == ax::mojom::Role::kComboBoxGrouping ||
           node->GetRole() == ax::mojom::Role::kComboBoxMenuButton ||
           node->GetRole() == ax::mojom::Role::kTextFieldWithComboBox ||
-          node->GetRole() == ax::mojom::Role::kPopUpButton);
+          node->GetRole() == ax::mojom::Role::kComboBoxSelect);
 }
 
 bool AccessibilityControlPredicate(BrowserAccessibility* start,
diff --git a/content/test/data/accessibility/html/action-verbs-expected-android-external.txt b/content/test/data/accessibility/html/action-verbs-expected-android-external.txt
index 3f4623f..9f07ff1 100644
--- a/content/test/data/accessibility/html/action-verbs-expected-android-external.txt
+++ b/content/test/data/accessibility/html/action-verbs-expected-android-external.txt
@@ -14,7 +14,7 @@
 ++ToggleButton text:"ARIA Switch" stateDescription:"Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", clickableScore="300", roleDescription="switch"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="details"]
 ++++View text:"Summary" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="disclosureTriangle", clickableScore="300", roleDescription="disclosure triangle"]
-++View text:"Pop-up button" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
+++View text:"Pop-up button" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
 ++TextView text:"Div with click handler" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", clickableScore="200"]
 ++View clickable actions:[CLICK, AX_FOCUS] bundle:[chromeRole="group", clickableScore="200"]
 ++++TextView text:"Paragraph with click handler on parent" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", clickableScore="100"]
diff --git a/content/test/data/accessibility/html/action-verbs-expected-blink.txt b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
index 32950c2..d4ed8f6 100644
--- a/content/test/data/accessibility/html/action-verbs-expected-blink.txt
+++ b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
@@ -35,7 +35,7 @@
 ++++++++++++++inlineTextBox name='Summary'
 ++++++++genericContainer ignored
 ++++++++++staticText ignored invisible name='Details'
-++++++popUpButton collapsed value='Pop-up button' defaultActionVerb=open haspopup=menu
+++++++comboBoxSelect collapsed value='Pop-up button' defaultActionVerb=open haspopup=menu
 ++++++++menuListPopup invisible
 ++++++++++menuListOption name='Pop-up button' defaultActionVerb=select selected=true
 ++++++genericContainer defaultActionVerb=click
@@ -74,4 +74,4 @@
 ++++++++++++inlineTextBox name='ARIA switch in clickable container'
 ++++++++genericContainer defaultActionVerb=clickAncestor
 ++++++++++staticText name='Generic in clickable container'
-++++++++++++inlineTextBox name='Generic in clickable container'
\ No newline at end of file
+++++++++++++inlineTextBox name='Generic in clickable container'
diff --git a/content/test/data/accessibility/html/combobox-item-visibility-expected-blink.txt b/content/test/data/accessibility/html/combobox-item-visibility-expected-blink.txt
index 17d24b1..7160539 100644
--- a/content/test/data/accessibility/html/combobox-item-visibility-expected-blink.txt
+++ b/content/test/data/accessibility/html/combobox-item-visibility-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer
-++++++popUpButton collapsed setSize=1
+++++++comboBoxSelect collapsed setSize=1
 ++++++++menuListPopup invisible setSize=1
 ++++++++++menuListOption setSize=1 posInSet=1 selected=true
 ++++++++++menuListOption ignored invisible selected=false
diff --git a/content/test/data/accessibility/html/combobox-optgroup-expected-android-external.txt b/content/test/data/accessibility/html/combobox-optgroup-expected-android-external.txt
index ed230ff1..c538ab3 100644
--- a/content/test/data/accessibility/html/combobox-optgroup-expected-android-external.txt
+++ b/content/test/data/accessibility/html/combobox-optgroup-expected-android-external.txt
@@ -1,3 +1,3 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View text:"Mercedes Label" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
+++++View text:"Mercedes Label" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/combobox-optgroup-expected-blink.txt b/content/test/data/accessibility/html/combobox-optgroup-expected-blink.txt
index 71ca16ab..c0ffa9e 100644
--- a/content/test/data/accessibility/html/combobox-optgroup-expected-blink.txt
+++ b/content/test/data/accessibility/html/combobox-optgroup-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer
-++++++popUpButton collapsed value='Mercedes Label' setSize=4
+++++++comboBoxSelect collapsed value='Mercedes Label' setSize=4
 ++++++++menuListPopup invisible setSize=4
 ++++++++++menuListOption invisible name='Volvo Label' setSize=4 posInSet=1 selected=false
 ++++++++++menuListOption invisible name='Saab Label' setSize=4 posInSet=2 selected=false
diff --git a/content/test/data/accessibility/html/contenteditable-on-disallowed-element-expected-blink.txt b/content/test/data/accessibility/html/contenteditable-on-disallowed-element-expected-blink.txt
index 8f47a4e..5284560 100644
--- a/content/test/data/accessibility/html/contenteditable-on-disallowed-element-expected-blink.txt
+++ b/content/test/data/accessibility/html/contenteditable-on-disallowed-element-expected-blink.txt
@@ -9,7 +9,7 @@
 ++++++++genericContainer editable
 ++++++++++staticText editable
 ++++++++++++inlineTextBox editable
-++++++popUpButton collapsed editable focusable multiline nonAtomicTextFieldRoot=true
+++++++comboBoxSelect collapsed editable focusable multiline nonAtomicTextFieldRoot=true
 ++++++++menuListPopup invisible
 ++++++++++menuListOption editable focusable multiline nonAtomicTextFieldRoot=true selected=true
 ++++++lineBreak editable focusable multiline nonAtomicTextFieldRoot=true
@@ -87,4 +87,4 @@
 ++++++++++++++genericContainer ignored invisible
 ++++++++++++++++genericContainer ignored invisible
 ++++++++++++++++++staticText ignored invisible
-++++++progressIndicator editable focusable multiline nonAtomicTextFieldRoot=true minValueForRange=0.00 maxValueForRange=1.00
\ No newline at end of file
+++++++progressIndicator editable focusable multiline nonAtomicTextFieldRoot=true minValueForRange=0.00 maxValueForRange=1.00
diff --git a/content/test/data/accessibility/html/label-with-selected-option-expected-blink.txt b/content/test/data/accessibility/html/label-with-selected-option-expected-blink.txt
index 6a12aad..79d8213 100644
--- a/content/test/data/accessibility/html/label-with-selected-option-expected-blink.txt
+++ b/content/test/data/accessibility/html/label-with-selected-option-expected-blink.txt
@@ -6,7 +6,7 @@
 ++++++++labelText
 ++++++++++staticText name='Test 1: Flash the screen '
 ++++++++++++inlineTextBox name='Test 1: Flash the screen '
-++++++++++popUpButton collapsed value='2'
+++++++++++comboBoxSelect collapsed value='2'
 ++++++++++++menuListPopup invisible
 ++++++++++++++menuListOption invisible name='1' selected=false
 ++++++++++++++menuListOption name='2' selected=true
@@ -30,7 +30,7 @@
 ++++++++labelText
 ++++++++++staticText name='Test 3: Flash the screen '
 ++++++++++++inlineTextBox name='Test 3: Flash the screen '
-++++++++++popUpButton collapsed value='two'
+++++++++++comboBoxSelect collapsed value='two'
 ++++++++++++menuListPopup invisible
 ++++++++++++++menuListOption invisible name='1' selected=false
 ++++++++++++++menuListOption name='two' selected=true
diff --git a/content/test/data/accessibility/html/modal-dialog-closed-expected-blink.txt b/content/test/data/accessibility/html/modal-dialog-closed-expected-blink.txt
index 8b65e57..1b96974 100644
--- a/content/test/data/accessibility/html/modal-dialog-closed-expected-blink.txt
+++ b/content/test/data/accessibility/html/modal-dialog-closed-expected-blink.txt
@@ -6,9 +6,9 @@
 ++++++section
 ++++++++dialog ignored invisible
 ++++++++++staticText ignored name='This dialog is closed and should not be in the tree'
-++++++++popUpButton collapsed value='This should be in the tree.' haspopup=menu
+++++++++comboBoxSelect collapsed value='This should be in the tree.' haspopup=menu
 ++++++++++menuListPopup invisible
 ++++++++++++menuListOption name='This should be in the tree.' selected=true
 ++++++colorWell value='#000000'
 ++++++++genericContainer ignored
-++++++++++genericContainer ignored
\ No newline at end of file
+++++++++++genericContainer ignored
diff --git a/content/test/data/accessibility/html/offscreen-select-expected-android-external.txt b/content/test/data/accessibility/html/offscreen-select-expected-android-external.txt
index 6e20b94..3ba9e43 100644
--- a/content/test/data/accessibility/html/offscreen-select-expected-android-external.txt
+++ b/content/test/data/accessibility/html/offscreen-select-expected-android-external.txt
@@ -1,3 +1,3 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++View text:"Onscreen 1" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
-++View text:"Offscreen 1" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
+++View text:"Onscreen 1" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
+++View text:"Offscreen 1" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/offscreen-select-expected-blink.txt b/content/test/data/accessibility/html/offscreen-select-expected-blink.txt
index 9c8b9b5..d96233e3 100644
--- a/content/test/data/accessibility/html/offscreen-select-expected-blink.txt
+++ b/content/test/data/accessibility/html/offscreen-select-expected-blink.txt
@@ -1,13 +1,13 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++popUpButton collapsed value='Onscreen 1'
+++++++comboBoxSelect collapsed value='Onscreen 1'
 ++++++++menuListPopup invisible
 ++++++++++menuListOption name='Onscreen 1' selected=true
 ++++++++++menuListOption invisible name='Onscreen 2' selected=false
 ++++++++++menuListOption invisible name='Onscreen 3' selected=false
 ++++++genericContainer ignored
-++++++popUpButton collapsed offscreen value='Offscreen 1'
+++++++comboBoxSelect collapsed offscreen value='Offscreen 1'
 ++++++++menuListPopup invisible
 ++++++++++menuListOption offscreen name='Offscreen 1' selected=true
 ++++++++++menuListOption invisible offscreen name='Offscreen 2' selected=false
diff --git a/content/test/data/accessibility/html/optgroup-expected-android-external.txt b/content/test/data/accessibility/html/optgroup-expected-android-external.txt
index f3bdfcff..2e87234 100644
--- a/content/test/data/accessibility/html/optgroup-expected-android-external.txt
+++ b/content/test/data/accessibility/html/optgroup-expected-android-external.txt
@@ -1,6 +1,6 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View viewIdResName:"listbox" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
+++++View viewIdResName:"listbox" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
 ++++++View notVisibleToUser actions:[AX_FOCUS] bundle:[chromeRole="menuListPopup"]
 ++++++++View text:"One" viewIdResName:"listbox_option_enabled_one" clickable focusable notVisibleToUser actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuListOption", clickableScore="300"]
 ++++++++View text:"Two" clickable focusable notVisibleToUser actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuListOption", clickableScore="300"]
diff --git a/content/test/data/accessibility/html/select-expected-android-external.txt b/content/test/data/accessibility/html/select-expected-android-external.txt
index 7fcd8392..7a58842 100644
--- a/content/test/data/accessibility/html/select-expected-android-external.txt
+++ b/content/test/data/accessibility/html/select-expected-android-external.txt
@@ -1,10 +1,10 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View text:"Placeholder option" viewIdResName:"A" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
-++++View text:"Option 2" viewIdResName:"B" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
-++++View text:"Option 1" viewIdResName:"C" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
-++++View text:"0 selected" viewIdResName:"D" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
-++++View viewIdResName:"E" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
+++++View text:"Placeholder option" viewIdResName:"A" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
+++++View text:"Option 2" viewIdResName:"B" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
+++++View text:"Option 1" viewIdResName:"C" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
+++++View text:"0 selected" viewIdResName:"D" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
+++++View viewIdResName:"E" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
 ++++++View notVisibleToUser actions:[AX_FOCUS] bundle:[chromeRole="menuListPopup"]
 ++++++++View text:"Option 1" clickable focusable notVisibleToUser actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuListOption", clickableScore="300"]
 ++++++++View text:"Option 2" clickable focusable notVisibleToUser actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuListOption", clickableScore="300"]
diff --git a/content/test/data/accessibility/html/select-expected-blink.txt b/content/test/data/accessibility/html/select-expected-blink.txt
index fab7032..05e3395 100644
--- a/content/test/data/accessibility/html/select-expected-blink.txt
+++ b/content/test/data/accessibility/html/select-expected-blink.txt
@@ -1,17 +1,17 @@
 rootWebArea focusable
 ++genericContainer ignored
 ++++genericContainer
-++++++popUpButton collapsed focusable value='Placeholder option' setSize=3 haspopup=menu
+++++++comboBoxSelect collapsed focusable value='Placeholder option' setSize=3 haspopup=menu
 ++++++++menuListPopup invisible setSize=3
 ++++++++++menuListOption focusable name='Placeholder option' setSize=3 posInSet=1 selected=true
 ++++++++++menuListOption focusable invisible name='Option 1' setSize=3 posInSet=2 selected=false
 ++++++++++menuListOption focusable invisible name='Option 2' setSize=3 posInSet=3 selected=false
-++++++popUpButton collapsed focusable value='Option 2' setSize=3 haspopup=menu
+++++++comboBoxSelect collapsed focusable value='Option 2' setSize=3 haspopup=menu
 ++++++++menuListPopup invisible setSize=3
 ++++++++++menuListOption focusable invisible name='Option 1' setSize=3 posInSet=1 selected=false
 ++++++++++menuListOption focusable name='Option 2' setSize=3 posInSet=2 selected=true
 ++++++++++menuListOption focusable invisible name='Option 3' setSize=3 posInSet=3 selected=false
-++++++popUpButton collapsed focusable required value='Option 1' setSize=3 haspopup=menu
+++++++comboBoxSelect collapsed focusable required value='Option 1' setSize=3 haspopup=menu
 ++++++++menuListPopup invisible setSize=3
 ++++++++++menuListOption focusable name='Option 1' setSize=3 posInSet=1 selected=true
 ++++++++++menuListOption focusable invisible name='Option 2' setSize=3 posInSet=2 selected=false
@@ -25,4 +25,4 @@
 ++++++++genericContainer ignored
 ++++++++++listBoxOption focusable name='Option 1' setSize=3 posInSet=1 selected=false
 ++++++++++listBoxOption focusable name='Option 2' setSize=3 posInSet=2 selected=false
-++++++++++listBoxOption focusable name='Option 3' setSize=3 posInSet=3 selected=false
\ No newline at end of file
+++++++++++listBoxOption focusable name='Option 3' setSize=3 posInSet=3 selected=false
diff --git a/content/test/data/accessibility/html/select-in-canvas-expected-blink.txt b/content/test/data/accessibility/html/select-in-canvas-expected-blink.txt
index e12576e..d4819c10 100644
--- a/content/test/data/accessibility/html/select-in-canvas-expected-blink.txt
+++ b/content/test/data/accessibility/html/select-in-canvas-expected-blink.txt
@@ -2,13 +2,13 @@
 ++genericContainer ignored
 ++++genericContainer ignored
 ++++++genericContainer
-++++++++popUpButton collapsed focusable value='1'
+++++++++comboBoxSelect collapsed focusable value='1'
 ++++++++++menuListPopup invisible
 ++++++++++++menuListOption focusable name='1' selected=true
 ++++++++++++menuListOption focusable invisible name='2' selected=false
 ++++++canvas
 ++++++++staticText name='<newline>  '
-++++++++popUpButton collapsed focusable value='1'
+++++++++comboBoxSelect collapsed focusable value='1'
 ++++++++++genericContainer ignored invisible
 ++++++++++++staticText ignored invisible name='1'
 ++++++++++genericContainer ignored
diff --git a/content/test/data/accessibility/html/selection-container-expected-android-external.txt b/content/test/data/accessibility/html/selection-container-expected-android-external.txt
index 4f92284..8fa5ef7e 100644
--- a/content/test/data/accessibility/html/selection-container-expected-android-external.txt
+++ b/content/test/data/accessibility/html/selection-container-expected-android-external.txt
@@ -1,3 +1,3 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View text:"selection_list" viewIdResName:"listbox" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
+++++View text:"selection_list" viewIdResName:"listbox" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxSelect", clickableScore="300", roleDescription="menu pop up button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/regression/display-contents-select-crash-expected-blink.txt b/content/test/data/accessibility/regression/display-contents-select-crash-expected-blink.txt
index 52b0ab7..47a5c33 100644
--- a/content/test/data/accessibility/regression/display-contents-select-crash-expected-blink.txt
+++ b/content/test/data/accessibility/regression/display-contents-select-crash-expected-blink.txt
@@ -1,6 +1,6 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer
-++++++popUpButton collapsed
+++++++comboBoxSelect collapsed
 ++++++++menuListPopup invisible
 ++++++++++menuListOption selected=true
diff --git a/content/test/data/accessibility/regression/option-accessible-name-is-select-expected-blink.txt b/content/test/data/accessibility/regression/option-accessible-name-is-select-expected-blink.txt
index f83eda7..b0c1ea87 100644
--- a/content/test/data/accessibility/regression/option-accessible-name-is-select-expected-blink.txt
+++ b/content/test/data/accessibility/regression/option-accessible-name-is-select-expected-blink.txt
@@ -4,7 +4,7 @@
 ++++++labelText
 ++++++++staticText name='How often?'
 ++++++++++inlineTextBox name='How often?'
-++++++popUpButton collapsed required name='How often?' invalidState=true
+++++++comboBoxSelect collapsed required name='How often?' invalidState=true
 ++++++++menuListPopup invisible
 ++++++++++menuListOption selected=true
 ++++++++++menuListOption invisible selected=false
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 34143ed6..72e06f9 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -155,6 +155,7 @@
     columnHeader,
     comboBoxGrouping,
     comboBoxMenuButton,
+    comboBoxSelect,
     comment,
     complementary,
     contentDeletion,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
index f90f2902..5beab07a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
@@ -40,7 +40,7 @@
 }
 
 ax::mojom::blink::Role AXMenuList::NativeRoleIgnoringAria() const {
-  return ax::mojom::blink::Role::kPopUpButton;
+  return ax::mojom::blink::Role::kComboBoxSelect;
 }
 
 bool AXMenuList::OnNativeClickAction() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 0d76ca8..3d5631b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1067,7 +1067,7 @@
     if (select_element->IsMultiple())
       return ax::mojom::blink::Role::kListBox;
     else
-      return ax::mojom::blink::Role::kPopUpButton;
+      return ax::mojom::blink::Role::kComboBoxSelect;
   }
 
   if (auto* option = DynamicTo<HTMLOptionElement>(*GetNode())) {
@@ -1853,7 +1853,7 @@
     }
   }
 
-  if (RoleValue() == ax::mojom::blink::Role::kPopUpButton &&
+  if (RoleValue() == ax::mojom::blink::Role::kComboBoxSelect &&
       IsA<HTMLSelectElement>(*element)) {
     return To<HTMLSelectElement>(element)->PopupIsVisible()
                ? kExpandedExpanded
@@ -2704,7 +2704,7 @@
 }
 
 int AXNodeObject::PosInSet() const {
-  if (RoleValue() == ax::mojom::blink::Role::kPopUpButton && GetNode() &&
+  if (RoleValue() == ax::mojom::blink::Role::kComboBoxSelect && GetNode() &&
       !AXObjectCache().UseAXMenuList()) {
     if (auto* select_element = DynamicTo<HTMLSelectElement>(*GetNode()))
       return 1 + select_element->selectedIndex();
@@ -2719,7 +2719,7 @@
 }
 
 int AXNodeObject::SetSize() const {
-  if (RoleValue() == ax::mojom::blink::Role::kPopUpButton && GetNode() &&
+  if (RoleValue() == ax::mojom::blink::Role::kComboBoxSelect && GetNode() &&
       !AXObjectCache().UseAXMenuList()) {
     if (auto* select_element = DynamicTo<HTMLSelectElement>(*GetNode()))
       return static_cast<int>(select_element->length());
@@ -4426,6 +4426,7 @@
     case ax::mojom::blink::Role::kSwitch:
     case ax::mojom::blink::Role::kTab:
       return false;
+    case ax::mojom::blink::Role::kComboBoxSelect:
     case ax::mojom::blink::Role::kPopUpButton:
     case ax::mojom::blink::Role::kLineBreak:
     case ax::mojom::blink::Role::kStaticText:
@@ -5863,12 +5864,9 @@
     // Step 2E from: http://www.w3.org/TR/accname-aam-1.1
     case ax::mojom::blink::Role::kComboBoxGrouping:
     case ax::mojom::blink::Role::kComboBoxMenuButton:
+    case ax::mojom::blink::Role::kComboBoxSelect:
     case ax::mojom::blink::Role::kListBox:
       return true;
-    // This can be either a button widget with a non-false value of
-    // aria-haspopup or a select element with size of 1.
-    case ax::mojom::blink::Role::kPopUpButton:
-      return DynamicTo<HTMLSelectElement>(*GetNode());
     default:
       return false;
   }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 94bea00..10ed64b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -450,10 +450,11 @@
 const RoleEntry kReverseRoles[] = {
     {"banner", ax::mojom::blink::Role::kHeader},
     {"button", ax::mojom::blink::Role::kToggleButton},
-    {"combobox", ax::mojom::blink::Role::kPopUpButton},
+    {"button", ax::mojom::blink::Role::kPopUpButton},
     {"contentinfo", ax::mojom::blink::Role::kFooter},
     {"menuitem", ax::mojom::blink::Role::kMenuListOption},
     {"combobox", ax::mojom::blink::Role::kComboBoxMenuButton},
+    {"combobox", ax::mojom::blink::Role::kComboBoxSelect},
     {"combobox", ax::mojom::blink::Role::kTextFieldWithComboBox}};
 
 static ARIARoleMap* CreateARIARoleMap() {
@@ -2064,12 +2065,15 @@
       node_data->AddState(ax::mojom::blink::State::kExpanded);
   }
 
-  if (HasPopup() != ax::mojom::blink::HasPopup::kFalse)
+  ax::mojom::blink::Role role = RoleValue();
+  if (HasPopup() != ax::mojom::blink::HasPopup::kFalse) {
     node_data->SetHasPopup(HasPopup());
-  else if (RoleValue() == ax::mojom::blink::Role::kPopUpButton)
+  } else if (role == ax::mojom::blink::Role::kPopUpButton ||
+             role == ax::mojom::blink::Role::kComboBoxSelect) {
     node_data->SetHasPopup(ax::mojom::blink::HasPopup::kMenu);
-  else if (ui::IsComboBox(RoleValue()))
+  } else if (ui::IsComboBox(role)) {
     node_data->SetHasPopup(ax::mojom::blink::HasPopup::kListbox);
+  }
 
   if (IsAutofillAvailable())
     node_data->AddState(ax::mojom::blink::State::kAutofillAvailable);
@@ -2144,7 +2148,7 @@
     }
 
     // Heading level.
-    if (ui::IsHeading(RoleValue()) && HeadingLevel()) {
+    if (ui::IsHeading(role) && HeadingLevel()) {
       node_data->AddIntAttribute(
           ax::mojom::blink::IntAttribute::kHierarchicalLevel, HeadingLevel());
     }
@@ -4233,6 +4237,7 @@
     case ax::mojom::blink::Role::kLink:
       return ax::mojom::blink::DefaultActionVerb::kJump;
     case ax::mojom::blink::Role::kComboBoxMenuButton:
+    case ax::mojom::blink::Role::kComboBoxSelect:
     case ax::mojom::blink::Role::kPopUpButton:
       return ax::mojom::blink::DefaultActionVerb::kOpen;
     default:
@@ -4260,6 +4265,7 @@
     case ax::mojom::blink::Role::kColumnHeader:
     case ax::mojom::blink::Role::kComboBoxGrouping:
     case ax::mojom::blink::Role::kComboBoxMenuButton:
+    case ax::mojom::blink::Role::kComboBoxSelect:
     case ax::mojom::blink::Role::kDisclosureTriangle:
     case ax::mojom::blink::Role::kListBox:
     case ax::mojom::blink::Role::kLink:
@@ -6265,6 +6271,7 @@
     case ax::mojom::blink::Role::kCell:
     case ax::mojom::blink::Role::kCheckBox:
     case ax::mojom::blink::Role::kColumnHeader:
+    case ax::mojom::blink::Role::kComboBoxSelect:
     case ax::mojom::blink::Role::kDocBackLink:
     case ax::mojom::blink::Role::kDocBiblioRef:
     case ax::mojom::blink::Role::kDocNoteRef:
diff --git a/third_party/blink/web_tests/accessibility/appearance-affects-role.html b/third_party/blink/web_tests/accessibility/appearance-affects-role.html
index a0b0891..477dc14b 100644
--- a/third_party/blink/web_tests/accessibility/appearance-affects-role.html
+++ b/third_party/blink/web_tests/accessibility/appearance-affects-role.html
@@ -142,7 +142,7 @@
     }, "Test computed AX role for <textarea>");
 
     test(function () {
-        check("select", "AXRole: AXPopUpButton");
+        check("select", "AXRole: AXComboBoxSelect");
     }, "Test computed AX role for <select>");
 
     test(function () {
diff --git a/third_party/blink/web_tests/accessibility/canvas-fallback-content.html b/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
index aad37166..796b9b1 100644
--- a/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
+++ b/third_party/blink/web_tests/accessibility/canvas-fallback-content.html
@@ -52,7 +52,6 @@
   }
 
 test((t) => {
-    var comboBoxRole = (testRunner.platformName == "gtk" || testRunner.platformName == "efl") ? "AXRole: AXComboBox" : "AXRole: AXPopUpButton";
 
     // Check rendered controls.
     check("link1", "AXRole: AXLink");
@@ -61,7 +60,7 @@
     check("checkbox1", "AXRole: AXCheckBox");
     check("radio1", "AXRole: AXRadioButton");
     check("submit1", "AXRole: AXButton");
-    check("combobox1", comboBoxRole);
+    check("combobox1", "AXRole: AXComboBoxSelect");
     check("focusable1", "AXRole: AXGenericContainer");
     check("aria-button1", "AXRole: AXButton");
     check("aria-link1", "AXRole: AXLink");
@@ -73,7 +72,7 @@
     check("checkbox2", "AXRole: AXCheckBox");
     check("radio2", "AXRole: AXRadioButton");
     check("submit2", "AXRole: AXButton", t);
-    check("combobox2", comboBoxRole, t);
+    check("combobox2", "AXRole: AXComboBoxSelect", t);
     check("focusable2", "AXRole: AXGenericContainer", t);
     check("aria-button2", "AXRole: AXButton", t);
     check("aria-link2", "AXRole: AXLink", t);
diff --git a/third_party/blink/web_tests/accessibility/element-role-mapping-focusable-expected.txt b/third_party/blink/web_tests/accessibility/element-role-mapping-focusable-expected.txt
index e79316b..31bce0d 100644
--- a/third_party/blink/web_tests/accessibility/element-role-mapping-focusable-expected.txt
+++ b/third_party/blink/web_tests/accessibility/element-role-mapping-focusable-expected.txt
@@ -72,7 +72,7 @@
 input_reset_id
 PASS elem.role is "AXRole: AXButton"
 select_id
-PASS elem.role is "AXRole: AXPopUpButton"
+PASS elem.role is "AXRole: AXComboBoxSelect"
 PASS axMenuListPopup.role is "AXRole: AXMenuListPopup"
 select_option_id
 PASS axMenuListOption.role is "AXRole: AXMenuListOption"
diff --git a/third_party/blink/web_tests/accessibility/element-role-mapping-focusable.html b/third_party/blink/web_tests/accessibility/element-role-mapping-focusable.html
index 604b0baa..ca0b125 100644
--- a/third_party/blink/web_tests/accessibility/element-role-mapping-focusable.html
+++ b/third_party/blink/web_tests/accessibility/element-role-mapping-focusable.html
@@ -86,7 +86,7 @@
     function hasSingleSelectRole(select_id, option_id) {
         debug(select_id);
         window.elem = accessibilityController.accessibleElementById(select_id);
-        shouldBe("elem.role", "\"AXRole: AXPopUpButton\"");
+        shouldBe("elem.role", "\"AXRole: AXComboBoxSelect\"");
         
         window.axMenuListPopup = elem.childAtIndex(0);
         shouldBe("axMenuListPopup.role", "\"AXRole: AXMenuListPopup\"");
diff --git a/third_party/blink/web_tests/accessibility/menu-list-optgroup.html b/third_party/blink/web_tests/accessibility/menu-list-optgroup.html
index 01bcc43..a5c05136 100644
--- a/third_party/blink/web_tests/accessibility/menu-list-optgroup.html
+++ b/third_party/blink/web_tests/accessibility/menu-list-optgroup.html
@@ -18,7 +18,7 @@
 test(function(t)
 {
     var axMenuList = accessibilityController.accessibleElementById("menulist");
-    assert_equals(axMenuList.role, "AXRole: AXPopUpButton");
+    assert_equals(axMenuList.role, "AXRole: AXComboBoxSelect");
     var axMenuListPopup = axMenuList.childAtIndex(0);
     assert_equals(axMenuListPopup.role, "AXRole: AXMenuListPopup");
     assert_equals(axMenuListPopup.childrenCount, 7);
diff --git a/third_party/blink/web_tests/accessibility/notification-listeners.html b/third_party/blink/web_tests/accessibility/notification-listeners.html
index 9872129..e1801bf 100644
--- a/third_party/blink/web_tests/accessibility/notification-listeners.html
+++ b/third_party/blink/web_tests/accessibility/notification-listeners.html
@@ -32,14 +32,14 @@
 
     let expected_global_notifications = new Map([
       ["Focus", new Map([
-        ["AXRole: AXPopUpButton", 1],
+        ["AXRole: AXComboBoxSelect", 1],
         ["AXRole: AXSlider", 1],
       ])],
       ["Blur", new Map([
-        ["AXRole: AXPopUpButton", 1],
+        ["AXRole: AXComboBoxSelect", 1],
       ])],
       ["MarkDirty", new Map([
-        ["AXRole: AXPopUpButton", 1],
+        ["AXRole: AXComboBoxSelect", 1],
       ])],
       ["ValueChanged", new Map([
         ["AXRole: AXSlider", 1],
diff --git a/third_party/blink/web_tests/accessibility/role-attribute-expected.txt b/third_party/blink/web_tests/accessibility/role-attribute-expected.txt
index a67cb02b..7640ed2 100644
--- a/third_party/blink/web_tests/accessibility/role-attribute-expected.txt
+++ b/third_party/blink/web_tests/accessibility/role-attribute-expected.txt
@@ -70,7 +70,7 @@
             AXRole: AXMenuListPopup
                 AXRole: AXTextField "Explicit dropdown 1"
                 AXRole: AXTextField "Explicit dropdown 2"
-        AXRole: AXPopUpButton
+        AXRole: AXComboBoxSelect
             AXRole: AXMenuListPopup
                 AXRole: AXMenuListOption "Implicit dropdown 1"
                 AXRole: AXMenuListOption "Implicit dropdown 2"
diff --git a/third_party/blink/web_tests/accessibility/role-change.html b/third_party/blink/web_tests/accessibility/role-change.html
index b3bc8eb..bc4ccfb1 100644
--- a/third_party/blink/web_tests/accessibility/role-change.html
+++ b/third_party/blink/web_tests/accessibility/role-change.html
@@ -95,7 +95,7 @@
 
     // Change @size to "1"
     document.getElementById('select1').setAttribute('size', '1');
-    assert_equals(axElementById('select1').role, 'AXRole: AXPopUpButton');
+    assert_equals(axElementById('select1').role, 'AXRole: AXComboBoxSelect');
     assert_equals(axElementById('opt1').role, 'AXRole: AXMenuListOption');
 
     // Change @size back to "2"
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
index f7b98bf5..1cc9a89 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
@@ -433,7 +433,7 @@
     childIds : <object>
     chromeRole : {
         type : internalRole
-        value : 137
+        value : 209
     }
     domNode : select
     ignored : false
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index b52d1e3..dc0d258 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -164,6 +164,7 @@
   COLUMN_HEADER: 'columnHeader',
   COMBO_BOX_GROUPING: 'comboBoxGrouping',
   COMBO_BOX_MENU_BUTTON: 'comboBoxMenuButton',
+  COMBO_BOX_SELECT: 'comboBoxSelect',
   COMMENT: 'comment',
   COMPLEMENTARY: 'complementary',
   CONTENT_DELETION: 'contentDeletion',
diff --git a/ui/accessibility/ax_assistant_structure.cc b/ui/accessibility/ax_assistant_structure.cc
index f49c17b..ad01a84 100644
--- a/ui/accessibility/ax_assistant_structure.cc
+++ b/ui/accessibility/ax_assistant_structure.cc
@@ -123,8 +123,9 @@
 
     switch (node->GetRole()) {
       case ax::mojom::Role::kComboBoxMenuButton:
-      case ax::mojom::Role::kTextFieldWithComboBox:
+      case ax::mojom::Role::kComboBoxSelect:
       case ax::mojom::Role::kPopUpButton:
+      case ax::mojom::Role::kTextFieldWithComboBox:
       case ax::mojom::Role::kTextField:
         return value;
       default:
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index b6c1fc603..1855e658 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -180,6 +180,8 @@
       return "comboBoxGrouping";
     case ax::mojom::Role::kComboBoxMenuButton:
       return "comboBoxMenuButton";
+    case ax::mojom::Role::kComboBoxSelect:
+      return "comboBoxSelect";
     case ax::mojom::Role::kComment:
       return "comment";
     case ax::mojom::Role::kComplementary:
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 4a3b149..5baca3eb 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -108,8 +108,8 @@
 // Web: this attribute is only used in web content.
 //
 // Native: this attribute is only used in native UI.
-// Next version: 4
-// Next value: 209
+// Next version: 5
+// Next value: 210
 [Extensible, Stable, Uuid="d258eb73-e0cc-490c-b881-80ee11d3fec2"]
 enum Role {
   [Default]kUnknown = 181, // The role has not been set.
@@ -132,8 +132,16 @@
   kColorWell = 17,
   kColumn = 18,
   kColumnHeader = 19,
+  // kComboBoxGrouping represents a combobox container that groups
+  // subcomponents. It always contains a button and a listbox. It may also
+  // contain a listbox as in case of an editable combobox (e.g. Views'
+  // EditableCombobox).
   kComboBoxGrouping = 20,
+  // kComboBoxMenuButton represents the arrow button part of a combobox, or a
+  // <button> with and explicit combobox role set.
   kComboBoxMenuButton = 21,
+  // kComboBoxSelect represents an HTML single-value <select> element.
+  [MinVersion=4] kComboBoxSelect = 209,
   kComplementary = 22,
   kComment = 23,
   kContentDeletion = 24,
@@ -287,6 +295,8 @@
   kPdfActionableHighlight = 134,
   kPdfRoot = 135,
   kPluginObject = 136,
+  // kPopUpButton is used for a button role that has a popup. It is not a
+  // combobox which can contain a value and associated list of values.
   kPopUpButton = 137,
   kPortal = 138,
   kPre = 139,
@@ -323,6 +333,8 @@
   kTableHeaderContainer = 168,
   kTerm = 169,
   kTextField = 170,
+  // kTextFieldWithComboBox represents an HTML <input> element with a datalist.
+  // <input type=text role=combobox> is one such example.
   kTextFieldWithComboBox = 171,
   kTime = 172,
   kTimer = 173,
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index 7108d1e..77575f8d 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -232,7 +232,7 @@
   initial_state.nodes[2].id = 3;
   initial_state.nodes[2].role = ax::mojom::Role::kRow;
   initial_state.nodes[3].id = 4;
-  initial_state.nodes[3].role = ax::mojom::Role::kPopUpButton;
+  initial_state.nodes[3].role = ax::mojom::Role::kComboBoxSelect;
   initial_state.nodes[3].AddState(ax::mojom::State::kExpanded);
   AXTree tree(initial_state);
 
@@ -313,7 +313,7 @@
   // </select>
   //
   // kRootWebArea
-  // ++kPopUpButton value="Item 1"
+  // ++kComboBoxSelect value="Item 1"
   // ++++kMenuListPopup invisible
   // ++++++kMenuListOption name="Item 1" selected=true
   // ++++++kMenuListOption name="Item 2" selected=false
@@ -327,7 +327,7 @@
 
   AXNodeData popup_button;
   popup_button.id = 2;
-  popup_button.role = ax::mojom::Role::kPopUpButton;
+  popup_button.role = ax::mojom::Role::kComboBoxSelect;
   popup_button.SetValue("Item 1");
 
   AXNodeData menu_list_popup;
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index 42b3bde..5fee9b3 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -1719,8 +1719,8 @@
       // and setsize.
       return item_role == ax::mojom::Role::kDescriptionListTerm ||
              item_role == ax::mojom::Role::kTerm;
-    case ax::mojom::Role::kPopUpButton:
-      // kPopUpButtons can wrap a kMenuListPopUp.
+    case ax::mojom::Role::kComboBoxSelect:
+      // kComboBoxSelect wraps a kMenuListPopUp.
       return item_role == ax::mojom::Role::kMenuListPopup;
     default:
       return false;
@@ -1798,14 +1798,6 @@
   if (IsCellOrHeaderOfAriaGrid())
     return true;
 
-  // kPopUpButton is special in that it is the role Blink assigns for both
-  // role=button with aria-haspopup set, along with <select> elements.
-  // HTML AAM (https://w3c.github.io/html-aam/) maps <select> to the combobox
-  // role, which supports readonly, but readonly is not supported for button
-  // roles.
-  if (GetRole() == ax::mojom::Role::kPopUpButton && !IsMenuListPopUpButton())
-    return false;
-
   return ui::IsReadOnlySupported(GetRole());
 }
 
@@ -1963,9 +1955,9 @@
   // On Windows, we want to hide the subtree of a collapsed <select> element.
   // Otherwise, ATs are always going to announce its options whether it's
   // collapsed or expanded. In the AXTree, this element corresponds to a node
-  // with role ax::mojom::Role::kPopUpButton that is the parent of a node with
-  // role ax::mojom::Role::kMenuListPopup.
-  if (IsCollapsedMenuListPopUpButton())
+  // with role ax::mojom::Role::kComboBoxSelect that is the parent of a node
+  // with // role ax::mojom::Role::kMenuListPopup.
+  if (IsCollapsedMenuListSelect())
     return true;
 #endif  // BUILDFLAG(IS_WIN)
 
@@ -2076,24 +2068,9 @@
          grandparent_node->GetRole() == ax::mojom::Role::kListMarker;
 }
 
-bool AXNode::IsMenuListPopUpButton() const {
-  if (GetRole() != ax::mojom::Role::kPopUpButton)
-    return false;
-
-  // When a popup button contains a menu list popup, its only child is unignored
-  // and is a menu list popup.
-  AXNode* node = GetFirstUnignoredChild();
-  if (!node)
-    return false;
-
-  return node->GetRole() == ax::mojom::Role::kMenuListPopup;
-}
-
-bool AXNode::IsCollapsedMenuListPopUpButton() const {
-  if (!HasState(ax::mojom::State::kCollapsed))
-    return false;
-
-  return IsMenuListPopUpButton();
+bool AXNode::IsCollapsedMenuListSelect() const {
+  return HasState(ax::mojom::State::kCollapsed) &&
+         GetRole() == ax::mojom::Role::kComboBoxSelect;
 }
 
 bool AXNode::IsRootWebAreaForPresentationalIframe() const {
@@ -2105,22 +2082,22 @@
   return parent->GetRole() == ax::mojom::Role::kIframePresentational;
 }
 
-AXNode* AXNode::GetCollapsedMenuListPopUpButtonAncestor() const {
+AXNode* AXNode::GetCollapsedMenuListSelectAncestor() const {
   AXNode* node = GetOrderedSet();
 
   if (!node)
     return nullptr;
 
-  // The ordered set returned is either the popup element child of the popup
-  // button (e.g., the AXMenuListPopup) or the popup button itself. We need
-  // |node| to point to the popup button itself.
-  if (node->GetRole() != ax::mojom::Role::kPopUpButton) {
+  // The ordered set returned is either the popup element child of the select
+  // combobox or the select combobox itself. We need |node| to point to the
+  // select combobox.
+  if (node->GetRole() != ax::mojom::Role::kComboBoxSelect) {
     node = node->GetParent();
     if (!node)
       return nullptr;
   }
 
-  return node->IsCollapsedMenuListPopUpButton() ? node : nullptr;
+  return node->IsCollapsedMenuListSelect() ? node : nullptr;
 }
 
 bool AXNode::IsEmbeddedGroup() const {
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index ef2c003..cf7c6f7 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -684,13 +684,9 @@
   // of a list marker node. Returns false otherwise.
   bool IsInListMarker() const;
 
-  // Returns true if this node is a popup button that is a parent to a menu list
-  // popup.
-  bool IsMenuListPopUpButton() const;
-
-  // Returns true if this node is a collapsed popup button that is parent to a
-  // menu list popup.
-  bool IsCollapsedMenuListPopUpButton() const;
+  // Returns true if this node is a collapsed combobox select that is parent to
+  // a menu list popup.
+  bool IsCollapsedMenuListSelect() const;
 
   // Returns true if this node is at the root of an accessibility tree that is
   // hosted by a presentational iframe.
@@ -699,7 +695,7 @@
   // Returns the popup button ancestor of this current node if any. The popup
   // button needs to be the parent of a menu list popup and needs to be
   // collapsed.
-  AXNode* GetCollapsedMenuListPopUpButtonAncestor() const;
+  AXNode* GetCollapsedMenuListSelectAncestor() const;
 
   // If this node is exposed to the platform's accessibility layer, returns this
   // node. Otherwise, returns the lowest ancestor that is exposed to the
diff --git a/ui/accessibility/ax_node_data_unittest.cc b/ui/accessibility/ax_node_data_unittest.cc
index 34fc6d7..e7072fae 100644
--- a/ui/accessibility/ax_node_data_unittest.cc
+++ b/ui/accessibility/ax_node_data_unittest.cc
@@ -167,6 +167,7 @@
       ax::mojom::Role::kCheckBox,
       ax::mojom::Role::kColorWell,
       ax::mojom::Role::kComboBoxMenuButton,
+      ax::mojom::Role::kComboBoxSelect,
       ax::mojom::Role::kDate,
       ax::mojom::Role::kDateTime,
       ax::mojom::Role::kDisclosureTriangle,
@@ -341,9 +342,12 @@
   AXNodeData data;
 
   std::unordered_set<ax::mojom::Role> roles_expected_supports_expand_collapse =
-      {ax::mojom::Role::kComboBoxGrouping, ax::mojom::Role::kComboBoxMenuButton,
+      {ax::mojom::Role::kComboBoxGrouping,
+       ax::mojom::Role::kComboBoxMenuButton,
+       ax::mojom::Role::kComboBoxSelect,
        ax::mojom::Role::kDisclosureTriangle,
-       ax::mojom::Role::kTextFieldWithComboBox, ax::mojom::Role::kTreeItem};
+       ax::mojom::Role::kTextFieldWithComboBox,
+       ax::mojom::Role::kTreeItem};
 
   for (int role_idx = static_cast<int>(ax::mojom::Role::kMinValue);
        role_idx <= static_cast<int>(ax::mojom::Role::kMaxValue); role_idx++) {
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 260489d..8a3303d5 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -5205,7 +5205,7 @@
 
   AXNodeData popup_button_5;
   popup_button_5.id = 5;
-  popup_button_5.role = ax::mojom::Role::kPopUpButton;
+  popup_button_5.role = ax::mojom::Role::kComboBoxSelect;
   popup_button_5.AddState(ax::mojom::State::kCollapsed);
   popup_button_5.SetName("option 1");
 
@@ -5236,7 +5236,7 @@
 
   AXNodeData popup_button_11;
   popup_button_11.id = 11;
-  popup_button_11.role = ax::mojom::Role::kPopUpButton;
+  popup_button_11.role = ax::mojom::Role::kComboBoxSelect;
   popup_button_11.AddState(ax::mojom::State::kCollapsed);
   popup_button_11.SetName("option 2");
 
@@ -5253,7 +5253,7 @@
 
   AXNodeData popup_button_14;
   popup_button_14.id = 14;
-  popup_button_14.role = ax::mojom::Role::kPopUpButton;
+  popup_button_14.role = ax::mojom::Role::kComboBoxSelect;
   popup_button_14.AddState(ax::mojom::State::kCollapsed);
   popup_button_14.SetName("option 3");
 
@@ -12159,7 +12159,7 @@
   // ++1 kRootWebArea
   // ++++2 kStaticText "Hi"
   // ++++++3 kInlineTextBox "Hi"
-  // ++++4 kPopUpButton
+  // ++++4 kComboBoxSelect
   // ++++++5 kMenuListPopup
   // ++++++++6 kMenuListOption "Option"
   // ++++7 kStaticText "3.14"
@@ -12199,7 +12199,7 @@
                                    {0});
   inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, {2});
 
-  popup_button_4.role = ax::mojom::Role::kPopUpButton;
+  popup_button_4.role = ax::mojom::Role::kComboBoxSelect;
   popup_button_4.child_ids = {menu_list_popup_5.id};
   popup_button_4.AddState(ax::mojom::State::kCollapsed);
 
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index ae48e078..30641ea 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -448,7 +448,7 @@
     // subtree representation we get from a collapsed <select> element on
     // Windows) should not expose its descendants even though they are not
     // ignored.
-    if (node.IsCollapsedMenuListPopUpButton())
+    if (node.IsCollapsedMenuListSelect())
       return true;
 
     // All anchor nodes that are empty leaf nodes should be treated as empty
@@ -3982,7 +3982,7 @@
       // the parent of a menu list popup, or on all platforms inside a generic
       // container that is the child of an empty text field.
       if (AXNode* popup_button =
-              GetAnchor()->GetCollapsedMenuListPopUpButtonAncestor()) {
+              GetAnchor()->GetCollapsedMenuListSelectAncestor()) {
         return popup_button;
       }
 
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index 7b4fa71..f69fed2 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -85,6 +85,10 @@
   // Role::kToggleButton.
   // https://www.w3.org/TR/wai-aria-1.1/#button
   return role == ax::mojom::Role::kButton ||
+         // TODO(crbug.com/1362834): Treat kComboBoxSelect like a combobox.
+         // When removing this, update ChromeVox's AutomationPredicate wherever
+         // it's looking at isButton.
+         role == ax::mojom::Role::kComboBoxSelect ||
          role == ax::mojom::Role::kPopUpButton ||
          role == ax::mojom::Role::kToggleButton;
 }
@@ -122,6 +126,7 @@
     case ax::mojom::Role::kCheckBox:
     case ax::mojom::Role::kColorWell:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDate:
     case ax::mojom::Role::kDateTime:
     case ax::mojom::Role::kDisclosureTriangle:
@@ -169,6 +174,7 @@
 }
 
 bool IsComboBox(const ax::mojom::Role role) {
+  // TODO(crbug.com/1362834): Treat kComboBoxSelect like a combobox.
   switch (role) {
     case ax::mojom::Role::kComboBoxMenuButton:
     case ax::mojom::Role::kComboBoxGrouping:
@@ -218,6 +224,7 @@
     case ax::mojom::Role::kCheckBox:
     case ax::mojom::Role::kColorWell:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDate:
     case ax::mojom::Role::kDateTime:
     case ax::mojom::Role::kDisclosureTriangle:
@@ -520,6 +527,7 @@
     case ax::mojom::Role::kColorWell:
     case ax::mojom::Role::kComboBoxGrouping:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDate:
     case ax::mojom::Role::kDateTime:
     case ax::mojom::Role::kGrid:
@@ -528,7 +536,6 @@
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
     case ax::mojom::Role::kMenuListPopup:
-    case ax::mojom::Role::kPopUpButton:
     case ax::mojom::Role::kRadioButton:
     case ax::mojom::Role::kRadioGroup:
     case ax::mojom::Role::kSearchBox:
@@ -659,10 +666,10 @@
 
 bool IsSelectElement(const ax::mojom::Role role) {
   // Depending on their "size" attribute, <select> elements come in two flavors:
-  // the first appears like a list box and the second like a popup menu.
+  // the first appears like a list box and the second a specific combobox type.
   switch (role) {
     case ax::mojom::Role::kListBox:
-    case ax::mojom::Role::kPopUpButton:
+    case ax::mojom::Role::kComboBoxSelect:
       return true;
     default:
       return false;
@@ -700,6 +707,7 @@
 
 bool IsSetLike(const ax::mojom::Role role) {
   switch (role) {
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDescriptionList:
     case ax::mojom::Role::kDirectory:
     case ax::mojom::Role::kDocBibliography:
@@ -889,6 +897,7 @@
     case ax::mojom::Role::kColumnHeader:
     case ax::mojom::Role::kComboBoxGrouping:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDate:
     case ax::mojom::Role::kDateTime:
     case ax::mojom::Role::kDescriptionList:
@@ -1015,6 +1024,7 @@
   switch (role) {
     case ax::mojom::Role::kComboBoxGrouping:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
     case ax::mojom::Role::kDisclosureTriangle:
     case ax::mojom::Role::kTextFieldWithComboBox:
     case ax::mojom::Role::kTreeItem:
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 84b165fe..14ae2c4a0 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -2419,11 +2419,11 @@
   OrderedSetItemsMap items_map_to_be_populated;
   PopulateOrderedSetItemsMap(node, ordered_set, &items_map_to_be_populated);
 
-  // If ordered_set role is kPopUpButton and it wraps a kMenuListPopUp, then we
-  // would like it to inherit the SetSize from the kMenuListPopUp it wraps. To
-  // do this, we treat the kMenuListPopUp as the ordered_set and eventually
-  // assign its SetSize value to the kPopUpButton.
-  if (node.GetRole() == ax::mojom::Role::kPopUpButton &&
+  // If ordered_set role is kComboBoxSelect and it wraps a kMenuListPopUp, then
+  // we would like it to inherit the SetSize from the kMenuListPopUp it wraps.
+  // To do this, we treat the kMenuListPopUp as the ordered_set and eventually
+  // assign its SetSize value to the kComboBoxSelect.
+  if (node.GetRole() == ax::mojom::Role::kComboBoxSelect &&
       node.GetUnignoredChildCount() > 0) {
     // kPopUpButtons are only allowed to contain one kMenuListPopUp.
     // The single element is guaranteed to be a kMenuListPopUp because that is
@@ -2540,7 +2540,8 @@
 }
 
 absl::optional<int> AXTree::GetPosInSet(const AXNode& node) {
-  if (node.GetRole() == ax::mojom::Role::kPopUpButton &&
+  if ((node.GetRole() == ax::mojom::Role::kComboBoxSelect ||
+       node.GetRole() == ax::mojom::Role::kPopUpButton) &&
       node.GetUnignoredChildCount() == 0 &&
       node.HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) {
     return node.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
@@ -2574,7 +2575,8 @@
 }
 
 absl::optional<int> AXTree::GetSetSize(const AXNode& node) {
-  if (node.GetRole() == ax::mojom::Role::kPopUpButton &&
+  if ((node.GetRole() == ax::mojom::Role::kComboBoxSelect ||
+       node.GetRole() == ax::mojom::Role::kPopUpButton) &&
       node.GetUnignoredChildCount() == 0 &&
       node.HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) {
     return node.GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
@@ -2608,7 +2610,8 @@
 
   // For popup buttons that control a single element, inherit the controlled
   // item's SetSize. Skip this block if the popup button controls itself.
-  if (node.GetRole() == ax::mojom::Role::kPopUpButton) {
+  if (node.GetRole() == ax::mojom::Role::kPopUpButton ||
+      node.GetRole() == ax::mojom::Role::kComboBoxSelect) {
     const auto& controls_ids =
         node.GetIntListAttribute(ax::mojom::IntListAttribute::kControlsIds);
     if (controls_ids.size() == 1 && GetFromId(controls_ids[0]) &&
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 4835507..06e9ad6 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -4429,7 +4429,7 @@
 
 // Tests that kPopUpButtons are assigned the SetSize of the wrapped
 // kMenuListPopup, if one is present.
-TEST(AXTreeTest, SetSizePosInSetPopUpButton) {
+TEST(AXTreeTest, SetSizePosInSetPopUpButtonAndSelect) {
   AXTreeUpdate initial_state;
   initial_state.root_id = 1;
   initial_state.nodes.resize(6);
@@ -4438,7 +4438,7 @@
   initial_state.nodes[1].id = 2;
   initial_state.nodes[1].role = ax::mojom::Role::kPopUpButton;
   initial_state.nodes[2].id = 3;
-  initial_state.nodes[2].role = ax::mojom::Role::kPopUpButton;
+  initial_state.nodes[2].role = ax::mojom::Role::kComboBoxSelect;
   initial_state.nodes[2].child_ids = {4};
   initial_state.nodes[3].id = 4;
   initial_state.nodes[3].role = ax::mojom::Role::kMenuListPopup;
@@ -4452,7 +4452,7 @@
   // The first popupbutton should have SetSize of 0.
   AXNode* popup_button_1 = tree.GetFromId(2);
   EXPECT_OPTIONAL_EQ(0, popup_button_1->GetSetSize());
-  // The second popupbutton should have SetSize of 2, since the menulistpopup
+  // The select should have SetSize of 2, since the menulistpopup
   // that it wraps has a SetSize of 2.
   AXNode* popup_button_2 = tree.GetFromId(3);
   EXPECT_OPTIONAL_EQ(2, popup_button_2->GetSetSize());
diff --git a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
index 2b73927..660d4e7 100644
--- a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
+++ b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
@@ -145,6 +145,7 @@
 AutomationPredicate.leaf = function(node) {
   return !node.firstChild || node.role == RoleType.BUTTON ||
       node.role == RoleType.BUTTONDROPDOWN ||
+      node.role == RoleType.COMBO_BOX_SELECT ||
       node.role == RoleType.POP_UP_BUTTON || node.role == RoleType.SLIDER ||
       node.role == RoleType.TEXT_FIELD || node.state.invisible ||
       node.children.every(function(n) {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 3e4e2ca..01dc629 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2692,6 +2692,8 @@
       return ATK_ROLE_COMBO_BOX;
     case ax::mojom::Role::kComboBoxMenuButton:
       return ATK_ROLE_COMBO_BOX;
+    case ax::mojom::Role::kComboBoxSelect:
+      return ATK_ROLE_COMBO_BOX;
     case ax::mojom::Role::kComplementary:
       return ATK_ROLE_LANDMARK;
     case ax::mojom::Role::kContentDeletion:
@@ -2936,13 +2938,8 @@
       return ATK_ROLE_DOCUMENT_FRAME;
     case ax::mojom::Role::kPluginObject:
       return ATK_ROLE_EMBEDDED;
-    case ax::mojom::Role::kPopUpButton: {
-      std::string html_tag =
-          GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
-      if (html_tag == "select")
-        return ATK_ROLE_COMBO_BOX;
+    case ax::mojom::Role::kPopUpButton:
       return ATK_ROLE_PUSH_BUTTON;
-    }
     case ax::mojom::Role::kPortal:
       return ATK_ROLE_PUSH_BUTTON;
     case ax::mojom::Role::kPre:
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index a152d1c..3e26aeb 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -355,7 +355,7 @@
         delegate_->GetFromNodeID(active_descendant_id));
   }
 
-  if (GetRole() == ax::mojom::Role::kPopUpButton) {
+  if (GetRole() == ax::mojom::Role::kComboBoxSelect) {
     AXPlatformNodeBase* child = GetFirstChild();
     if (child && child->GetRole() == ax::mojom::Role::kMenuListPopup &&
         !child->IsInvisibleOrIgnored()) {
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm
index ee41555c..4b2eb19 100644
--- a/ui/accessibility/platform/ax_platform_node_cocoa.mm
+++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -184,7 +184,8 @@
 bool AlsoUseShowMenuActionForDefaultAction(const ui::AXPlatformNodeBase& node) {
   return HasImplicitAction(node, ax::mojom::Action::kDoDefault) &&
          !node.HasAction(ax::mojom::Action::kShowContextMenu) &&
-         node.GetRole() == ax::mojom::Role::kPopUpButton;
+         (node.GetRole() == ax::mojom::Role::kPopUpButton ||
+          node.GetRole() == ax::mojom::Role::kComboBoxSelect);
 }
 
 // Check whether |selector| is an accessibility setter. This is a heuristic but
@@ -484,6 +485,9 @@
       return NSAccessibilityComboBoxRole;
     case ax::mojom::Role::kComboBoxMenuButton:
       return NSAccessibilityComboBoxRole;
+    case ax::mojom::Role::kComboBoxSelect:
+      // TODO(crbug.com/1362834): Can this be NSAccessibilityComboBoxRole?
+      return NSAccessibilityPopUpButtonRole;
     case ax::mojom::Role::kDate:
       return @"AXDateField";
     case ax::mojom::Role::kDateTime:
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 0d16eec..33c54e5 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -864,6 +864,10 @@
       return {UIALocalizationStrategy::kSupply, UIA_ComboBoxControlTypeId,
               L"combobox"};
 
+    case ax::mojom::Role::kComboBoxSelect:
+      return {UIALocalizationStrategy::kDeferToControlType,
+              UIA_ComboBoxControlTypeId, L"combobox"};
+
     case ax::mojom::Role::kComplementary:
       return {UIALocalizationStrategy::kDeferToAriaRole, UIA_GroupControlTypeId,
               L"complementary"};
@@ -1227,16 +1231,9 @@
       return {UIALocalizationStrategy::kSupply, UIA_DocumentControlTypeId,
               L"document"};
 
-    case ax::mojom::Role::kPopUpButton: {
-      const std::string& html_tag =
-          GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
-      if (html_tag == "select") {
-        return {UIALocalizationStrategy::kDeferToControlType,
-                UIA_ComboBoxControlTypeId, L"combobox"};
-      }
+    case ax::mojom::Role::kPopUpButton:
       return {UIALocalizationStrategy::kDeferToControlType,
               UIA_ButtonControlTypeId, L"button"};
-    }
 
     case ax::mojom::Role::kPortal:
       return {UIALocalizationStrategy::kSupply, UIA_ButtonControlTypeId,
@@ -6196,6 +6193,7 @@
 
     case ax::mojom::Role::kComboBoxGrouping:
     case ax::mojom::Role::kComboBoxMenuButton:
+    case ax::mojom::Role::kComboBoxSelect:
       return ROLE_SYSTEM_COMBOBOX;
 
     case ax::mojom::Role::kComplementary:
@@ -6483,13 +6481,8 @@
         return ROLE_SYSTEM_CLIENT;
       }
 
-    case ax::mojom::Role::kPopUpButton: {
-      std::string html_tag =
-          GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
-      if (html_tag == "select")
-        return ROLE_SYSTEM_COMBOBOX;
+    case ax::mojom::Role::kPopUpButton:
       return ROLE_SYSTEM_BUTTONMENU;
-    }
 
     case ax::mojom::Role::kPortal:
       return ROLE_SYSTEM_PUSHBUTTON;