Bug 1837540 - Make radio button group navigation follow tree-order. r=sefeng,smaug

Differential Revision: https://phabricator.services.mozilla.com/D183426
This commit is contained in:
avandolder
2023-08-03 01:52:19 +00:00
parent b88e92110c
commit 8898fd2eec
10 changed files with 201 additions and 112 deletions

View File

@@ -1086,44 +1086,6 @@ Element* HTMLFormElement::IndexedGetter(uint32_t aIndex, bool& aFound) {
return element;
}
/**
* Compares the position of aControl1 and aControl2 in the document
* @param aControl1 First control to compare.
* @param aControl2 Second control to compare.
* @param aForm Parent form of the controls.
* @return < 0 if aControl1 is before aControl2,
* > 0 if aControl1 is after aControl2,
* 0 otherwise
*/
/* static */
int32_t HTMLFormElement::CompareFormControlPosition(Element* aElement1,
Element* aElement2,
const nsIContent* aForm) {
NS_ASSERTION(aElement1 != aElement2, "Comparing a form control to itself");
// If an element has a @form, we can assume it *might* be able to not have
// a parent and still be in the form.
NS_ASSERTION(
(aElement1->HasAttr(nsGkAtoms::form) || aElement1->GetParent()) &&
(aElement2->HasAttr(nsGkAtoms::form) || aElement2->GetParent()),
"Form controls should always have parents");
// If we pass aForm, we are assuming both controls are form descendants which
// is not always the case. This function should work but maybe slower.
// However, checking if both elements are form descendants may be slow too...
// TODO: remove the prevent asserts fix, see bug 598468.
#ifdef DEBUG
nsLayoutUtils::gPreventAssertInCompareTreePosition = true;
int32_t rVal =
nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
return rVal;
#else // DEBUG
return nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
#endif // DEBUG
}
#ifdef DEBUG
/**
* Checks that all form elements are in document order. Asserts if any pair of
@@ -1206,58 +1168,6 @@ void HTMLFormElement::PostPossibleUsernameEvent() {
mFormPossibleUsernameEventDispatcher->PostDOMEvent();
}
namespace {
struct FormComparator {
Element* const mChild;
HTMLFormElement* const mForm;
FormComparator(Element* aChild, HTMLFormElement* aForm)
: mChild(aChild), mForm(aForm) {}
int operator()(Element* aElement) const {
return HTMLFormElement::CompareFormControlPosition(mChild, aElement, mForm);
}
};
} // namespace
// This function return true if the element, once appended, is the last one in
// the array.
template <typename ElementType>
static bool AddElementToList(nsTArray<ElementType*>& aList, ElementType* aChild,
HTMLFormElement* aForm) {
NS_ASSERTION(aList.IndexOf(aChild) == aList.NoIndex,
"aChild already in aList");
const uint32_t count = aList.Length();
ElementType* element;
bool lastElement = false;
// Optimize most common case where we insert at the end.
int32_t position = -1;
if (count > 0) {
element = aList[count - 1];
position =
HTMLFormElement::CompareFormControlPosition(aChild, element, aForm);
}
// If this item comes after the last element, or the elements array is
// empty, we append to the end. Otherwise, we do a binary search to
// determine where the element should go.
if (position >= 0 || count == 0) {
// WEAK - don't addref
aList.AppendElement(aChild);
lastElement = true;
} else {
size_t idx;
BinarySearchIf(aList, 0, count, FormComparator(aChild, aForm), &idx);
// WEAK - don't addref
aList.InsertElementAt(idx, aChild);
}
return lastElement;
}
nsresult HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
bool aUpdateValidity, bool aNotify) {
// If an element has a @form, we can assume it *might* be able to not have
@@ -1272,7 +1182,8 @@ nsresult HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
nsTArray<nsGenericHTMLFormElement*>& controlList =
childInElements ? mControls->mElements : mControls->mNotInElements;
bool lastElement = AddElementToList(controlList, aChild, this);
bool lastElement =
nsContentUtils::AddElementToListByTreeOrder(controlList, aChild, this);
#ifdef DEBUG
AssertDocumentOrder(controlList, this);
@@ -1308,16 +1219,16 @@ nsresult HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
// what's in the slot or the child is earlier than the default submit.
nsGenericHTMLFormElement* oldDefaultSubmit = mDefaultSubmitElement;
if (!*firstSubmitSlot ||
(!lastElement &&
CompareFormControlPosition(aChild, *firstSubmitSlot, this) < 0)) {
(!lastElement && nsContentUtils::CompareTreePosition(
aChild, *firstSubmitSlot, this) < 0)) {
// Update mDefaultSubmitElement if it's currently in a valid state.
// Valid state means either non-null or null because there are in fact
// no submit elements around.
if ((mDefaultSubmitElement ||
(!mFirstSubmitInElements && !mFirstSubmitNotInElements)) &&
(*firstSubmitSlot == mDefaultSubmitElement ||
CompareFormControlPosition(aChild, mDefaultSubmitElement, this) <
0)) {
nsContentUtils::CompareTreePosition(aChild, mDefaultSubmitElement,
this) < 0)) {
mDefaultSubmitElement = aChild;
}
*firstSubmitSlot = aChild;
@@ -1448,8 +1359,8 @@ void HTMLFormElement::HandleDefaultSubmitRemoval() {
"How did that happen?");
// Have both; use the earlier one
mDefaultSubmitElement =
CompareFormControlPosition(mFirstSubmitInElements,
mFirstSubmitNotInElements, this) < 0
nsContentUtils::CompareTreePosition(mFirstSubmitInElements,
mFirstSubmitNotInElements, this) < 0
? mFirstSubmitInElements
: mFirstSubmitNotInElements;
}
@@ -1797,8 +1708,8 @@ bool HTMLFormElement::IsDefaultSubmitElement(
// We have both kinds of submits. Check which comes first.
nsGenericHTMLFormElement* defaultSubmit =
CompareFormControlPosition(mFirstSubmitInElements,
mFirstSubmitNotInElements, this) < 0
nsContentUtils::CompareTreePosition(mFirstSubmitInElements,
mFirstSubmitNotInElements, this) < 0
? mFirstSubmitInElements
: mFirstSubmitNotInElements;
return aElement == defaultSubmit;
@@ -2011,7 +1922,7 @@ HTMLFormElement::WalkRadioGroup(const nsAString& aName,
void HTMLFormElement::AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio) {
RadioGroupManager::AddToRadioGroup(aName, aRadio);
RadioGroupManager::AddToRadioGroup(aName, aRadio, this);
}
void HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
@@ -2167,7 +2078,7 @@ nsresult HTMLFormElement::AddElementToTableInternal(
}
nsresult HTMLFormElement::AddImageElement(HTMLImageElement* aChild) {
AddElementToList(mImageElements, aChild, this);
nsContentUtils::AddElementToListByTreeOrder(mImageElements, aChild, this);
return NS_OK;
}