/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
SafeOptionListMutation::SafeOptionListMutation(nsIContent* aSelect,
nsIContent* aParent,
nsIContent* aKid,
uint32_t aIndex, bool aNotify)
: mSelect(HTMLSelectElement::FromNodeOrNull(aSelect)),
mTopLevelMutation(false),
mNeedsRebuild(false),
mNotify(aNotify) { if (mSelect) {
mInitialSelectedOption = mSelect->Item(mSelect->SelectedIndex());
mTopLevelMutation = !mSelect->mMutating; if (mTopLevelMutation) {
mSelect->mMutating = true;
} else { // This is very unfortunate, but to handle mutation events properly, // option list must be up-to-date before inserting or removing options. // Fortunately this is called only if mutation event listener // adds or removes options.
mSelect->RebuildOptionsArray(mNotify);
}
nsresult rv; if (aKid) {
rv = mSelect->WillAddOptions(aKid, aParent, aIndex, mNotify);
} else {
rv = mSelect->WillRemoveOptions(aParent, aIndex, mNotify);
}
mNeedsRebuild = NS_FAILED(rv);
}
}
SafeOptionListMutation::~SafeOptionListMutation() { if (mSelect) { if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
mSelect->RebuildOptionsArray(true);
} if (mTopLevelMutation) {
mSelect->mMutating = false;
} if (mSelect->Item(mSelect->SelectedIndex()) != mInitialSelectedOption) { // We must have triggered the SelectSomething() codepath, which can cause // our validity to change. Unfortunately, our attempt to update validity // in that case may not have worked correctly, because we actually call it // before we have inserted the new <option>s into the DOM! Go ahead and // update validity here as needed, because by now we know our <option>s // are where they should be.
mSelect->UpdateValueMissingValidityState();
mSelect->UpdateValidityElementStates(mNotify);
} #ifdef DEBUG
mSelect->VerifyOptionsArray(); #endif
}
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-showpicker void HTMLSelectElement::ShowPicker(ErrorResult& aRv) { // Step 1. If this is not mutable, then throw an "InvalidStateError" // DOMException. if (IsDisabled()) { return aRv.ThrowInvalidStateError("This select is disabled.");
}
// Step 2. If this's relevant settings object's origin is not same origin with // this's relevant settings object's top-level origin, and this is a select // element, [...], then throw a "SecurityError" DOMException.
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
WindowGlobalChild* windowGlobalChild =
window ? window->GetWindowGlobalChild() : nullptr; if (!windowGlobalChild || !windowGlobalChild->SameOriginWithTop()) { return aRv.ThrowSecurityError( "Call was blocked because the current origin isn't same-origin with " "top.");
}
// Step 3. If this's relevant global object does not have transient // activation, then throw a "NotAllowedError" DOMException. if (!OwnerDoc()->HasValidTransientUserGestureActivation()) { return aRv.ThrowNotAllowedError( "Call was blocked due to lack of user activation.");
}
// Step 4. If this is a select element, and this is not being rendered, then // throw a "NotSupportedError" DOMException.
// Flush frames so that IsRendered returns up-to-date results.
Unused << GetPrimaryFrame(FlushType::Frames); if (!IsRendered()) { return aRv.ThrowNotSupportedError("This select isn't being rendered.");
}
// Step 3. Consume user activation given element's relevant global object.
OwnerDoc()->ConsumeTransientUserGestureActivation();
// Step 5. Otherwise, the user agent should show any relevant user interface // for selecting a value for element, in the way it normally would when the // user interacts with the control. #if !defined(ANDROID) if (!IsCombobox()) { return;
} #endif
HTMLOptionElement* optElement = HTMLOptionElement::FromNode(aOptions); if (optElement) {
mOptions->InsertOptionAt(optElement, insertIndex);
insertIndex++;
} elseif (aDepth == 0) { // If it's at the top level, then we just found out there are non-options // at the top level, which will throw off the insert count
mNonOptionChildren++;
// Deal with optgroups if (aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
mOptGroupCount++;
for (nsIContent* child = aOptions->GetFirstChild(); child;
child = child->GetNextSibling()) {
optElement = HTMLOptionElement::FromNode(child); if (optElement) {
mOptions->InsertOptionAt(optElement, insertIndex);
insertIndex++;
}
}
}
} // else ignore even if optgroup; we want to ignore nested optgroups.
// Deal with the selected list if (insertIndex - aListIndex) { // Fix the currently selected index if (aListIndex <= mSelectedIndex) {
mSelectedIndex += (insertIndex - aListIndex);
OnSelectionChanged();
}
// Get the frame stuff for notification. No need to flush here // since if there's no frame for the select yet the select will // get into the right state once it's created.
nsISelectControlFrame* selectFrame = nullptr;
AutoWeakFrame weakSelectFrame; bool didGetFrame = false;
// Actually select the options if the added options warrant it for (int32_t i = aListIndex; i < insertIndex; i++) { // Notify the frame that the option is added if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
selectFrame = GetSelectFrame();
weakSelectFrame = do_QueryFrame(selectFrame);
didGetFrame = true;
}
if (selectFrame) {
selectFrame->AddOption(i);
}
RefPtr<HTMLOptionElement> option = Item(i); if (option && option->Selected()) { // Clear all other options if (!HasAttr(nsGkAtoms::multiple)) {
OptionFlags mask{OptionFlag::IsSelected, OptionFlag::ClearAll,
OptionFlag::SetDisabled, OptionFlag::Notify,
OptionFlag::InsertingOptions};
SetOptionsSelectedByIndex(i, i, mask);
}
// This is sort of a hack ... we need to notify that the option was // set and change selectedIndex even though we didn't really change // its value.
OnOptionSelected(selectFrame, i, true, false, aNotify);
}
}
HTMLOptionElement* optElement = HTMLOptionElement::FromNode(aOptions); if (optElement) { if (mOptions->ItemAsOption(aListIndex) != optElement) {
NS_ERROR("wrong option at index"); return NS_ERROR_UNEXPECTED;
}
mOptions->RemoveOptionAt(aListIndex);
numRemoved++;
} elseif (aDepth == 0) { // Yay, one less artifact at the top level.
mNonOptionChildren--;
// Recurse down deeper for options if (mOptGroupCount && aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
mOptGroupCount--;
for (nsIContent* child = aOptions->GetFirstChild(); child;
child = child->GetNextSibling()) {
optElement = HTMLOptionElement::FromNode(child); if (optElement) { if (mOptions->ItemAsOption(aListIndex) != optElement) {
NS_ERROR("wrong option at index"); return NS_ERROR_UNEXPECTED;
}
mOptions->RemoveOptionAt(aListIndex);
numRemoved++;
}
}
}
} // else don't check for an optgroup; we want to ignore nested optgroups
if (numRemoved) { // Tell the widget we removed the options
nsISelectControlFrame* selectFrame = GetSelectFrame(); if (selectFrame) {
nsAutoScriptBlocker scriptBlocker; for (int32_t i = aListIndex; i < aListIndex + numRemoved; ++i) {
selectFrame->RemoveOption(i);
}
}
// Fix the selected index if (aListIndex <= mSelectedIndex) { if (mSelectedIndex < (aListIndex + numRemoved)) { // aListIndex <= mSelectedIndex < aListIndex+numRemoved // Find a new selected index if it was one of the ones removed. // If this is a Combobox, no other Item will be selected. if (IsCombobox()) {
mSelectedIndex = -1;
OnSelectionChanged();
} else {
FindSelectedIndex(aListIndex, aNotify);
}
} else { // Shift the selected index if something in front of it was removed // aListIndex+numRemoved <= mSelectedIndex
mSelectedIndex -= numRemoved;
OnSelectionChanged();
}
}
// Select something in case we removed the selected option on a // single select if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) { // Update the validity state in case of we've just removed the last // option.
UpdateValueMissingValidityState();
UpdateValidityElementStates(aNotify);
}
}
return NS_OK;
}
// XXXldb Doing the processing before the content nodes have been added // to the document (as the name of this function seems to require, and // as the callers do), is highly unusual. Passing around unparented // content to other parts of the app can make those things think the // options are the root content node.
NS_IMETHODIMP
HTMLSelectElement::WillAddOptions(nsIContent* aOptions, nsIContent* aParent,
int32_t aContentIndex, bool aNotify) { if (this != aParent && this != aParent->GetParent()) { return NS_OK;
}
int32_t level = aParent == this ? 0 : 1;
// Get the index where the options will be inserted
int32_t ind = -1; if (!mNonOptionChildren) { // If there are no artifacts, aContentIndex == ind
ind = aContentIndex;
} else { // If there are artifacts, we have to get the index of the option the // hard way
int32_t children = aParent->GetChildCount();
if (aContentIndex >= children) { // If the content insert is after the end of the parent, then we want to // get the next index *after* the parent and insert there.
ind = GetOptionIndexAfter(aParent);
} else { // If the content insert is somewhere in the middle of the container, then // we want to get the option currently at the index and insert in front of // that.
nsIContent* currentKid = aParent->GetChildAt_Deprecated(aContentIndex);
NS_ASSERTION(currentKid, "Child not found!"); if (currentKid) {
ind = GetOptionIndexAt(currentKid);
} else {
ind = -1;
}
}
}
NS_IMETHODIMP
HTMLSelectElement::WillRemoveOptions(nsIContent* aParent, int32_t aContentIndex, bool aNotify) { if (this != aParent && this != aParent->GetParent()) { return NS_OK;
}
int32_t level = this == aParent ? 0 : 1;
// Get the index where the options will be removed
nsIContent* currentKid = aParent->GetChildAt_Deprecated(aContentIndex); if (currentKid) {
int32_t ind; if (!mNonOptionChildren) { // If there are no artifacts, aContentIndex == ind
ind = aContentIndex;
} else { // If there are artifacts, we have to get the index of the option the // hard way
ind = GetFirstOptionIndex(currentKid);
} if (ind != -1) {
nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
int32_t HTMLSelectElement::GetOptionIndexAt(nsIContent* aOptions) { // Search this node and below. // If not found, find the first one *after* this node.
int32_t retval = GetFirstOptionIndex(aOptions); if (retval == -1) {
retval = GetOptionIndexAfter(aOptions);
}
return retval;
}
int32_t HTMLSelectElement::GetOptionIndexAfter(nsIContent* aOptions) { // - If this is the select, the next option is the last. // - If not, search all the options after aOptions and up to the last option // in the parent. // - If it's not there, search for the first option after the parent. if (aOptions == this) { return Length();
}
// Just in case we're not the parent, get the parent of the reference // element
nsCOMPtr<nsINode> parent = aBefore->Element::GetParentNode(); if (!parent || !parent->IsInclusiveDescendantOf(this)) { // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT // element.
aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); return;
}
// If the before parameter is not null, we are equivalent to the // insertBefore method on the parent of before.
nsCOMPtr<nsINode> refNode = aBefore;
parent->InsertBefore(aElement, refNode, aError);
}
if (aChangeOptionState) { // Tell the option to get its bad self selected
RefPtr<HTMLOptionElement> option = Item(static_cast<uint32_t>(aIndex)); if (option) {
option->SetSelectedInternal(aSelected, aNotify);
}
}
// Let the frame know too if (aSelectFrame) {
aSelectFrame->OnOptionSelected(aIndex, aSelected);
}
void HTMLSelectElement::FindSelectedIndex(int32_t aStartIndex, bool aNotify) {
mSelectedIndex = -1;
uint32_t len = Length(); for (int32_t i = aStartIndex; i < int32_t(len); i++) { if (IsOptionSelectedByIndex(i)) {
mSelectedIndex = i; break;
}
}
OnSelectionChanged();
}
// XXX Consider splitting this into two functions for ease of reading: // SelectOptionsByIndex(startIndex, endIndex, clearAll, checkDisabled) // startIndex, endIndex - the range of options to turn on // (-1, -1) will clear all indices no matter what. // clearAll - will clear all other options unless checkDisabled is on // and all the options attempted to be set are disabled // (note that if it is not multiple, and an option is selected, // everything else will be cleared regardless). // checkDisabled - if this is TRUE, and an option is disabled, it will not be // changed regardless of whether it is selected or not. // Generally the UI passes TRUE and JS passes FALSE. // (setDisabled currently is the opposite) // DeselectOptionsByIndex(startIndex, endIndex, checkDisabled) // startIndex, endIndex - the range of options to turn on // (-1, -1) will clear all indices no matter what. // checkDisabled - if this is TRUE, and an option is disabled, it will not be // changed regardless of whether it is selected or not. // Generally the UI passes TRUE and JS passes FALSE. // (setDisabled currently is the opposite) // // XXXbz the above comment is pretty confusing. Maybe we should actually // document the args to this function too, in addition to documenting what // things might end up looking like? In particular, pay attention to the // setDisabled vs checkDisabled business. bool HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex,
int32_t aEndIndex,
OptionFlags aOptionsMask) { #if 0
printf("SetOption(%d-%d, %c, ClearAll=%c)\n", aStartIndex, aEndIndex,
(aOptionsMask.contains(OptionFlag::IsSelected) ? 'Y' : 'N'),
(aOptionsMask.contains(OptionFlag::ClearAll) ? 'Y' : 'N')); #endif // Don't bother if the select is disabled if (!aOptionsMask.contains(OptionFlag::SetDisabled) && IsDisabled()) { returnfalse;
}
// Don't bother if there are no options
uint32_t numItems = Length(); if (numItems == 0) { returnfalse;
}
// First, find out whether multiple items can be selected bool isMultiple = Multiple();
// These variables tell us whether any options were selected // or deselected. bool optionsSelected = false; bool optionsDeselected = false;
if (aOptionsMask.contains(OptionFlag::IsSelected)) { // Setting selectedIndex to an out-of-bounds index means -1. (HTML5) if (aStartIndex < 0 || AssertedCast<uint32_t>(aStartIndex) >= numItems ||
aEndIndex < 0 || AssertedCast<uint32_t>(aEndIndex) >= numItems) {
aStartIndex = -1;
aEndIndex = -1;
}
// Only select the first value if it's not multiple if (!isMultiple) {
aEndIndex = aStartIndex;
}
// This variable tells whether or not all of the options we attempted to // select are disabled. If ClearAll is passed in as true, and we do not // select anything because the options are disabled, we will not clear the // other options. (This is to make the UI work the way one might expect.) bool allDisabled = !aOptionsMask.contains(OptionFlag::SetDisabled);
// // Save a little time when clearing other options //
int32_t previousSelectedIndex = mSelectedIndex;
// // Select the requested indices // // If index is -1, everything will be deselected (bug 28143) if (aStartIndex != -1) {
MOZ_ASSERT(aStartIndex >= 0);
MOZ_ASSERT(aEndIndex >= 0); // Loop through the options and select them (if they are not disabled and // if they are not already selected). for (uint32_t optIndex = AssertedCast<uint32_t>(aStartIndex);
optIndex <= AssertedCast<uint32_t>(aEndIndex); optIndex++) {
RefPtr<HTMLOptionElement> option = Item(optIndex);
// Ignore disabled options. if (!aOptionsMask.contains(OptionFlag::SetDisabled)) { if (option && IsOptionDisabled(option)) { continue;
}
allDisabled = false;
}
// If the index is already selected, ignore it. On the other hand when // the option has just been inserted we have to get in sync with it. if (option && (aOptionsMask.contains(OptionFlag::InsertingOptions) ||
!option->Selected())) { // To notify the frame if anything gets changed. No need // to flush here, if there's no frame yet we don't need to // force it to be created just to notify it about a change // in the select.
selectFrame = GetSelectFrame();
weakSelectFrame = do_QueryFrame(selectFrame);
didGetFrame = true;
// Next remove all other options if single select or all is clear // If index is -1, everything will be deselected (bug 28143) if (((!isMultiple && optionsSelected) ||
(aOptionsMask.contains(OptionFlag::ClearAll) && !allDisabled) ||
aStartIndex == -1) &&
previousSelectedIndex != -1) { for (uint32_t optIndex = AssertedCast<uint32_t>(previousSelectedIndex);
optIndex < numItems; optIndex++) { if (static_cast<int32_t>(optIndex) < aStartIndex || static_cast<int32_t>(optIndex) > aEndIndex) {
HTMLOptionElement* option = Item(optIndex); // If the index is already deselected, ignore it. if (option && option->Selected()) { if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) { // To notify the frame if anything gets changed, don't // flush, if the frame doesn't exist we don't need to // create it just to tell it about this change.
selectFrame = GetSelectFrame();
weakSelectFrame = do_QueryFrame(selectFrame);
// Only need to deselect one option if not multiple if (!isMultiple) { break;
}
}
}
}
}
} else { // If we're deselecting, loop through all selected items and deselect // any that are in the specified range. for (int32_t optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
HTMLOptionElement* option = Item(optIndex); if (!aOptionsMask.contains(OptionFlag::SetDisabled) &&
IsOptionDisabled(option)) { continue;
}
// If the index is already selected, ignore it. if (option && option->Selected()) { if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) { // To notify the frame if anything gets changed, don't // flush, if the frame doesn't exist we don't need to // create it just to tell it about this change.
selectFrame = GetSelectFrame();
weakSelectFrame = do_QueryFrame(selectFrame);
// Make sure something is selected unless we were set to -1 (none) if (optionsDeselected && aStartIndex != -1 &&
!aOptionsMask.contains(OptionFlag::NoReselect)) {
optionsSelected =
CheckSelectSomething(aOptionsMask.contains(OptionFlag::Notify)) ||
optionsSelected;
}
// Let the caller know whether anything was changed return optionsSelected || optionsDeselected;
}
// Check for disabled optgroups // If there are no artifacts, there are no optgroups if (mNonOptionChildren) { for (nsCOMPtr<Element> node = static_cast<nsINode*>(aOption)->GetParentElement();
node; node = node->GetParentElement()) { // If we reached the select element, we're done if (node->IsHTMLElement(nsGkAtoms::select)) { returnfalse;
}
if (!optGroupElement) { // If you put something else between you and the optgroup, you're a // moron and you deserve not to have optgroup disabling work. returnfalse;
}
if (optGroupElement->Disabled()) { returntrue;
}
}
}
bool HTMLSelectElement::CheckSelectSomething(bool aNotify) { if (mIsDoneAddingChildren) { if (mSelectedIndex < 0 && IsCombobox()) { return SelectSomething(aNotify);
}
} returnfalse;
}
bool HTMLSelectElement::SelectSomething(bool aNotify) { // If we're not done building the select, don't play with this yet. if (!mIsDoneAddingChildren) { returnfalse;
}
uint32_t count = Length(); for (uint32_t i = 0; i < count; i++) { bool disabled;
nsresult rv = IsOptionDisabled(i, &disabled);
if (NS_FAILED(rv) || !disabled) {
SetSelectedIndexInternal(i, aNotify);
// If there is a disabled fieldset in the parent chain, the element is now // barred from constraint validation. // XXXbz is this still needed now that fieldset changes always call // FieldSetDisabledChanged?
UpdateBarredFromConstraintValidation();
// And now make sure our state is up to date
UpdateValidityElementStates(false);
// We might be no longer disabled because our parent chain changed. // XXXbz is this still needed now that fieldset changes always call // FieldSetDisabledChanged?
UpdateBarredFromConstraintValidation();
// And now make sure our state is up to date
UpdateValidityElementStates(false);
}
void HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::disabled) { if (aNotify) {
mDisabledChanged = true;
}
} elseif (aName == nsGkAtoms::multiple) { if (!aValue && aNotify && mSelectedIndex >= 0) { // We're changing from being a multi-select to a single-select. // Make sure we only have one option selected before we do that. // Note that this needs to come before we really unset the attr, // since SetOptionsSelectedByIndex does some bail-out type // optimization for cases when the select is not multiple that // would lead to only a single option getting deselected.
SetSelectedIndexInternal(mSelectedIndex, aNotify);
}
}
}
void HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue,
nsIPrincipal* aSubjectPrincipal, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::disabled) { // This *has* to be called *before* validity state check because // UpdateBarredFromConstraintValidation and // UpdateValueMissingValidityState depend on our disabled state.
UpdateDisabledState(aNotify);
UpdateValueMissingValidityState();
UpdateBarredFromConstraintValidation();
UpdateValidityElementStates(aNotify);
} elseif (aName == nsGkAtoms::required) { // This *has* to be called *before* UpdateValueMissingValidityState // because UpdateValueMissingValidityState depends on our required // state.
UpdateRequiredState(!!aValue, aNotify);
UpdateValueMissingValidityState();
UpdateValidityElementStates(aNotify);
} elseif (aName == nsGkAtoms::autocomplete) { // Clear the cached @autocomplete attribute and autocompleteInfo state.
mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
} elseif (aName == nsGkAtoms::multiple) { if (!aValue && aNotify) { // We might have become a combobox; make sure _something_ gets // selected in that case
CheckSelectSomething(aNotify);
}
}
}
// If we foolishly tried to restore before we were done adding // content, restore the rest of the options proper-like if (mRestoreState) {
RestoreStateTo(*mRestoreState);
mRestoreState = nullptr;
}
// Notify the frame if (selectFrame) {
selectFrame->DoneAddingChildren(true);
}
if (!mInhibitStateRestoration) {
GenerateStateKey();
RestoreFormControlState();
}
// Now that we're done, select something (if it's a single select something // must be selected) if (!CheckSelectSomething(false)) { // If an option has @selected set, it will be selected during parsing but // with an empty value. We have to make sure the select element updates it's // validity state to take this into account.
UpdateValueMissingValidityState();
// And now make sure we update our content state too
UpdateValidityElementStates(aHaveNotified);
}
ElementState state; if (IsValid()) {
state |= ElementState::VALID; if (mUserInteracted) {
state |= ElementState::USER_VALID;
}
} else {
state |= ElementState::INVALID; if (mUserInteracted) {
state |= ElementState::USER_INVALID;
}
}
if (mDisabledChanged) { // We do not want to save the real disabled state but the disabled // attribute.
presState->disabled() = HasAttr(nsGkAtoms::disabled);
presState->disabledSet() = true;
}
}
bool HTMLSelectElement::RestoreState(PresState* aState) { // Get the presentation state object to retrieve our stuff out of. const PresContentData& state = aState->contentData(); if (state.type() == PresContentData::TSelectContentData) {
RestoreStateTo(state.get_SelectContentData());
// Don't flush, if the frame doesn't exist yet it doesn't care if // we're reset or not.
DispatchContentReset();
}
if (aState->disabledSet() && !aState->disabled()) {
SetDisabled(false, IgnoreErrors());
}
returnfalse;
}
void HTMLSelectElement::RestoreStateTo(const SelectContentData& aNewSelected) { if (!mIsDoneAddingChildren) { // Make a copy of the state for us to restore from in the future.
mRestoreState = MakeUnique<SelectContentData>(aNewSelected); return;
}
uint32_t len = Length();
OptionFlags mask{OptionFlag::IsSelected, OptionFlag::ClearAll,
OptionFlag::SetDisabled, OptionFlag::Notify};
// First clear all
SetOptionsSelectedByIndex(-1, -1, mask);
// Select by index. for (uint32_t idx : aNewSelected.indices()) { if (idx < len) {
SetOptionsSelectedByIndex(idx, idx,
{OptionFlag::IsSelected,
OptionFlag::SetDisabled, OptionFlag::Notify});
}
}
// Select by value. for (uint32_t i = 0; i < len; ++i) {
HTMLOptionElement* option = Item(i); if (option) {
nsAutoString value;
option->GetValue(value); if (aNewSelected.values().Contains(value)) {
SetOptionsSelectedByIndex(
i, i,
{OptionFlag::IsSelected, OptionFlag::SetDisabled,
OptionFlag::Notify});
}
}
}
}
SetOptionsSelectedByIndex(i, i, mask);
option->SetSelectedChanged(false);
}
}
// // If nothing was selected and it's not multiple, select something // if (numSelected == 0 && IsCombobox()) {
SelectSomething(true);
}
OnSelectionChanged();
SetUserInteracted(false);
// Let the frame know we were reset // // Don't flush, if there's no frame yet it won't care about us being // reset even if we forced it to be created now. //
DispatchContentReset();
return NS_OK;
}
NS_IMETHODIMP
HTMLSelectElement::SubmitNamesValues(FormData* aFormData) { // // Get the name (if no name, no submit) //
nsAutoString name;
GetAttr(nsGkAtoms::name, name); if (name.IsEmpty()) { return NS_OK;
}
bool HTMLSelectElement::IsValueMissing() const { if (!Required()) { returnfalse;
}
uint32_t length = Length();
for (uint32_t i = 0; i < length; ++i) {
RefPtr<HTMLOptionElement> option = Item(i); // Check for a placeholder label option, don't count it as a valid value. if (i == 0 && !Multiple() && Size() <= 1 && option->GetParent() == this) {
nsAutoString value;
option->GetValue(value); if (value.IsEmpty()) { continue;
}
}
void HTMLSelectElement::FieldSetDisabledChanged(bool aNotify) { // This *has* to be called before UpdateBarredFromConstraintValidation and // UpdateValueMissingValidityState because these two functions depend on our // disabled state.
nsGenericHTMLFormControlElementWithState::FieldSetDisabledChanged(aNotify);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.