Track nested <input> in <select> for FilterableSelect [chromium/src : main]

0 views
Skip to first unread message

Mason Freed (Gerrit)

unread,
May 8, 2026, 12:12:57 PM (4 days ago) May 8
to Code Review Nudger, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org
Attention needed from Joey Arhar

Mason Freed added 16 comments

Patchset-level comments
File-level comment, Patchset 8:
Mason Freed . resolved

Overall, I think I like the approach. It'll be interesting to see how this affects general perf, e.g. speedometer. But for now, behind the flag, I think it's ok.

File third_party/blink/renderer/core/html/forms/html_input_element.h
Line 537, Patchset 8: Member<Node> nearest_ancestor_select_child_;
Mason Freed . unresolved

`Element` perhaps? Or `ContainerNode` might be better?

Also, these two could use a descriptive comment.

File third_party/blink/renderer/core/html/forms/html_input_element.cc
Line 1852, Patchset 8: HTMLSelectElement::AssociatedSelectAndOptgroupAndDatalist(*this);
Mason Freed . unresolved

I think it'd be good to rename this to indicate that it's a (potentially slow) tree walk. Perhaps `WalkAncestorsForRelatedParts` or something?

(Because I'm actually a bit worried about performance - this will likely negatively impact speedometer, once the flag is enabled. Ok to land now, but heads up for later.)

Line 1882, Patchset 8: if (result.select != nearest_ancestor_select_) {
Mason Freed . unresolved

Since we're calling this after the element is removed (right?), won't this always be true? Or is this for the case where you remove the entire tree?

File third_party/blink/renderer/core/html/forms/html_option_element.h
Line 228, Patchset 8: Member<Node> nearest_ancestor_select_child_;
Mason Freed . unresolved

ditto

File third_party/blink/renderer/core/html/forms/html_option_element.cc
Line 591, Patchset 8:void HTMLOptionElement::UpdateAncestors() {
Mason Freed . unresolved

Maybe the same comment? E.g. `WalkAncestorsAndUpdate()`?

File third_party/blink/renderer/core/html/forms/html_select_element.h
Line 570, Patchset 8: // num_descendant_inputs_ is redundant with the sum of num_inputs in each
Mason Freed . unresolved

Perhaps it's worth a helper function that only DCHECKs that this var matches the sum of the map contents? (DCHECK because this will be slow.) Then sprinkle calls to this function in interesting places?

Line 552, Patchset 8: // child element. This is used in order to change the structure of the DOM in
// the shadow root and to determine which slot each child node is slotted
// into.
Mason Freed . unresolved

You might also include why it's a count and not a bool. (I think the answer is that it helps in tracking removals?)

Line 421, Patchset 8: const HeapHashMap<Member<Node>, DescendantCounts> ChildrenDescendantCounts()
Mason Freed . unresolved

This should probably return by reference, not value (copy).

Line 194, Patchset 8: void InputRemoved(HTMLInputElement*, Node* select_child);
Mason Freed . unresolved

These could use descriptive comments. And I wonder if the arg should be `nearest_ancestor_select_child`? I'm on the fence about that, so if you prefer as-is, ok.

Line 185, Patchset 8: HTMLSelectElement* select;
HTMLOptGroupElement* optgroup;
HTMLDataListElement* datalist;
Node* select_child;
Mason Freed . unresolved

It seems like all of these should be initialized to `nullptr`? Especially if you take my suggestion below to use designated initializers.

File third_party/blink/renderer/core/html/forms/html_select_element.cc
Line 411, Patchset 8: node.AddConsoleMessage(
Mason Freed . unresolved

PErhaps add a TODO to make this a proper accessibility warning on the Issues pane, with the red squiggly?

Line 2097, Patchset 8: const Element& element) {
Mason Freed . unresolved

Is it possible to make this non-const, to avoid the const_cast below?

Line 2110, Patchset 8: return {nullptr, ancestor_optgroup, nullptr, nullptr};
Mason Freed . unresolved

For all of these, I think it'd be a lot more readable (and easier to expand, as in this patch) to use designated initializers:

```
return {.optgroup = ancestor_optgroup};
```

Line 2167, Patchset 8: auto insert_result = children_descendant_counts_map_.insert(
Mason Freed . unresolved

I don't understand why we're inserting here? There are two paths (input and option) and both of them CHECK that the retrieved value is not what we're inserting here. Seems much more straightforward to just use `children_descendant_counts_map_.at()` to retrieve the value (or CHECK if it's not there) and then just CHECK that one of the two values is `>0`. Either that, or I'm missing something about how this works.

Line 2184, Patchset 8: // This has to be run after `insert_result` is destroyed or else a DCHECK
Mason Freed . unresolved

The above comment *might* also fix this situation.

Open in Gerrit

Related details

Attention is currently required from:
  • Joey Arhar
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
Gerrit-Change-Number: 7793502
Gerrit-PatchSet: 8
Gerrit-Owner: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-Attention: Joey Arhar <jar...@chromium.org>
Gerrit-Comment-Date: Fri, 08 May 2026 16:12:48 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Joey Arhar (Gerrit)

unread,
May 8, 2026, 6:08:59 PM (4 days ago) May 8
to Code Review Nudger, Mason Freed, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org
Attention needed from Mason Freed

Joey Arhar added 15 comments

File third_party/blink/renderer/core/html/forms/html_input_element.h
Line 537, Patchset 8: Member<Node> nearest_ancestor_select_child_;
Mason Freed . resolved

`Element` perhaps? Or `ContainerNode` might be better?

Also, these two could use a descriptive comment.

Joey Arhar

i added a comment and changed it to ContainerNode

File third_party/blink/renderer/core/html/forms/html_input_element.cc
Line 1852, Patchset 8: HTMLSelectElement::AssociatedSelectAndOptgroupAndDatalist(*this);
Mason Freed . resolved

I think it'd be good to rename this to indicate that it's a (potentially slow) tree walk. Perhaps `WalkAncestorsForRelatedParts` or something?

(Because I'm actually a bit worried about performance - this will likely negatively impact speedometer, once the flag is enabled. Ok to land now, but heads up for later.)

Joey Arhar

Done

Line 1882, Patchset 8: if (result.select != nearest_ancestor_select_) {
Mason Freed . resolved

Since we're calling this after the element is removed (right?), won't this always be true? Or is this for the case where you remove the entire tree?

Joey Arhar

In the case where the "entire tree" above the select element is removed, then yeah result.select will still be the same as nearest_ancestor_select_.

File third_party/blink/renderer/core/html/forms/html_option_element.h
Line 228, Patchset 8: Member<Node> nearest_ancestor_select_child_;
Mason Freed . resolved

ditto

Joey Arhar

Done

File third_party/blink/renderer/core/html/forms/html_option_element.cc
Line 591, Patchset 8:void HTMLOptionElement::UpdateAncestors() {
Mason Freed . resolved

Maybe the same comment? E.g. `WalkAncestorsAndUpdate()`?

Joey Arhar

Done

File third_party/blink/renderer/core/html/forms/html_select_element.h
Line 570, Patchset 8: // num_descendant_inputs_ is redundant with the sum of num_inputs in each
Mason Freed . resolved

Perhaps it's worth a helper function that only DCHECKs that this var matches the sum of the map contents? (DCHECK because this will be slow.) Then sprinkle calls to this function in interesting places?

Joey Arhar

Done

Line 552, Patchset 8: // child element. This is used in order to change the structure of the DOM in
// the shadow root and to determine which slot each child node is slotted
// into.
Mason Freed . resolved

You might also include why it's a count and not a bool. (I think the answer is that it helps in tracking removals?)

Joey Arhar

Done

Line 421, Patchset 8: const HeapHashMap<Member<Node>, DescendantCounts> ChildrenDescendantCounts()
Mason Freed . resolved

This should probably return by reference, not value (copy).

Joey Arhar

Done

Line 194, Patchset 8: void InputRemoved(HTMLInputElement*, Node* select_child);
Mason Freed . resolved

These could use descriptive comments. And I wonder if the arg should be `nearest_ancestor_select_child`? I'm on the fence about that, so if you prefer as-is, ok.

Joey Arhar

Done

Line 185, Patchset 8: HTMLSelectElement* select;
HTMLOptGroupElement* optgroup;
HTMLDataListElement* datalist;
Node* select_child;
Mason Freed . resolved

It seems like all of these should be initialized to `nullptr`? Especially if you take my suggestion below to use designated initializers.

Joey Arhar

Done

File third_party/blink/renderer/core/html/forms/html_select_element.cc
Line 411, Patchset 8: node.AddConsoleMessage(
Mason Freed . resolved

PErhaps add a TODO to make this a proper accessibility warning on the Issues pane, with the red squiggly?

Joey Arhar

Done

Line 2097, Patchset 8: const Element& element) {
Mason Freed . unresolved

Is it possible to make this non-const, to avoid the const_cast below?

Joey Arhar

Removing const from this would require also removing const from OwnerSelectElement on a bunch of classes, which cascades into removing const from a lot of places, including HTMLOptGroupElement::SupportsFocus for example.

Adding const to the returned struct also causes issues when the returned node is used to call other things such as HTMLSelectElement::OptionInserted.

I think we should keep this const_cast, do you agree?

Line 2110, Patchset 8: return {nullptr, ancestor_optgroup, nullptr, nullptr};
Mason Freed . resolved

For all of these, I think it'd be a lot more readable (and easier to expand, as in this patch) to use designated initializers:

```
return {.optgroup = ancestor_optgroup};
```

Joey Arhar

Done

Line 2167, Patchset 8: auto insert_result = children_descendant_counts_map_.insert(
Mason Freed . resolved

I don't understand why we're inserting here? There are two paths (input and option) and both of them CHECK that the retrieved value is not what we're inserting here. Seems much more straightforward to just use `children_descendant_counts_map_.at()` to retrieve the value (or CHECK if it's not there) and then just CHECK that one of the two values is `>0`. Either that, or I'm missing something about how this works.

Joey Arhar

makes sense, i replaced the insert with a find

Line 2184, Patchset 8: // This has to be run after `insert_result` is destroyed or else a DCHECK
Mason Freed . resolved

The above comment *might* also fix this situation.

Joey Arhar

yes it does

Open in Gerrit

Related details

Attention is currently required from:
  • Mason Freed
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
Gerrit-Change-Number: 7793502
Gerrit-PatchSet: 9
Gerrit-Owner: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-Attention: Mason Freed <mas...@chromium.org>
Gerrit-Comment-Date: Fri, 08 May 2026 22:08:49 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Mason Freed <mas...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Joey Arhar (Gerrit)

unread,
May 8, 2026, 7:20:29 PM (4 days ago) May 8
to Code Review Nudger, Mason Freed, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org
Attention needed from Mason Freed

Joey Arhar added 1 comment

File third_party/blink/renderer/core/html/forms/html_select_element.h
Line 570, Patchset 8: // num_descendant_inputs_ is redundant with the sum of num_inputs in each
Mason Freed . unresolved

Perhaps it's worth a helper function that only DCHECKs that this var matches the sum of the map contents? (DCHECK because this will be slow.) Then sprinkle calls to this function in interesting places?

Joey Arhar

Done

Joey Arhar

i actually did this earlier, and now that ive re-added it, i remembered why i removed it - its so slow that it makes one of the select tests time out (the test already has <meta name="timeout" content="long">).

I moved it to the getter for the number of descendant inputs, hopefully that performs better and provides the same benefit.

Open in Gerrit

Related details

Attention is currently required from:
  • Mason Freed
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
Gerrit-Change-Number: 7793502
Gerrit-PatchSet: 10
Gerrit-Owner: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-Attention: Mason Freed <mas...@chromium.org>
Gerrit-Comment-Date: Fri, 08 May 2026 23:20:19 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Mason Freed <mas...@chromium.org>
Comment-In-Reply-To: Joey Arhar <jar...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Mason Freed (Gerrit)

unread,
May 11, 2026, 4:20:13 PM (21 hours ago) May 11
to Code Review Nudger, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org
Attention needed from Joey Arhar

Mason Freed voted and added 5 comments

Votes added by Mason Freed

Code-Review+1

5 comments

Patchset-level comments
File-level comment, Patchset 11 (Latest):
Mason Freed . resolved

LGTM, but do fix up the `.find` - replace with the `.at`.

File third_party/blink/renderer/core/html/forms/html_select_element.h
Line 570, Patchset 8: // num_descendant_inputs_ is redundant with the sum of num_inputs in each
Mason Freed . resolved

Perhaps it's worth a helper function that only DCHECKs that this var matches the sum of the map contents? (DCHECK because this will be slow.) Then sprinkle calls to this function in interesting places?

Joey Arhar

Done

Joey Arhar

i actually did this earlier, and now that ive re-added it, i remembered why i removed it - its so slow that it makes one of the select tests time out (the test already has <meta name="timeout" content="long">).

I moved it to the getter for the number of descendant inputs, hopefully that performs better and provides the same benefit.

Mason Freed

Wow! Ok, good reason not to do it I guess. Thanks for trying.

File third_party/blink/renderer/core/html/forms/html_select_element.cc
Line 2097, Patchset 8: const Element& element) {
Mason Freed . resolved

Is it possible to make this non-const, to avoid the const_cast below?

Joey Arhar

Removing const from this would require also removing const from OwnerSelectElement on a bunch of classes, which cascades into removing const from a lot of places, including HTMLOptGroupElement::SupportsFocus for example.

Adding const to the returned struct also causes issues when the returned node is used to call other things such as HTMLSelectElement::OptionInserted.

I think we should keep this const_cast, do you agree?

Mason Freed

I wish const weren't so viral. Yeah, given what you said, this is ok.

Line 2172, Patchset 11 (Latest): children_descendant_counts_map_.at(nearest_ancestor_select_child);
Mason Freed . unresolved

This is extra, and I think it can be used instead of the find, right?

Line 2192, Patchset 11 (Latest): unsigned num_inputs_in_map = 0;
Mason Freed . unresolved

Maybe just for future-safety, wrap this block in `{}` so we don't accidentally use any of it later.


BTW, this is good. This is where I was thinking about DCHECKing anyway.

Open in Gerrit

Related details

Attention is currently required from:
  • Joey Arhar
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
    Gerrit-Change-Number: 7793502
    Gerrit-PatchSet: 11
    Gerrit-Owner: Joey Arhar <jar...@chromium.org>
    Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
    Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
    Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
    Gerrit-Attention: Joey Arhar <jar...@chromium.org>
    Gerrit-Comment-Date: Mon, 11 May 2026 20:20:03 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Joey Arhar (Gerrit)

    unread,
    May 11, 2026, 6:08:54 PM (20 hours ago) May 11
    to Mason Freed, Code Review Nudger, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org

    Joey Arhar voted and added 2 comments

    Votes added by Joey Arhar

    Code-Review+1
    Commit-Queue+2

    2 comments

    File third_party/blink/renderer/core/html/forms/html_select_element.cc
    Line 2172, Patchset 11: children_descendant_counts_map_.at(nearest_ancestor_select_child);
    Mason Freed . resolved

    This is extra, and I think it can be used instead of the find, right?

    Joey Arhar

    Whoops, don't know how that got there and I removed it. I guess it would be functionally the same since at() will crash if the value isn't in the map and we are explicitly CHECKing the same thing...

    I like find() more because it allows for erasing the value at the end of the method without the need to give it a key type and do another lookup.

    Line 2192, Patchset 11: unsigned num_inputs_in_map = 0;
    Mason Freed . resolved

    Maybe just for future-safety, wrap this block in `{}` so we don't accidentally use any of it later.


    BTW, this is good. This is where I was thinking about DCHECKing anyway.

    Joey Arhar

    Done

    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
      Gerrit-Change-Number: 7793502
      Gerrit-PatchSet: 13
      Gerrit-Owner: Joey Arhar <jar...@chromium.org>
      Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
      Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
      Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
      Gerrit-Comment-Date: Mon, 11 May 2026 22:08:44 +0000
      satisfied_requirement
      open
      diffy

      Chromium LUCI CQ (Gerrit)

      unread,
      May 11, 2026, 10:04:44 PM (16 hours ago) May 11
      to Mason Freed, Code Review Nudger, android-bu...@system.gserviceaccount.com, blink-...@chromium.org, blink-rev...@chromium.org

      Chromium LUCI CQ submitted the change

      Change information

      Commit message:
      Track nested <input> in <select> for FilterableSelect

      Currently, the only way to use FilterableSelect was to create a
      sibling <input> and listbox <select> element and connect them with
      idrefs, like this:
      <input filter=select>
      <select id=select size=4>

      This patch starts the work to make it possible to nest an <input> inside
      a <select> to make filtering work like this:
      <select>
      <input>

      In order to dynamically switch the DOM of the shadowroot between the
      existing structure and the new structure outlined above, and to slot
      each child of the select element into the correct slot, this patch adds
      logic to the InsertedInto and RemovedFrom methods on HTMLOptionElement
      and HTMLInputElement to tell their ancestor select element which one of
      the select's children had a descendant option or input added to it.
      HTMLSelectElement now stores a map of each of its child nodes to the
      number of options and input element descendants (inclusive) of that
      child.
      Bug: 402429384, 453705243
      Change-Id: I551d47c4524605d2b053aee3eee393858357d224
      Reviewed-by: Joey Arhar <jar...@chromium.org>
      Commit-Queue: Joey Arhar <jar...@chromium.org>
      Reviewed-by: Mason Freed <mas...@chromium.org>
      Cr-Commit-Position: refs/heads/main@{#1628943}
      Files:
      • M third_party/blink/renderer/core/html/forms/html_input_element.cc
      • M third_party/blink/renderer/core/html/forms/html_input_element.h
      • M third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
      • M third_party/blink/renderer/core/html/forms/html_option_element.cc
      • M third_party/blink/renderer/core/html/forms/html_option_element.h
      • M third_party/blink/renderer/core/html/forms/html_select_element.cc
      • M third_party/blink/renderer/core/html/forms/html_select_element.h
      • M third_party/blink/renderer/core/html/forms/html_select_element_test.cc
      • M third_party/blink/renderer/core/html/forms/option_list.cc
      • M third_party/blink/renderer/core/html/html_hr_element.cc
      Change size: L
      Delta: 10 files changed, 291 insertions(+), 46 deletions(-)
      Branch: refs/heads/main
      Submit Requirements:
      • requirement satisfiedCode-Review: +1 by Mason Freed, +1 by Joey Arhar
      Open in Gerrit
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: merged
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I551d47c4524605d2b053aee3eee393858357d224
      Gerrit-Change-Number: 7793502
      Gerrit-PatchSet: 14
      Gerrit-Owner: Joey Arhar <jar...@chromium.org>
      Gerrit-Reviewer: Chromium LUCI CQ <chromiu...@luci-project-accounts.iam.gserviceaccount.com>
      Gerrit-Reviewer: Joey Arhar <jar...@chromium.org>
      Gerrit-Reviewer: Mason Freed <mas...@chromium.org>
      open
      diffy
      satisfied_requirement
      Reply all
      Reply to author
      Forward
      0 new messages