Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/HTMLSelectElement.h"
8 :
9 : #include "mozAutoDocUpdate.h"
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/BasicEvents.h"
12 : #include "mozilla/EventDispatcher.h"
13 : #include "mozilla/EventStates.h"
14 : #include "mozilla/dom/Element.h"
15 : #include "mozilla/dom/HTMLFormSubmission.h"
16 : #include "mozilla/dom/HTMLOptGroupElement.h"
17 : #include "mozilla/dom/HTMLOptionElement.h"
18 : #include "mozilla/dom/HTMLSelectElementBinding.h"
19 : #include "mozilla/dom/UnionTypes.h"
20 : #include "mozilla/GenericSpecifiedValuesInlines.h"
21 : #include "nsContentCreatorFunctions.h"
22 : #include "nsContentList.h"
23 : #include "nsError.h"
24 : #include "nsGkAtoms.h"
25 : #include "nsIComboboxControlFrame.h"
26 : #include "nsIDocument.h"
27 : #include "nsIFormControlFrame.h"
28 : #include "nsIForm.h"
29 : #include "nsIFormProcessor.h"
30 : #include "nsIFrame.h"
31 : #include "nsIListControlFrame.h"
32 : #include "nsISelectControlFrame.h"
33 : #include "nsLayoutUtils.h"
34 : #include "nsMappedAttributes.h"
35 : #include "nsPresState.h"
36 : #include "nsServiceManagerUtils.h"
37 : #include "nsStyleConsts.h"
38 : #include "nsTextNode.h"
39 :
40 0 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Select)
41 :
42 : namespace mozilla {
43 : namespace dom {
44 :
45 0 : NS_IMPL_ISUPPORTS(SelectState, SelectState)
46 :
47 : //----------------------------------------------------------------------
48 : //
49 : // SafeOptionListMutation
50 : //
51 :
52 0 : SafeOptionListMutation::SafeOptionListMutation(nsIContent* aSelect,
53 : nsIContent* aParent,
54 : nsIContent* aKid,
55 : uint32_t aIndex,
56 0 : bool aNotify)
57 : : mSelect(HTMLSelectElement::FromContentOrNull(aSelect))
58 : , mTopLevelMutation(false)
59 : , mNeedsRebuild(false)
60 : , mNotify(aNotify)
61 0 : , mInitialSelectedIndex(-1)
62 : {
63 0 : if (mSelect) {
64 0 : mInitialSelectedIndex = mSelect->SelectedIndex();
65 0 : mTopLevelMutation = !mSelect->mMutating;
66 0 : if (mTopLevelMutation) {
67 0 : mSelect->mMutating = true;
68 : } else {
69 : // This is very unfortunate, but to handle mutation events properly,
70 : // option list must be up-to-date before inserting or removing options.
71 : // Fortunately this is called only if mutation event listener
72 : // adds or removes options.
73 0 : mSelect->RebuildOptionsArray(mNotify);
74 : }
75 : nsresult rv;
76 0 : if (aKid) {
77 0 : rv = mSelect->WillAddOptions(aKid, aParent, aIndex, mNotify);
78 : } else {
79 0 : rv = mSelect->WillRemoveOptions(aParent, aIndex, mNotify);
80 : }
81 0 : mNeedsRebuild = NS_FAILED(rv);
82 : }
83 0 : }
84 :
85 0 : SafeOptionListMutation::~SafeOptionListMutation()
86 : {
87 0 : if (mSelect) {
88 0 : if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
89 0 : mSelect->RebuildOptionsArray(true);
90 : }
91 0 : if (mTopLevelMutation) {
92 0 : mSelect->mMutating = false;
93 : }
94 0 : if (mSelect->SelectedIndex() != mInitialSelectedIndex) {
95 : // We must have triggered the SelectSomething() codepath, which can cause
96 : // our validity to change. Unfortunately, our attempt to update validity
97 : // in that case may not have worked correctly, because we actually call it
98 : // before we have inserted the new <option>s into the DOM! Go ahead and
99 : // update validity here as needed, because by now we know our <option>s
100 : // are where they should be.
101 0 : mSelect->UpdateValueMissingValidityState();
102 0 : mSelect->UpdateState(mNotify);
103 : }
104 : #ifdef DEBUG
105 0 : mSelect->VerifyOptionsArray();
106 : #endif
107 : }
108 0 : }
109 :
110 : //----------------------------------------------------------------------
111 : //
112 : // HTMLSelectElement
113 : //
114 :
115 : // construction, destruction
116 :
117 :
118 0 : HTMLSelectElement::HTMLSelectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
119 0 : FromParser aFromParser)
120 : : nsGenericHTMLFormElementWithState(aNodeInfo, NS_FORM_SELECT),
121 0 : mOptions(new HTMLOptionsCollection(this)),
122 : mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
123 : mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
124 0 : mIsDoneAddingChildren(!aFromParser),
125 : mDisabledChanged(false),
126 : mMutating(false),
127 0 : mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
128 : mSelectionHasChanged(false),
129 : mDefaultSelectionSet(false),
130 : mCanShowInvalidUI(true),
131 : mCanShowValidUI(true),
132 : mNonOptionChildren(0),
133 : mOptGroupCount(0),
134 0 : mSelectedIndex(-1)
135 : {
136 0 : SetHasWeirdParserInsertionMode();
137 :
138 : // DoneAddingChildren() will be called later if it's from the parser,
139 : // otherwise it is
140 :
141 : // Set up our default state: enabled, optional, and valid.
142 0 : AddStatesSilently(NS_EVENT_STATE_ENABLED |
143 0 : NS_EVENT_STATE_OPTIONAL |
144 0 : NS_EVENT_STATE_VALID);
145 0 : }
146 :
147 0 : HTMLSelectElement::~HTMLSelectElement()
148 : {
149 0 : mOptions->DropReference();
150 0 : }
151 :
152 : // ISupports
153 :
154 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLSelectElement)
155 :
156 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSelectElement,
157 : nsGenericHTMLFormElementWithState)
158 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
159 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOptions)
160 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedOptions)
161 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
162 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLSelectElement,
163 : nsGenericHTMLFormElementWithState)
164 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
165 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedOptions)
166 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
167 :
168 0 : NS_IMPL_ADDREF_INHERITED(HTMLSelectElement, Element)
169 0 : NS_IMPL_RELEASE_INHERITED(HTMLSelectElement, Element)
170 :
171 : // QueryInterface implementation for HTMLSelectElement
172 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLSelectElement)
173 0 : NS_INTERFACE_TABLE_INHERITED(HTMLSelectElement,
174 : nsIDOMHTMLSelectElement,
175 : nsIConstraintValidation)
176 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
177 :
178 :
179 : // nsIDOMHTMLSelectElement
180 :
181 :
182 0 : NS_IMPL_ELEMENT_CLONE(HTMLSelectElement)
183 :
184 : // nsIConstraintValidation
185 0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLSelectElement)
186 :
187 : NS_IMETHODIMP
188 0 : HTMLSelectElement::SetCustomValidity(const nsAString& aError)
189 : {
190 0 : nsIConstraintValidation::SetCustomValidity(aError);
191 :
192 0 : UpdateState(true);
193 :
194 0 : return NS_OK;
195 : }
196 :
197 : void
198 0 : HTMLSelectElement::GetAutocomplete(DOMString& aValue)
199 : {
200 0 : const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
201 :
202 0 : mAutocompleteAttrState =
203 0 : nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue,
204 0 : mAutocompleteAttrState);
205 0 : }
206 :
207 : void
208 0 : HTMLSelectElement::GetAutocompleteInfo(AutocompleteInfo& aInfo)
209 : {
210 0 : const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
211 0 : mAutocompleteInfoState =
212 0 : nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aInfo,
213 0 : mAutocompleteInfoState,
214 : true);
215 0 : }
216 :
217 : NS_IMETHODIMP
218 0 : HTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
219 : {
220 0 : return nsGenericHTMLFormElementWithState::GetForm(aForm);
221 : }
222 :
223 : nsresult
224 0 : HTMLSelectElement::InsertChildAt(nsIContent* aKid,
225 : uint32_t aIndex,
226 : bool aNotify)
227 : {
228 0 : SafeOptionListMutation safeMutation(this, this, aKid, aIndex, aNotify);
229 0 : nsresult rv = nsGenericHTMLFormElementWithState::InsertChildAt(aKid, aIndex,
230 0 : aNotify);
231 0 : if (NS_FAILED(rv)) {
232 0 : safeMutation.MutationFailed();
233 : }
234 0 : return rv;
235 : }
236 :
237 : void
238 0 : HTMLSelectElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
239 : {
240 0 : SafeOptionListMutation safeMutation(this, this, nullptr, aIndex, aNotify);
241 0 : nsGenericHTMLFormElementWithState::RemoveChildAt(aIndex, aNotify);
242 0 : }
243 :
244 :
245 :
246 : void
247 0 : HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
248 : int32_t aListIndex,
249 : int32_t aDepth,
250 : bool aNotify)
251 : {
252 0 : MOZ_ASSERT(aDepth == 0 || aDepth == 1);
253 0 : int32_t insertIndex = aListIndex;
254 :
255 0 : HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
256 0 : if (optElement) {
257 0 : mOptions->InsertOptionAt(optElement, insertIndex);
258 0 : insertIndex++;
259 0 : } else if (aDepth == 0) {
260 : // If it's at the top level, then we just found out there are non-options
261 : // at the top level, which will throw off the insert count
262 0 : mNonOptionChildren++;
263 :
264 : // Deal with optgroups
265 0 : if (aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
266 0 : mOptGroupCount++;
267 :
268 0 : for (nsIContent* child = aOptions->GetFirstChild();
269 0 : child;
270 0 : child = child->GetNextSibling()) {
271 0 : optElement = HTMLOptionElement::FromContent(child);
272 0 : if (optElement) {
273 0 : mOptions->InsertOptionAt(optElement, insertIndex);
274 0 : insertIndex++;
275 : }
276 : }
277 : }
278 : } // else ignore even if optgroup; we want to ignore nested optgroups.
279 :
280 : // Deal with the selected list
281 0 : if (insertIndex - aListIndex) {
282 : // Fix the currently selected index
283 0 : if (aListIndex <= mSelectedIndex) {
284 0 : mSelectedIndex += (insertIndex - aListIndex);
285 0 : SetSelectionChanged(true, aNotify);
286 : }
287 :
288 : // Get the frame stuff for notification. No need to flush here
289 : // since if there's no frame for the select yet the select will
290 : // get into the right state once it's created.
291 0 : nsISelectControlFrame* selectFrame = nullptr;
292 0 : AutoWeakFrame weakSelectFrame;
293 0 : bool didGetFrame = false;
294 :
295 : // Actually select the options if the added options warrant it
296 0 : for (int32_t i = aListIndex; i < insertIndex; i++) {
297 : // Notify the frame that the option is added
298 0 : if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
299 0 : selectFrame = GetSelectFrame();
300 0 : weakSelectFrame = do_QueryFrame(selectFrame);
301 0 : didGetFrame = true;
302 : }
303 :
304 0 : if (selectFrame) {
305 0 : selectFrame->AddOption(i);
306 : }
307 :
308 0 : RefPtr<HTMLOptionElement> option = Item(i);
309 0 : if (option && option->Selected()) {
310 : // Clear all other options
311 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
312 0 : uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
313 0 : SetOptionsSelectedByIndex(i, i, mask);
314 : }
315 :
316 : // This is sort of a hack ... we need to notify that the option was
317 : // set and change selectedIndex even though we didn't really change
318 : // its value.
319 0 : OnOptionSelected(selectFrame, i, true, false, false);
320 : }
321 : }
322 :
323 0 : CheckSelectSomething(aNotify);
324 : }
325 0 : }
326 :
327 : nsresult
328 0 : HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
329 : int32_t aListIndex,
330 : int32_t aDepth,
331 : bool aNotify)
332 : {
333 0 : MOZ_ASSERT(aDepth == 0 || aDepth == 1);
334 0 : int32_t numRemoved = 0;
335 :
336 0 : HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
337 0 : if (optElement) {
338 0 : if (mOptions->ItemAsOption(aListIndex) != optElement) {
339 0 : NS_ERROR("wrong option at index");
340 0 : return NS_ERROR_UNEXPECTED;
341 : }
342 0 : mOptions->RemoveOptionAt(aListIndex);
343 0 : numRemoved++;
344 0 : } else if (aDepth == 0) {
345 : // Yay, one less artifact at the top level.
346 0 : mNonOptionChildren--;
347 :
348 : // Recurse down deeper for options
349 0 : if (mOptGroupCount && aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
350 0 : mOptGroupCount--;
351 :
352 0 : for (nsIContent* child = aOptions->GetFirstChild();
353 0 : child;
354 0 : child = child->GetNextSibling()) {
355 0 : optElement = HTMLOptionElement::FromContent(child);
356 0 : if (optElement) {
357 0 : if (mOptions->ItemAsOption(aListIndex) != optElement) {
358 0 : NS_ERROR("wrong option at index");
359 0 : return NS_ERROR_UNEXPECTED;
360 : }
361 0 : mOptions->RemoveOptionAt(aListIndex);
362 0 : numRemoved++;
363 : }
364 : }
365 : }
366 : } // else don't check for an optgroup; we want to ignore nested optgroups
367 :
368 0 : if (numRemoved) {
369 : // Tell the widget we removed the options
370 0 : nsISelectControlFrame* selectFrame = GetSelectFrame();
371 0 : if (selectFrame) {
372 0 : nsAutoScriptBlocker scriptBlocker;
373 0 : for (int32_t i = aListIndex; i < aListIndex + numRemoved; ++i) {
374 0 : selectFrame->RemoveOption(i);
375 : }
376 : }
377 :
378 : // Fix the selected index
379 0 : if (aListIndex <= mSelectedIndex) {
380 0 : if (mSelectedIndex < (aListIndex+numRemoved)) {
381 : // aListIndex <= mSelectedIndex < aListIndex+numRemoved
382 : // Find a new selected index if it was one of the ones removed.
383 0 : FindSelectedIndex(aListIndex, aNotify);
384 : } else {
385 : // Shift the selected index if something in front of it was removed
386 : // aListIndex+numRemoved <= mSelectedIndex
387 0 : mSelectedIndex -= numRemoved;
388 0 : SetSelectionChanged(true, aNotify);
389 : }
390 : }
391 :
392 : // Select something in case we removed the selected option on a
393 : // single select
394 0 : if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) {
395 : // Update the validity state in case of we've just removed the last
396 : // option.
397 0 : UpdateValueMissingValidityState();
398 :
399 0 : UpdateState(aNotify);
400 : }
401 : }
402 :
403 0 : return NS_OK;
404 : }
405 :
406 : // XXXldb Doing the processing before the content nodes have been added
407 : // to the document (as the name of this function seems to require, and
408 : // as the callers do), is highly unusual. Passing around unparented
409 : // content to other parts of the app can make those things think the
410 : // options are the root content node.
411 : NS_IMETHODIMP
412 0 : HTMLSelectElement::WillAddOptions(nsIContent* aOptions,
413 : nsIContent* aParent,
414 : int32_t aContentIndex,
415 : bool aNotify)
416 : {
417 0 : if (this != aParent && this != aParent->GetParent()) {
418 0 : return NS_OK;
419 : }
420 0 : int32_t level = aParent == this ? 0 : 1;
421 :
422 : // Get the index where the options will be inserted
423 0 : int32_t ind = -1;
424 0 : if (!mNonOptionChildren) {
425 : // If there are no artifacts, aContentIndex == ind
426 0 : ind = aContentIndex;
427 : } else {
428 : // If there are artifacts, we have to get the index of the option the
429 : // hard way
430 0 : int32_t children = aParent->GetChildCount();
431 :
432 0 : if (aContentIndex >= children) {
433 : // If the content insert is after the end of the parent, then we want to get
434 : // the next index *after* the parent and insert there.
435 0 : ind = GetOptionIndexAfter(aParent);
436 : } else {
437 : // If the content insert is somewhere in the middle of the container, then
438 : // we want to get the option currently at the index and insert in front of
439 : // that.
440 0 : nsIContent* currentKid = aParent->GetChildAt(aContentIndex);
441 0 : NS_ASSERTION(currentKid, "Child not found!");
442 0 : if (currentKid) {
443 0 : ind = GetOptionIndexAt(currentKid);
444 : } else {
445 0 : ind = -1;
446 : }
447 : }
448 : }
449 :
450 0 : InsertOptionsIntoList(aOptions, ind, level, aNotify);
451 0 : return NS_OK;
452 : }
453 :
454 : NS_IMETHODIMP
455 0 : HTMLSelectElement::WillRemoveOptions(nsIContent* aParent,
456 : int32_t aContentIndex,
457 : bool aNotify)
458 : {
459 0 : if (this != aParent && this != aParent->GetParent()) {
460 0 : return NS_OK;
461 : }
462 0 : int32_t level = this == aParent ? 0 : 1;
463 :
464 : // Get the index where the options will be removed
465 0 : nsIContent* currentKid = aParent->GetChildAt(aContentIndex);
466 0 : if (currentKid) {
467 : int32_t ind;
468 0 : if (!mNonOptionChildren) {
469 : // If there are no artifacts, aContentIndex == ind
470 0 : ind = aContentIndex;
471 : } else {
472 : // If there are artifacts, we have to get the index of the option the
473 : // hard way
474 0 : ind = GetFirstOptionIndex(currentKid);
475 : }
476 0 : if (ind != -1) {
477 0 : nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify);
478 0 : NS_ENSURE_SUCCESS(rv, rv);
479 : }
480 : }
481 :
482 0 : return NS_OK;
483 : }
484 :
485 : int32_t
486 0 : HTMLSelectElement::GetOptionIndexAt(nsIContent* aOptions)
487 : {
488 : // Search this node and below.
489 : // If not found, find the first one *after* this node.
490 0 : int32_t retval = GetFirstOptionIndex(aOptions);
491 0 : if (retval == -1) {
492 0 : retval = GetOptionIndexAfter(aOptions);
493 : }
494 :
495 0 : return retval;
496 : }
497 :
498 : int32_t
499 0 : HTMLSelectElement::GetOptionIndexAfter(nsIContent* aOptions)
500 : {
501 : // - If this is the select, the next option is the last.
502 : // - If not, search all the options after aOptions and up to the last option
503 : // in the parent.
504 : // - If it's not there, search for the first option after the parent.
505 0 : if (aOptions == this) {
506 0 : return Length();
507 : }
508 :
509 0 : int32_t retval = -1;
510 :
511 0 : nsCOMPtr<nsIContent> parent = aOptions->GetParent();
512 :
513 0 : if (parent) {
514 0 : int32_t index = parent->IndexOf(aOptions);
515 0 : int32_t count = parent->GetChildCount();
516 :
517 0 : retval = GetFirstChildOptionIndex(parent, index+1, count);
518 :
519 0 : if (retval == -1) {
520 0 : retval = GetOptionIndexAfter(parent);
521 : }
522 : }
523 :
524 0 : return retval;
525 : }
526 :
527 : int32_t
528 0 : HTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
529 : {
530 0 : int32_t listIndex = -1;
531 0 : HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
532 0 : if (optElement) {
533 0 : GetOptionIndex(optElement, 0, true, &listIndex);
534 0 : return listIndex;
535 : }
536 :
537 0 : listIndex = GetFirstChildOptionIndex(aOptions, 0, aOptions->GetChildCount());
538 :
539 0 : return listIndex;
540 : }
541 :
542 : int32_t
543 0 : HTMLSelectElement::GetFirstChildOptionIndex(nsIContent* aOptions,
544 : int32_t aStartIndex,
545 : int32_t aEndIndex)
546 : {
547 0 : int32_t retval = -1;
548 :
549 0 : for (int32_t i = aStartIndex; i < aEndIndex; ++i) {
550 0 : retval = GetFirstOptionIndex(aOptions->GetChildAt(i));
551 0 : if (retval != -1) {
552 0 : break;
553 : }
554 : }
555 :
556 0 : return retval;
557 : }
558 :
559 : nsISelectControlFrame*
560 0 : HTMLSelectElement::GetSelectFrame()
561 : {
562 0 : nsIFormControlFrame* form_control_frame = GetFormControlFrame(false);
563 :
564 0 : nsISelectControlFrame* select_frame = nullptr;
565 :
566 0 : if (form_control_frame) {
567 0 : select_frame = do_QueryFrame(form_control_frame);
568 : }
569 :
570 0 : return select_frame;
571 : }
572 :
573 : void
574 0 : HTMLSelectElement::Add(const HTMLOptionElementOrHTMLOptGroupElement& aElement,
575 : const Nullable<HTMLElementOrLong>& aBefore,
576 : ErrorResult& aRv)
577 : {
578 : nsGenericHTMLElement& element =
579 0 : aElement.IsHTMLOptionElement() ?
580 0 : static_cast<nsGenericHTMLElement&>(aElement.GetAsHTMLOptionElement()) :
581 0 : static_cast<nsGenericHTMLElement&>(aElement.GetAsHTMLOptGroupElement());
582 :
583 0 : if (aBefore.IsNull()) {
584 0 : Add(element, static_cast<nsGenericHTMLElement*>(nullptr), aRv);
585 0 : } else if (aBefore.Value().IsHTMLElement()) {
586 0 : Add(element, &aBefore.Value().GetAsHTMLElement(), aRv);
587 : } else {
588 0 : Add(element, aBefore.Value().GetAsLong(), aRv);
589 : }
590 0 : }
591 :
592 : void
593 0 : HTMLSelectElement::Add(nsGenericHTMLElement& aElement,
594 : nsGenericHTMLElement* aBefore,
595 : ErrorResult& aError)
596 : {
597 0 : if (!aBefore) {
598 0 : Element::AppendChild(aElement, aError);
599 0 : return;
600 : }
601 :
602 : // Just in case we're not the parent, get the parent of the reference
603 : // element
604 0 : nsCOMPtr<nsINode> parent = aBefore->Element::GetParentNode();
605 0 : if (!parent || !nsContentUtils::ContentIsDescendantOf(parent, this)) {
606 : // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT
607 : // element.
608 0 : aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
609 0 : return;
610 : }
611 :
612 : // If the before parameter is not null, we are equivalent to the
613 : // insertBefore method on the parent of before.
614 0 : nsCOMPtr<nsINode> refNode = aBefore;
615 0 : parent->InsertBefore(aElement, refNode, aError);
616 : }
617 :
618 : NS_IMETHODIMP
619 0 : HTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
620 : nsIVariant* aBefore)
621 : {
622 : uint16_t dataType;
623 0 : nsresult rv = aBefore->GetDataType(&dataType);
624 0 : NS_ENSURE_SUCCESS(rv, rv);
625 :
626 0 : nsCOMPtr<nsIContent> element = do_QueryInterface(aElement);
627 : nsGenericHTMLElement* htmlElement =
628 0 : nsGenericHTMLElement::FromContentOrNull(element);
629 0 : if (!htmlElement) {
630 0 : return NS_ERROR_NULL_POINTER;
631 : }
632 :
633 : // aBefore is omitted, undefined or null
634 0 : if (dataType == nsIDataType::VTYPE_EMPTY ||
635 0 : dataType == nsIDataType::VTYPE_VOID) {
636 0 : ErrorResult error;
637 0 : Add(*htmlElement, (nsGenericHTMLElement*)nullptr, error);
638 0 : return error.StealNSResult();
639 : }
640 :
641 0 : nsCOMPtr<nsISupports> supports;
642 :
643 : // whether aBefore is nsIDOMHTMLElement...
644 0 : if (NS_SUCCEEDED(aBefore->GetAsISupports(getter_AddRefs(supports)))) {
645 0 : nsCOMPtr<nsIContent> beforeElement = do_QueryInterface(supports);
646 : nsGenericHTMLElement* beforeHTMLElement =
647 0 : nsGenericHTMLElement::FromContentOrNull(beforeElement);
648 :
649 0 : NS_ENSURE_TRUE(beforeHTMLElement, NS_ERROR_DOM_SYNTAX_ERR);
650 :
651 0 : ErrorResult error;
652 0 : Add(*htmlElement, beforeHTMLElement, error);
653 0 : return error.StealNSResult();
654 : }
655 :
656 : // otherwise, whether aBefore is long
657 : int32_t index;
658 0 : NS_ENSURE_SUCCESS(aBefore->GetAsInt32(&index), NS_ERROR_DOM_SYNTAX_ERR);
659 :
660 0 : ErrorResult error;
661 0 : Add(*htmlElement, index, error);
662 0 : return error.StealNSResult();
663 : }
664 :
665 : NS_IMETHODIMP
666 0 : HTMLSelectElement::Remove(int32_t aIndex)
667 : {
668 0 : nsCOMPtr<nsINode> option = Item(static_cast<uint32_t>(aIndex));
669 0 : if (!option) {
670 0 : return NS_OK;
671 : }
672 :
673 0 : option->Remove();
674 0 : return NS_OK;
675 : }
676 :
677 : NS_IMETHODIMP
678 0 : HTMLSelectElement::GetOptions(nsIDOMHTMLOptionsCollection** aValue)
679 : {
680 0 : NS_IF_ADDREF(*aValue = GetOptions());
681 :
682 0 : return NS_OK;
683 : }
684 :
685 : NS_IMETHODIMP
686 0 : HTMLSelectElement::GetType(nsAString& aType)
687 : {
688 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
689 0 : aType.AssignLiteral("select-multiple");
690 : }
691 : else {
692 0 : aType.AssignLiteral("select-one");
693 : }
694 :
695 0 : return NS_OK;
696 : }
697 :
698 : NS_IMETHODIMP
699 0 : HTMLSelectElement::GetLength(uint32_t* aLength)
700 : {
701 0 : return mOptions->GetLength(aLength);
702 : }
703 :
704 : #define MAX_DYNAMIC_SELECT_LENGTH 10000
705 :
706 : NS_IMETHODIMP
707 0 : HTMLSelectElement::SetLength(uint32_t aLength)
708 : {
709 0 : ErrorResult rv;
710 0 : SetLength(aLength, rv);
711 0 : return rv.StealNSResult();
712 : }
713 :
714 : void
715 0 : HTMLSelectElement::SetLength(uint32_t aLength, ErrorResult& aRv)
716 : {
717 0 : uint32_t curlen = Length();
718 :
719 0 : if (curlen > aLength) { // Remove extra options
720 0 : for (uint32_t i = curlen; i > aLength; --i) {
721 0 : MOZ_ALWAYS_SUCCEEDS(Remove(i - 1));
722 : }
723 0 : } else if (aLength > curlen) {
724 0 : if (aLength > MAX_DYNAMIC_SELECT_LENGTH) {
725 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
726 0 : return;
727 : }
728 :
729 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
730 :
731 0 : nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::option,
732 0 : getter_AddRefs(nodeInfo));
733 :
734 0 : nsCOMPtr<nsINode> node = NS_NewHTMLOptionElement(nodeInfo.forget());
735 :
736 0 : RefPtr<nsTextNode> text = new nsTextNode(mNodeInfo->NodeInfoManager());
737 :
738 0 : aRv = node->AppendChildTo(text, false);
739 0 : if (aRv.Failed()) {
740 0 : return;
741 : }
742 :
743 0 : for (uint32_t i = curlen; i < aLength; i++) {
744 0 : nsINode::AppendChild(*node, aRv);
745 0 : if (aRv.Failed()) {
746 0 : return;
747 : }
748 :
749 0 : if (i + 1 < aLength) {
750 0 : node = node->CloneNode(true, aRv);
751 0 : if (aRv.Failed()) {
752 0 : return;
753 : }
754 0 : MOZ_ASSERT(node);
755 : }
756 : }
757 : }
758 : }
759 :
760 : /* static */
761 : bool
762 0 : HTMLSelectElement::MatchSelectedOptions(Element* aElement,
763 : int32_t /* unused */,
764 : nsIAtom* /* unused */,
765 : void* /* unused*/)
766 : {
767 0 : HTMLOptionElement* option = HTMLOptionElement::FromContent(aElement);
768 0 : return option && option->Selected();
769 : }
770 :
771 : nsIHTMLCollection*
772 0 : HTMLSelectElement::SelectedOptions()
773 : {
774 0 : if (!mSelectedOptions) {
775 : mSelectedOptions = new nsContentList(this, MatchSelectedOptions, nullptr,
776 0 : nullptr, /* deep */ true);
777 : }
778 0 : return mSelectedOptions;
779 : }
780 :
781 : NS_IMETHODIMP
782 0 : HTMLSelectElement::GetSelectedOptions(nsIDOMHTMLCollection** aSelectedOptions)
783 : {
784 0 : NS_ADDREF(*aSelectedOptions = SelectedOptions());
785 0 : return NS_OK;
786 : }
787 :
788 : //NS_IMPL_INT_ATTR(HTMLSelectElement, SelectedIndex, selectedindex)
789 :
790 : NS_IMETHODIMP
791 0 : HTMLSelectElement::GetSelectedIndex(int32_t* aValue)
792 : {
793 0 : *aValue = SelectedIndex();
794 :
795 0 : return NS_OK;
796 : }
797 :
798 : nsresult
799 0 : HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify)
800 : {
801 0 : int32_t oldSelectedIndex = mSelectedIndex;
802 0 : uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED;
803 0 : if (aNotify) {
804 0 : mask |= NOTIFY;
805 : }
806 :
807 0 : SetOptionsSelectedByIndex(aIndex, aIndex, mask);
808 :
809 0 : nsresult rv = NS_OK;
810 0 : nsISelectControlFrame* selectFrame = GetSelectFrame();
811 0 : if (selectFrame) {
812 0 : rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
813 : }
814 :
815 0 : SetSelectionChanged(true, aNotify);
816 :
817 0 : return rv;
818 : }
819 :
820 : NS_IMETHODIMP
821 0 : HTMLSelectElement::SetSelectedIndex(int32_t aIndex)
822 : {
823 0 : return SetSelectedIndexInternal(aIndex, true);
824 : }
825 :
826 : NS_IMETHODIMP
827 0 : HTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
828 : int32_t aStartIndex, bool aForward,
829 : int32_t* aIndex)
830 : {
831 0 : nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
832 0 : return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
833 : }
834 :
835 : bool
836 0 : HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex)
837 : {
838 0 : HTMLOptionElement* option = Item(static_cast<uint32_t>(aIndex));
839 0 : return option && option->Selected();
840 : }
841 :
842 : void
843 0 : HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
844 : int32_t aIndex,
845 : bool aSelected,
846 : bool aChangeOptionState,
847 : bool aNotify)
848 : {
849 : // Set the selected index
850 0 : if (aSelected && (aIndex < mSelectedIndex || mSelectedIndex < 0)) {
851 0 : mSelectedIndex = aIndex;
852 0 : SetSelectionChanged(true, aNotify);
853 0 : } else if (!aSelected && aIndex == mSelectedIndex) {
854 0 : FindSelectedIndex(aIndex + 1, aNotify);
855 : }
856 :
857 0 : if (aChangeOptionState) {
858 : // Tell the option to get its bad self selected
859 0 : RefPtr<HTMLOptionElement> option = Item(static_cast<uint32_t>(aIndex));
860 0 : if (option) {
861 0 : option->SetSelectedInternal(aSelected, aNotify);
862 : }
863 : }
864 :
865 : // Let the frame know too
866 0 : if (aSelectFrame) {
867 0 : aSelectFrame->OnOptionSelected(aIndex, aSelected);
868 : }
869 :
870 0 : UpdateSelectedOptions();
871 0 : UpdateValueMissingValidityState();
872 0 : UpdateState(aNotify);
873 0 : }
874 :
875 : void
876 0 : HTMLSelectElement::FindSelectedIndex(int32_t aStartIndex, bool aNotify)
877 : {
878 0 : mSelectedIndex = -1;
879 0 : SetSelectionChanged(true, aNotify);
880 0 : uint32_t len = Length();
881 0 : for (int32_t i = aStartIndex; i < int32_t(len); i++) {
882 0 : if (IsOptionSelectedByIndex(i)) {
883 0 : mSelectedIndex = i;
884 0 : SetSelectionChanged(true, aNotify);
885 0 : break;
886 : }
887 : }
888 0 : }
889 :
890 : // XXX Consider splitting this into two functions for ease of reading:
891 : // SelectOptionsByIndex(startIndex, endIndex, clearAll, checkDisabled)
892 : // startIndex, endIndex - the range of options to turn on
893 : // (-1, -1) will clear all indices no matter what.
894 : // clearAll - will clear all other options unless checkDisabled is on
895 : // and all the options attempted to be set are disabled
896 : // (note that if it is not multiple, and an option is selected,
897 : // everything else will be cleared regardless).
898 : // checkDisabled - if this is TRUE, and an option is disabled, it will not be
899 : // changed regardless of whether it is selected or not.
900 : // Generally the UI passes TRUE and JS passes FALSE.
901 : // (setDisabled currently is the opposite)
902 : // DeselectOptionsByIndex(startIndex, endIndex, checkDisabled)
903 : // startIndex, endIndex - the range of options to turn on
904 : // (-1, -1) will clear all indices no matter what.
905 : // checkDisabled - if this is TRUE, and an option is disabled, it will not be
906 : // changed regardless of whether it is selected or not.
907 : // Generally the UI passes TRUE and JS passes FALSE.
908 : // (setDisabled currently is the opposite)
909 : //
910 : // XXXbz the above comment is pretty confusing. Maybe we should actually
911 : // document the args to this function too, in addition to documenting what
912 : // things might end up looking like? In particular, pay attention to the
913 : // setDisabled vs checkDisabled business.
914 : bool
915 0 : HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex,
916 : int32_t aEndIndex,
917 : uint32_t aOptionsMask)
918 : {
919 : #if 0
920 : printf("SetOption(%d-%d, %c, ClearAll=%c)\n", aStartIndex, aEndIndex,
921 : (aOptionsMask & IS_SELECTED ? 'Y' : 'N'),
922 : (aOptionsMask & CLEAR_ALL ? 'Y' : 'N'));
923 : #endif
924 : // Don't bother if the select is disabled
925 0 : if (!(aOptionsMask & SET_DISABLED) && IsDisabled()) {
926 0 : return false;
927 : }
928 :
929 : // Don't bother if there are no options
930 0 : uint32_t numItems = Length();
931 0 : if (numItems == 0) {
932 0 : return false;
933 : }
934 :
935 : // First, find out whether multiple items can be selected
936 0 : bool isMultiple = Multiple();
937 :
938 : // These variables tell us whether any options were selected
939 : // or deselected.
940 0 : bool optionsSelected = false;
941 0 : bool optionsDeselected = false;
942 :
943 0 : nsISelectControlFrame* selectFrame = nullptr;
944 0 : bool didGetFrame = false;
945 0 : AutoWeakFrame weakSelectFrame;
946 :
947 0 : if (aOptionsMask & IS_SELECTED) {
948 : // Setting selectedIndex to an out-of-bounds index means -1. (HTML5)
949 0 : if (aStartIndex < 0 || AssertedCast<uint32_t>(aStartIndex) >= numItems ||
950 0 : aEndIndex < 0 || AssertedCast<uint32_t>(aEndIndex) >= numItems) {
951 0 : aStartIndex = -1;
952 0 : aEndIndex = -1;
953 : }
954 :
955 : // Only select the first value if it's not multiple
956 0 : if (!isMultiple) {
957 0 : aEndIndex = aStartIndex;
958 : }
959 :
960 : // This variable tells whether or not all of the options we attempted to
961 : // select are disabled. If ClearAll is passed in as true, and we do not
962 : // select anything because the options are disabled, we will not clear the
963 : // other options. (This is to make the UI work the way one might expect.)
964 0 : bool allDisabled = !(aOptionsMask & SET_DISABLED);
965 :
966 : //
967 : // Save a little time when clearing other options
968 : //
969 0 : int32_t previousSelectedIndex = mSelectedIndex;
970 :
971 : //
972 : // Select the requested indices
973 : //
974 : // If index is -1, everything will be deselected (bug 28143)
975 0 : if (aStartIndex != -1) {
976 0 : MOZ_ASSERT(aStartIndex >= 0);
977 0 : MOZ_ASSERT(aEndIndex >= 0);
978 : // Loop through the options and select them (if they are not disabled and
979 : // if they are not already selected).
980 0 : for (uint32_t optIndex = AssertedCast<uint32_t>(aStartIndex);
981 0 : optIndex <= AssertedCast<uint32_t>(aEndIndex);
982 : optIndex++) {
983 0 : RefPtr<HTMLOptionElement> option = Item(optIndex);
984 :
985 : // Ignore disabled options.
986 0 : if (!(aOptionsMask & SET_DISABLED)) {
987 0 : if (option && IsOptionDisabled(option)) {
988 0 : continue;
989 : }
990 0 : allDisabled = false;
991 : }
992 :
993 : // If the index is already selected, ignore it.
994 0 : if (option && !option->Selected()) {
995 : // To notify the frame if anything gets changed. No need
996 : // to flush here, if there's no frame yet we don't need to
997 : // force it to be created just to notify it about a change
998 : // in the select.
999 0 : selectFrame = GetSelectFrame();
1000 0 : weakSelectFrame = do_QueryFrame(selectFrame);
1001 0 : didGetFrame = true;
1002 :
1003 0 : OnOptionSelected(selectFrame, optIndex, true, true,
1004 0 : aOptionsMask & NOTIFY);
1005 0 : optionsSelected = true;
1006 : }
1007 : }
1008 : }
1009 :
1010 : // Next remove all other options if single select or all is clear
1011 : // If index is -1, everything will be deselected (bug 28143)
1012 0 : if (((!isMultiple && optionsSelected)
1013 0 : || ((aOptionsMask & CLEAR_ALL) && !allDisabled)
1014 0 : || aStartIndex == -1)
1015 0 : && previousSelectedIndex != -1) {
1016 0 : for (uint32_t optIndex = AssertedCast<uint32_t>(previousSelectedIndex);
1017 0 : optIndex < numItems;
1018 : optIndex++) {
1019 0 : if (static_cast<int32_t>(optIndex) < aStartIndex ||
1020 0 : static_cast<int32_t>(optIndex) > aEndIndex) {
1021 0 : HTMLOptionElement* option = Item(optIndex);
1022 : // If the index is already selected, ignore it.
1023 0 : if (option && option->Selected()) {
1024 0 : if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
1025 : // To notify the frame if anything gets changed, don't
1026 : // flush, if the frame doesn't exist we don't need to
1027 : // create it just to tell it about this change.
1028 0 : selectFrame = GetSelectFrame();
1029 0 : weakSelectFrame = do_QueryFrame(selectFrame);
1030 :
1031 0 : didGetFrame = true;
1032 : }
1033 :
1034 0 : OnOptionSelected(selectFrame, optIndex, false, true,
1035 0 : aOptionsMask & NOTIFY);
1036 0 : optionsDeselected = true;
1037 :
1038 : // Only need to deselect one option if not multiple
1039 0 : if (!isMultiple) {
1040 0 : break;
1041 : }
1042 : }
1043 : }
1044 : }
1045 : }
1046 : } else {
1047 : // If we're deselecting, loop through all selected items and deselect
1048 : // any that are in the specified range.
1049 0 : for (int32_t optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
1050 0 : HTMLOptionElement* option = Item(optIndex);
1051 0 : if (!(aOptionsMask & SET_DISABLED) && IsOptionDisabled(option)) {
1052 0 : continue;
1053 : }
1054 :
1055 : // If the index is already selected, ignore it.
1056 0 : if (option && option->Selected()) {
1057 0 : if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
1058 : // To notify the frame if anything gets changed, don't
1059 : // flush, if the frame doesn't exist we don't need to
1060 : // create it just to tell it about this change.
1061 0 : selectFrame = GetSelectFrame();
1062 0 : weakSelectFrame = do_QueryFrame(selectFrame);
1063 :
1064 0 : didGetFrame = true;
1065 : }
1066 :
1067 0 : OnOptionSelected(selectFrame, optIndex, false, true,
1068 0 : aOptionsMask & NOTIFY);
1069 0 : optionsDeselected = true;
1070 : }
1071 : }
1072 : }
1073 :
1074 : // Make sure something is selected unless we were set to -1 (none)
1075 0 : if (optionsDeselected && aStartIndex != -1 && !(aOptionsMask & NO_RESELECT)) {
1076 0 : optionsSelected =
1077 0 : CheckSelectSomething(aOptionsMask & NOTIFY) || optionsSelected;
1078 : }
1079 :
1080 : // Let the caller know whether anything was changed
1081 0 : return optionsSelected || optionsDeselected;
1082 : }
1083 :
1084 : NS_IMETHODIMP
1085 0 : HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled)
1086 : {
1087 0 : *aIsDisabled = false;
1088 0 : RefPtr<HTMLOptionElement> option = Item(aIndex);
1089 0 : NS_ENSURE_TRUE(option, NS_ERROR_FAILURE);
1090 :
1091 0 : *aIsDisabled = IsOptionDisabled(option);
1092 0 : return NS_OK;
1093 : }
1094 :
1095 : bool
1096 0 : HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption)
1097 : {
1098 0 : MOZ_ASSERT(aOption);
1099 0 : if (aOption->Disabled()) {
1100 0 : return true;
1101 : }
1102 :
1103 : // Check for disabled optgroups
1104 : // If there are no artifacts, there are no optgroups
1105 0 : if (mNonOptionChildren) {
1106 0 : for (nsCOMPtr<Element> node = static_cast<nsINode*>(aOption)->GetParentElement();
1107 : node;
1108 0 : node = node->GetParentElement()) {
1109 : // If we reached the select element, we're done
1110 0 : if (node->IsHTMLElement(nsGkAtoms::select)) {
1111 0 : return false;
1112 : }
1113 :
1114 : RefPtr<HTMLOptGroupElement> optGroupElement =
1115 0 : HTMLOptGroupElement::FromContent(node);
1116 :
1117 0 : if (!optGroupElement) {
1118 : // If you put something else between you and the optgroup, you're a
1119 : // moron and you deserve not to have optgroup disabling work.
1120 0 : return false;
1121 : }
1122 :
1123 0 : if (optGroupElement->Disabled()) {
1124 0 : return true;
1125 : }
1126 : }
1127 : }
1128 :
1129 0 : return false;
1130 : }
1131 :
1132 : NS_IMETHODIMP
1133 0 : HTMLSelectElement::GetValue(nsAString& aValue)
1134 : {
1135 0 : DOMString value;
1136 0 : GetValue(value);
1137 0 : value.ToString(aValue);
1138 0 : return NS_OK;
1139 : }
1140 :
1141 : void
1142 0 : HTMLSelectElement::GetValue(DOMString& aValue)
1143 : {
1144 0 : int32_t selectedIndex = SelectedIndex();
1145 0 : if (selectedIndex < 0) {
1146 0 : return;
1147 : }
1148 :
1149 : RefPtr<HTMLOptionElement> option =
1150 0 : Item(static_cast<uint32_t>(selectedIndex));
1151 :
1152 0 : if (!option) {
1153 0 : return;
1154 : }
1155 :
1156 0 : DebugOnly<nsresult> rv = option->GetValue(aValue);
1157 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1158 : }
1159 :
1160 : NS_IMETHODIMP
1161 0 : HTMLSelectElement::SetValue(const nsAString& aValue)
1162 : {
1163 0 : uint32_t length = Length();
1164 :
1165 0 : for (uint32_t i = 0; i < length; i++) {
1166 0 : RefPtr<HTMLOptionElement> option = Item(i);
1167 0 : if (!option) {
1168 0 : continue;
1169 : }
1170 :
1171 0 : nsAutoString optionVal;
1172 0 : option->GetValue(optionVal);
1173 0 : if (optionVal.Equals(aValue)) {
1174 0 : SetSelectedIndexInternal(int32_t(i), true);
1175 0 : return NS_OK;
1176 : }
1177 : }
1178 : // No matching option was found.
1179 0 : SetSelectedIndexInternal(-1, true);
1180 0 : return NS_OK;
1181 : }
1182 :
1183 :
1184 0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Autofocus, autofocus)
1185 0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Disabled, disabled)
1186 0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Multiple, multiple)
1187 0 : NS_IMPL_STRING_ATTR(HTMLSelectElement, Name, name)
1188 0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Required, required)
1189 0 : NS_IMPL_UINT_ATTR(HTMLSelectElement, Size, size)
1190 :
1191 : int32_t
1192 0 : HTMLSelectElement::TabIndexDefault()
1193 : {
1194 0 : return 0;
1195 : }
1196 :
1197 : bool
1198 0 : HTMLSelectElement::IsHTMLFocusable(bool aWithMouse,
1199 : bool* aIsFocusable, int32_t* aTabIndex)
1200 : {
1201 0 : if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable,
1202 : aTabIndex))
1203 : {
1204 0 : return true;
1205 : }
1206 :
1207 0 : *aIsFocusable = !IsDisabled();
1208 :
1209 0 : return false;
1210 : }
1211 :
1212 : NS_IMETHODIMP
1213 0 : HTMLSelectElement::Item(uint32_t aIndex, nsIDOMNode** aReturn)
1214 : {
1215 0 : return mOptions->Item(aIndex, aReturn);
1216 : }
1217 :
1218 : NS_IMETHODIMP
1219 0 : HTMLSelectElement::NamedItem(const nsAString& aName, nsIDOMNode** aReturn)
1220 : {
1221 0 : return mOptions->NamedItem(aName, aReturn);
1222 : }
1223 :
1224 : bool
1225 0 : HTMLSelectElement::CheckSelectSomething(bool aNotify)
1226 : {
1227 0 : if (mIsDoneAddingChildren) {
1228 0 : if (mSelectedIndex < 0 && IsCombobox()) {
1229 0 : return SelectSomething(aNotify);
1230 : }
1231 : }
1232 0 : return false;
1233 : }
1234 :
1235 : bool
1236 0 : HTMLSelectElement::SelectSomething(bool aNotify)
1237 : {
1238 : // If we're not done building the select, don't play with this yet.
1239 0 : if (!mIsDoneAddingChildren) {
1240 0 : return false;
1241 : }
1242 :
1243 : uint32_t count;
1244 0 : GetLength(&count);
1245 0 : for (uint32_t i = 0; i < count; i++) {
1246 : bool disabled;
1247 0 : nsresult rv = IsOptionDisabled(i, &disabled);
1248 :
1249 0 : if (NS_FAILED(rv) || !disabled) {
1250 0 : rv = SetSelectedIndexInternal(i, aNotify);
1251 0 : NS_ENSURE_SUCCESS(rv, false);
1252 :
1253 0 : UpdateValueMissingValidityState();
1254 0 : UpdateState(aNotify);
1255 :
1256 0 : return true;
1257 : }
1258 : }
1259 :
1260 0 : return false;
1261 : }
1262 :
1263 : nsresult
1264 0 : HTMLSelectElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1265 : nsIContent* aBindingParent,
1266 : bool aCompileEventHandlers)
1267 : {
1268 0 : nsresult rv = nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent,
1269 : aBindingParent,
1270 0 : aCompileEventHandlers);
1271 0 : NS_ENSURE_SUCCESS(rv, rv);
1272 :
1273 : // If there is a disabled fieldset in the parent chain, the element is now
1274 : // barred from constraint validation.
1275 : // XXXbz is this still needed now that fieldset changes always call
1276 : // FieldSetDisabledChanged?
1277 0 : UpdateBarredFromConstraintValidation();
1278 :
1279 : // And now make sure our state is up to date
1280 0 : UpdateState(false);
1281 :
1282 0 : return rv;
1283 : }
1284 :
1285 : void
1286 0 : HTMLSelectElement::UnbindFromTree(bool aDeep, bool aNullParent)
1287 : {
1288 0 : nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
1289 :
1290 : // We might be no longer disabled because our parent chain changed.
1291 : // XXXbz is this still needed now that fieldset changes always call
1292 : // FieldSetDisabledChanged?
1293 0 : UpdateBarredFromConstraintValidation();
1294 :
1295 : // And now make sure our state is up to date
1296 0 : UpdateState(false);
1297 0 : }
1298 :
1299 : nsresult
1300 0 : HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1301 : const nsAttrValueOrString* aValue,
1302 : bool aNotify)
1303 : {
1304 0 : if (aNameSpaceID == kNameSpaceID_None) {
1305 0 : if (aName == nsGkAtoms::disabled) {
1306 0 : if (aNotify) {
1307 0 : mDisabledChanged = true;
1308 : }
1309 0 : } else if (aName == nsGkAtoms::multiple) {
1310 0 : if (!aValue && aNotify && mSelectedIndex >= 0) {
1311 : // We're changing from being a multi-select to a single-select.
1312 : // Make sure we only have one option selected before we do that.
1313 : // Note that this needs to come before we really unset the attr,
1314 : // since SetOptionsSelectedByIndex does some bail-out type
1315 : // optimization for cases when the select is not multiple that
1316 : // would lead to only a single option getting deselected.
1317 0 : SetSelectedIndexInternal(mSelectedIndex, aNotify);
1318 : }
1319 : }
1320 : }
1321 :
1322 0 : return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
1323 0 : aValue, aNotify);
1324 : }
1325 :
1326 : nsresult
1327 0 : HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1328 : const nsAttrValue* aValue,
1329 : const nsAttrValue* aOldValue, bool aNotify)
1330 : {
1331 0 : if (aNameSpaceID == kNameSpaceID_None) {
1332 0 : if (aName == nsGkAtoms::disabled) {
1333 0 : UpdateBarredFromConstraintValidation();
1334 0 : } else if (aName == nsGkAtoms::required) {
1335 0 : UpdateValueMissingValidityState();
1336 0 : } else if (aName == nsGkAtoms::autocomplete) {
1337 : // Clear the cached @autocomplete attribute and autocompleteInfo state.
1338 0 : mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
1339 0 : mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
1340 0 : } else if (aName == nsGkAtoms::multiple) {
1341 0 : if (!aValue && aNotify) {
1342 : // We might have become a combobox; make sure _something_ gets
1343 : // selected in that case
1344 0 : CheckSelectSomething(aNotify);
1345 : }
1346 : }
1347 : }
1348 :
1349 0 : return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
1350 : aValue, aOldValue,
1351 0 : aNotify);
1352 : }
1353 :
1354 : void
1355 0 : HTMLSelectElement::DoneAddingChildren(bool aHaveNotified)
1356 : {
1357 0 : mIsDoneAddingChildren = true;
1358 :
1359 0 : nsISelectControlFrame* selectFrame = GetSelectFrame();
1360 :
1361 : // If we foolishly tried to restore before we were done adding
1362 : // content, restore the rest of the options proper-like
1363 0 : if (mRestoreState) {
1364 0 : RestoreStateTo(mRestoreState);
1365 0 : mRestoreState = nullptr;
1366 : }
1367 :
1368 : // Notify the frame
1369 0 : if (selectFrame) {
1370 0 : selectFrame->DoneAddingChildren(true);
1371 : }
1372 :
1373 0 : if (!mInhibitStateRestoration) {
1374 0 : nsresult rv = GenerateStateKey();
1375 0 : if (NS_SUCCEEDED(rv)) {
1376 0 : RestoreFormControlState();
1377 : }
1378 : }
1379 :
1380 : // Now that we're done, select something (if it's a single select something
1381 : // must be selected)
1382 0 : if (!CheckSelectSomething(false)) {
1383 : // If an option has @selected set, it will be selected during parsing but
1384 : // with an empty value. We have to make sure the select element updates it's
1385 : // validity state to take this into account.
1386 0 : UpdateValueMissingValidityState();
1387 :
1388 : // And now make sure we update our content state too
1389 0 : UpdateState(aHaveNotified);
1390 : }
1391 :
1392 0 : mDefaultSelectionSet = true;
1393 0 : }
1394 :
1395 : bool
1396 0 : HTMLSelectElement::ParseAttribute(int32_t aNamespaceID,
1397 : nsIAtom* aAttribute,
1398 : const nsAString& aValue,
1399 : nsAttrValue& aResult)
1400 : {
1401 0 : if (kNameSpaceID_None == aNamespaceID) {
1402 0 : if (aAttribute == nsGkAtoms::size) {
1403 0 : return aResult.ParsePositiveIntValue(aValue);
1404 0 : } else if (aAttribute == nsGkAtoms::autocomplete) {
1405 0 : aResult.ParseAtomArray(aValue);
1406 0 : return true;
1407 : }
1408 : }
1409 0 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
1410 0 : aResult);
1411 : }
1412 :
1413 : void
1414 0 : HTMLSelectElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
1415 : GenericSpecifiedValues* aData)
1416 : {
1417 0 : nsGenericHTMLFormElementWithState::MapImageAlignAttributeInto(aAttributes, aData);
1418 0 : nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
1419 0 : }
1420 :
1421 : nsChangeHint
1422 0 : HTMLSelectElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
1423 : int32_t aModType) const
1424 : {
1425 : nsChangeHint retval =
1426 0 : nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
1427 0 : if (aAttribute == nsGkAtoms::multiple ||
1428 0 : aAttribute == nsGkAtoms::size) {
1429 0 : retval |= nsChangeHint_ReconstructFrame;
1430 : }
1431 0 : return retval;
1432 : }
1433 :
1434 : NS_IMETHODIMP_(bool)
1435 0 : HTMLSelectElement::IsAttributeMapped(const nsIAtom* aAttribute) const
1436 : {
1437 : static const MappedAttributeEntry* const map[] = {
1438 : sCommonAttributeMap,
1439 : sImageAlignAttributeMap
1440 : };
1441 :
1442 0 : return FindAttributeDependence(aAttribute, map);
1443 : }
1444 :
1445 : nsMapRuleToAttributesFunc
1446 0 : HTMLSelectElement::GetAttributeMappingFunction() const
1447 : {
1448 0 : return &MapAttributesIntoRule;
1449 : }
1450 :
1451 : bool
1452 0 : HTMLSelectElement::IsDisabledForEvents(EventMessage aMessage)
1453 : {
1454 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1455 0 : nsIFrame* formFrame = nullptr;
1456 0 : if (formControlFrame) {
1457 0 : formFrame = do_QueryFrame(formControlFrame);
1458 : }
1459 0 : return IsElementDisabledForEvents(aMessage, formFrame);
1460 : }
1461 :
1462 : nsresult
1463 0 : HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
1464 : {
1465 0 : aVisitor.mCanHandle = false;
1466 0 : if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
1467 0 : return NS_OK;
1468 : }
1469 :
1470 0 : return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
1471 : }
1472 :
1473 : nsresult
1474 0 : HTMLSelectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
1475 : {
1476 0 : if (aVisitor.mEvent->mMessage == eFocus) {
1477 : // If the invalid UI is shown, we should show it while focused and
1478 : // update the invalid/valid UI.
1479 0 : mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
1480 :
1481 : // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
1482 : // UI while focused.
1483 0 : mCanShowValidUI = ShouldShowValidityUI();
1484 :
1485 : // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
1486 : // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
1487 0 : } else if (aVisitor.mEvent->mMessage == eBlur) {
1488 0 : mCanShowInvalidUI = true;
1489 0 : mCanShowValidUI = true;
1490 :
1491 0 : UpdateState(true);
1492 : }
1493 :
1494 0 : return nsGenericHTMLFormElementWithState::PostHandleEvent(aVisitor);
1495 : }
1496 :
1497 : EventStates
1498 0 : HTMLSelectElement::IntrinsicState() const
1499 : {
1500 0 : EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
1501 :
1502 0 : if (IsCandidateForConstraintValidation()) {
1503 0 : if (IsValid()) {
1504 0 : state |= NS_EVENT_STATE_VALID;
1505 : } else {
1506 0 : state |= NS_EVENT_STATE_INVALID;
1507 :
1508 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
1509 0 : (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
1510 0 : (mCanShowInvalidUI && ShouldShowValidityUI()))) {
1511 0 : state |= NS_EVENT_STATE_MOZ_UI_INVALID;
1512 : }
1513 : }
1514 :
1515 : // :-moz-ui-valid applies if all the following are true:
1516 : // 1. The element is not focused, or had either :-moz-ui-valid or
1517 : // :-moz-ui-invalid applying before it was focused ;
1518 : // 2. The element is either valid or isn't allowed to have
1519 : // :-moz-ui-invalid applying ;
1520 : // 3. The element has no form owner or its form owner doesn't have the
1521 : // novalidate attribute set ;
1522 : // 4. The element has already been modified or the user tried to submit the
1523 : // form owner while invalid.
1524 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
1525 0 : (mCanShowValidUI && ShouldShowValidityUI() &&
1526 0 : (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
1527 0 : !mCanShowInvalidUI)))) {
1528 0 : state |= NS_EVENT_STATE_MOZ_UI_VALID;
1529 : }
1530 : }
1531 :
1532 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
1533 0 : state |= NS_EVENT_STATE_REQUIRED;
1534 : } else {
1535 0 : state |= NS_EVENT_STATE_OPTIONAL;
1536 : }
1537 :
1538 0 : return state;
1539 : }
1540 :
1541 : // nsIFormControl
1542 :
1543 : NS_IMETHODIMP
1544 0 : HTMLSelectElement::SaveState()
1545 : {
1546 0 : RefPtr<SelectState> state = new SelectState();
1547 :
1548 0 : uint32_t len = Length();
1549 :
1550 0 : for (uint32_t optIndex = 0; optIndex < len; optIndex++) {
1551 0 : HTMLOptionElement* option = Item(optIndex);
1552 0 : if (option && option->Selected()) {
1553 0 : nsAutoString value;
1554 0 : option->GetValue(value);
1555 0 : state->PutOption(optIndex, value);
1556 : }
1557 : }
1558 :
1559 0 : nsPresState* presState = GetPrimaryPresState();
1560 0 : if (presState) {
1561 0 : presState->SetStateProperty(state);
1562 :
1563 0 : if (mDisabledChanged) {
1564 : // We do not want to save the real disabled state but the disabled
1565 : // attribute.
1566 0 : presState->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
1567 : }
1568 : }
1569 :
1570 0 : return NS_OK;
1571 : }
1572 :
1573 : bool
1574 0 : HTMLSelectElement::RestoreState(nsPresState* aState)
1575 : {
1576 : // Get the presentation state object to retrieve our stuff out of.
1577 : nsCOMPtr<SelectState> state(
1578 0 : do_QueryInterface(aState->GetStateProperty()));
1579 :
1580 0 : if (state) {
1581 0 : RestoreStateTo(state);
1582 :
1583 : // Don't flush, if the frame doesn't exist yet it doesn't care if
1584 : // we're reset or not.
1585 0 : DispatchContentReset();
1586 : }
1587 :
1588 0 : if (aState->IsDisabledSet() && !aState->GetDisabled()) {
1589 0 : SetDisabled(false);
1590 : }
1591 :
1592 0 : return false;
1593 : }
1594 :
1595 : void
1596 0 : HTMLSelectElement::RestoreStateTo(SelectState* aNewSelected)
1597 : {
1598 0 : if (!mIsDoneAddingChildren) {
1599 0 : mRestoreState = aNewSelected;
1600 0 : return;
1601 : }
1602 :
1603 0 : uint32_t len = Length();
1604 0 : uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
1605 :
1606 : // First clear all
1607 0 : SetOptionsSelectedByIndex(-1, -1, mask);
1608 :
1609 : // Next set the proper ones
1610 0 : for (uint32_t i = 0; i < len; i++) {
1611 0 : HTMLOptionElement* option = Item(i);
1612 0 : if (option) {
1613 0 : nsAutoString value;
1614 0 : nsresult rv = option->GetValue(value);
1615 0 : if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
1616 0 : SetOptionsSelectedByIndex(i, i, IS_SELECTED | SET_DISABLED | NOTIFY);
1617 : }
1618 : }
1619 : }
1620 : }
1621 :
1622 : NS_IMETHODIMP
1623 0 : HTMLSelectElement::Reset()
1624 : {
1625 0 : uint32_t numSelected = 0;
1626 :
1627 : //
1628 : // Cycle through the options array and reset the options
1629 : //
1630 0 : uint32_t numOptions = Length();
1631 :
1632 0 : for (uint32_t i = 0; i < numOptions; i++) {
1633 0 : RefPtr<HTMLOptionElement> option = Item(i);
1634 0 : if (option) {
1635 : //
1636 : // Reset the option to its default value
1637 : //
1638 :
1639 0 : uint32_t mask = SET_DISABLED | NOTIFY | NO_RESELECT;
1640 0 : if (option->DefaultSelected()) {
1641 0 : mask |= IS_SELECTED;
1642 0 : numSelected++;
1643 : }
1644 :
1645 0 : SetOptionsSelectedByIndex(i, i, mask);
1646 0 : option->SetSelectedChanged(false);
1647 : }
1648 : }
1649 :
1650 : //
1651 : // If nothing was selected and it's not multiple, select something
1652 : //
1653 0 : if (numSelected == 0 && IsCombobox()) {
1654 0 : SelectSomething(true);
1655 : }
1656 :
1657 0 : SetSelectionChanged(false, true);
1658 :
1659 : //
1660 : // Let the frame know we were reset
1661 : //
1662 : // Don't flush, if there's no frame yet it won't care about us being
1663 : // reset even if we forced it to be created now.
1664 : //
1665 0 : DispatchContentReset();
1666 :
1667 0 : return NS_OK;
1668 : }
1669 :
1670 : static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
1671 :
1672 : NS_IMETHODIMP
1673 0 : HTMLSelectElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
1674 : {
1675 : // Disabled elements don't submit
1676 0 : if (IsDisabled()) {
1677 0 : return NS_OK;
1678 : }
1679 :
1680 : //
1681 : // Get the name (if no name, no submit)
1682 : //
1683 0 : nsAutoString name;
1684 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1685 0 : if (name.IsEmpty()) {
1686 0 : return NS_OK;
1687 : }
1688 :
1689 : //
1690 : // Submit
1691 : //
1692 0 : uint32_t len = Length();
1693 :
1694 0 : nsAutoString mozType;
1695 0 : nsCOMPtr<nsIFormProcessor> keyGenProcessor;
1696 0 : if (GetAttr(kNameSpaceID_None, nsGkAtoms::moztype, mozType) &&
1697 0 : mozType.EqualsLiteral("-mozilla-keygen")) {
1698 0 : keyGenProcessor = do_GetService(kFormProcessorCID);
1699 : }
1700 :
1701 0 : for (uint32_t optIndex = 0; optIndex < len; optIndex++) {
1702 0 : HTMLOptionElement* option = Item(optIndex);
1703 :
1704 : // Don't send disabled options
1705 0 : if (!option || IsOptionDisabled(option)) {
1706 0 : continue;
1707 : }
1708 :
1709 0 : if (!option->Selected()) {
1710 0 : continue;
1711 : }
1712 :
1713 0 : nsString value;
1714 0 : MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
1715 :
1716 0 : if (keyGenProcessor) {
1717 0 : nsString tmp(value);
1718 0 : if (NS_SUCCEEDED(keyGenProcessor->ProcessValue(this, name, tmp))) {
1719 0 : value = tmp;
1720 : }
1721 : }
1722 :
1723 0 : aFormSubmission->AddNameValuePair(name, value);
1724 : }
1725 :
1726 0 : return NS_OK;
1727 : }
1728 :
1729 : void
1730 0 : HTMLSelectElement::DispatchContentReset()
1731 : {
1732 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1733 0 : if (formControlFrame) {
1734 : // Only dispatch content reset notification if this is a list control
1735 : // frame or combo box control frame.
1736 0 : if (IsCombobox()) {
1737 0 : nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
1738 0 : if (comboFrame) {
1739 0 : comboFrame->OnContentReset();
1740 : }
1741 : } else {
1742 0 : nsIListControlFrame* listFrame = do_QueryFrame(formControlFrame);
1743 0 : if (listFrame) {
1744 0 : listFrame->OnContentReset();
1745 : }
1746 : }
1747 : }
1748 0 : }
1749 :
1750 : static void
1751 0 : AddOptions(nsIContent* aRoot, HTMLOptionsCollection* aArray)
1752 : {
1753 0 : for (nsIContent* child = aRoot->GetFirstChild();
1754 0 : child;
1755 0 : child = child->GetNextSibling()) {
1756 0 : HTMLOptionElement* opt = HTMLOptionElement::FromContent(child);
1757 0 : if (opt) {
1758 0 : aArray->AppendOption(opt);
1759 0 : } else if (child->IsHTMLElement(nsGkAtoms::optgroup)) {
1760 0 : for (nsIContent* grandchild = child->GetFirstChild();
1761 0 : grandchild;
1762 0 : grandchild = grandchild->GetNextSibling()) {
1763 0 : opt = HTMLOptionElement::FromContent(grandchild);
1764 0 : if (opt) {
1765 0 : aArray->AppendOption(opt);
1766 : }
1767 : }
1768 : }
1769 : }
1770 0 : }
1771 :
1772 : void
1773 0 : HTMLSelectElement::RebuildOptionsArray(bool aNotify)
1774 : {
1775 0 : mOptions->Clear();
1776 0 : AddOptions(this, mOptions);
1777 0 : FindSelectedIndex(0, aNotify);
1778 0 : }
1779 :
1780 : bool
1781 0 : HTMLSelectElement::IsValueMissing()
1782 : {
1783 0 : if (!Required()) {
1784 0 : return false;
1785 : }
1786 :
1787 0 : uint32_t length = Length();
1788 :
1789 0 : for (uint32_t i = 0; i < length; ++i) {
1790 0 : RefPtr<HTMLOptionElement> option = Item(i);
1791 : // Check for a placeholder label option, don't count it as a valid value.
1792 0 : if (i == 0 && !Multiple() && Size() <= 1 && option->GetParent() == this) {
1793 0 : nsAutoString value;
1794 0 : MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
1795 0 : if (value.IsEmpty()) {
1796 0 : continue;
1797 : }
1798 : }
1799 :
1800 0 : if (!option->Selected()) {
1801 0 : continue;
1802 : }
1803 :
1804 0 : if (IsOptionDisabled(option)) {
1805 0 : continue;
1806 : }
1807 :
1808 0 : return false;
1809 : }
1810 :
1811 0 : return true;
1812 : }
1813 :
1814 : void
1815 0 : HTMLSelectElement::UpdateValueMissingValidityState()
1816 : {
1817 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
1818 0 : }
1819 :
1820 : nsresult
1821 0 : HTMLSelectElement::GetValidationMessage(nsAString& aValidationMessage,
1822 : ValidityStateType aType)
1823 : {
1824 0 : switch (aType) {
1825 : case VALIDITY_STATE_VALUE_MISSING: {
1826 0 : nsXPIDLString message;
1827 : nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1828 : "FormValidationSelectMissing",
1829 0 : message);
1830 0 : aValidationMessage = message;
1831 0 : return rv;
1832 : }
1833 : default: {
1834 0 : return nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
1835 : }
1836 : }
1837 : }
1838 :
1839 : #ifdef DEBUG
1840 :
1841 : void
1842 0 : HTMLSelectElement::VerifyOptionsArray()
1843 : {
1844 0 : int32_t index = 0;
1845 0 : for (nsIContent* child = nsINode::GetFirstChild();
1846 0 : child;
1847 0 : child = child->GetNextSibling()) {
1848 0 : HTMLOptionElement* opt = HTMLOptionElement::FromContent(child);
1849 0 : if (opt) {
1850 0 : NS_ASSERTION(opt == mOptions->ItemAsOption(index++),
1851 : "Options collection broken");
1852 0 : } else if (child->IsHTMLElement(nsGkAtoms::optgroup)) {
1853 0 : for (nsIContent* grandchild = child->GetFirstChild();
1854 0 : grandchild;
1855 0 : grandchild = grandchild->GetNextSibling()) {
1856 0 : opt = HTMLOptionElement::FromContent(grandchild);
1857 0 : if (opt) {
1858 0 : NS_ASSERTION(opt == mOptions->ItemAsOption(index++),
1859 : "Options collection broken");
1860 : }
1861 : }
1862 : }
1863 : }
1864 0 : }
1865 :
1866 : #endif
1867 :
1868 : void
1869 0 : HTMLSelectElement::UpdateBarredFromConstraintValidation()
1870 : {
1871 0 : SetBarredFromConstraintValidation(IsDisabled());
1872 0 : }
1873 :
1874 : void
1875 0 : HTMLSelectElement::FieldSetDisabledChanged(bool aNotify)
1876 : {
1877 0 : UpdateBarredFromConstraintValidation();
1878 :
1879 0 : nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
1880 0 : }
1881 :
1882 : void
1883 0 : HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify)
1884 : {
1885 0 : if (!mDefaultSelectionSet) {
1886 0 : return;
1887 : }
1888 :
1889 0 : UpdateSelectedOptions();
1890 :
1891 0 : bool previousSelectionChangedValue = mSelectionHasChanged;
1892 0 : mSelectionHasChanged = aValue;
1893 :
1894 0 : if (mSelectionHasChanged != previousSelectionChangedValue) {
1895 0 : UpdateState(aNotify);
1896 : }
1897 : }
1898 :
1899 : void
1900 0 : HTMLSelectElement::UpdateSelectedOptions()
1901 : {
1902 0 : if (mSelectedOptions) {
1903 0 : mSelectedOptions->SetDirty();
1904 : }
1905 0 : }
1906 :
1907 : bool
1908 0 : HTMLSelectElement::OpenInParentProcess()
1909 : {
1910 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1911 0 : nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
1912 0 : if (comboFrame) {
1913 0 : return comboFrame->IsOpenInParentProcess();
1914 : }
1915 :
1916 0 : return false;
1917 : }
1918 :
1919 : void
1920 0 : HTMLSelectElement::SetOpenInParentProcess(bool aVal)
1921 : {
1922 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1923 0 : nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
1924 0 : if (comboFrame) {
1925 0 : comboFrame->SetOpenInParentProcess(aVal);
1926 : }
1927 0 : }
1928 :
1929 : void
1930 0 : HTMLSelectElement::GetPreviewValue(nsAString& aValue)
1931 : {
1932 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1933 0 : nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
1934 0 : if (comboFrame) {
1935 0 : comboFrame->GetPreviewText(aValue);
1936 : }
1937 0 : }
1938 :
1939 : void
1940 0 : HTMLSelectElement::SetPreviewValue(const nsAString& aValue)
1941 : {
1942 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
1943 0 : nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
1944 0 : if (comboFrame) {
1945 0 : comboFrame->SetPreviewText(aValue);
1946 : }
1947 0 : }
1948 :
1949 : JSObject*
1950 0 : HTMLSelectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
1951 : {
1952 0 : return HTMLSelectElementBinding::Wrap(aCx, this, aGivenProto);
1953 : }
1954 :
1955 : } // namespace dom
1956 : } // namespace mozilla
|