Bug 596681 - Implement HTMLSelectElement.selectedOptions attribute. r=smaug

This commit is contained in:
Reuben Morais 2013-09-02 14:23:27 -03:00
parent 2209a370c7
commit cad644ddfe
7 changed files with 194 additions and 12 deletions

View File

@ -299,6 +299,16 @@ public:
mMatchNameSpaceId == aKey.mMatchNameSpaceId;
}
/**
* Sets the state to LIST_DIRTY and clears mElements array.
* @note This is the only acceptable way to set state to LIST_DIRTY.
*/
void SetDirty()
{
mState = LIST_DIRTY;
Reset();
}
protected:
/**
* Returns whether the element matches our criterion
@ -350,16 +360,6 @@ protected:
*/
inline void BringSelfUpToDate(bool aDoFlush);
/**
* Sets the state to LIST_DIRTY and clears mElements array.
* @note This is the only acceptable way to set state to LIST_DIRTY.
*/
void SetDirty()
{
mState = LIST_DIRTY;
Reset();
}
/**
* To be called from non-destructor locations that want to remove from caches.
* Needed because if subclasses want to have cache behavior they can't just

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/HTMLSelectElementBinding.h"
#include "mozilla/Util.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentList.h"
#include "nsError.h"
#include "nsEventDispatcher.h"
#include "nsEventStates.h"
@ -139,10 +140,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSelectElement,
nsGenericHTMLFormElementWithState)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLSelectElement,
nsGenericHTMLFormElementWithState)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_ADDREF_INHERITED(HTMLSelectElement, Element)
@ -769,6 +772,34 @@ HTMLSelectElement::SetLength(uint32_t aLength, ErrorResult& aRv)
}
}
/* static */
bool
HTMLSelectElement::MatchSelectedOptions(nsIContent* aContent,
int32_t /* unused */,
nsIAtom* /* unused */,
void* /* unused*/)
{
HTMLOptionElement* option = HTMLOptionElement::FromContent(aContent);
return option && option->Selected();
}
nsIHTMLCollection*
HTMLSelectElement::SelectedOptions()
{
if (!mSelectedOptions) {
mSelectedOptions = new nsContentList(this, MatchSelectedOptions, nullptr,
nullptr, /* deep */ true);
}
return mSelectedOptions;
}
NS_IMETHODIMP
HTMLSelectElement::GetSelectedOptions(nsIDOMHTMLCollection** aSelectedOptions)
{
NS_ADDREF(*aSelectedOptions = SelectedOptions());
return NS_OK;
}
//NS_IMPL_INT_ATTR(HTMLSelectElement, SelectedIndex, selectedindex)
NS_IMETHODIMP
@ -847,6 +878,7 @@ HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
aSelectFrame->OnOptionSelected(aIndex, aSelected);
}
UpdateSelectedOptions();
UpdateValueMissingValidityState();
UpdateState(aNotify);
}
@ -1848,6 +1880,8 @@ HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify)
return;
}
UpdateSelectedOptions();
bool previousSelectionChangedValue = mSelectionHasChanged;
mSelectionHasChanged = aValue;
@ -1856,6 +1890,14 @@ HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify)
}
}
void
HTMLSelectElement::UpdateSelectedOptions()
{
if (mSelectedOptions) {
mSelectedOptions->SetDirty();
}
}
JSObject*
HTMLSelectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
{

View File

@ -18,7 +18,9 @@
#include "nsError.h"
#include "mozilla/dom/HTMLFormElement.h"
class nsContentList;
class nsIDOMHTMLOptionElement;
class nsIHTMLCollection;
class nsISelectControlFrame;
class nsPresState;
@ -208,6 +210,11 @@ public:
mOptions->IndexedSetter(aIndex, aOption, aRv);
}
static bool MatchSelectedOptions(nsIContent* aContent, int32_t, nsIAtom*,
void*);
nsIHTMLCollection* SelectedOptions();
int32_t SelectedIndex() const
{
return mSelectedIndex;
@ -553,6 +560,12 @@ protected:
void SetSelectionChanged(bool aValue, bool aNotify);
/**
* Marks the selectedOptions list as dirty, so that it'll populate itself
* again.
*/
void UpdateSelectedOptions();
/**
* Return whether an element should have a validity UI.
* (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
@ -618,6 +631,11 @@ protected:
* done adding options
*/
nsCOMPtr<SelectState> mRestoreState;
/**
* The live list of selected options.
*/
nsRefPtr<nsContentList> mSelectedOptions;
};
} // namespace dom

View File

@ -67,6 +67,7 @@ MOCHITEST_FILES = \
test_input_color_picker_popup.html \
test_input_color_input_change_events.html \
test_restore_form_elements.html \
test_select_selectedOptions.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,120 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=596681
-->
<head>
<title>Test for HTMLSelectElement.selectedOptions</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596681">Mozilla Bug 596681</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for HTMLSelectElement's selectedOptions attribute.
*
* selectedOptions is a live list of the options that have selectedness of true
* (not the selected content attribute).
*
* See http://www.whatwg.org/html/#dom-select-selectedoptions
**/
function checkSelectedOptions(size, elements)
{
is(selectedOptions.length, size,
"select should have " + size + " selected options");
for (let i = 0; i < size; ++i) {
ok(selectedOptions[i], "selected option is valid");
if (selectedOptions[i]) {
is(selectedOptions[i].value, elements[i].value, "selected options are correct");
}
}
}
let select = document.createElement("select");
document.body.appendChild(select);
let selectedOptions = select.selectedOptions;
ok("selectedOptions" in select,
"select element should have a selectedOptions IDL attribute");
ok(select.selectedOptions instanceof HTMLCollection,
"selectedOptions should be an HTMLCollection instance");
let option1 = document.createElement("option");
let option2 = document.createElement("option");
let option3 = document.createElement("option");
option1.id = "option1";
option1.value = "option1";
option2.value = "option2";
option3.value = "option3";
checkSelectedOptions(0, null);
select.add(option1, null);
is(selectedOptions.namedItem("option1").value, "option1", "named getter works");
checkSelectedOptions(1, [option1]);
select.add(option2, null);
checkSelectedOptions(1, [option1]);
select.options[1].selected = true;
checkSelectedOptions(1, [option2]);
select.multiple = true;
checkSelectedOptions(1, [option2]);
select.options[0].selected = true;
checkSelectedOptions(2, [option1, option2]);
option1.selected = false;
// Usinig selected directly on the option should work.
checkSelectedOptions(1, [option2]);
select.remove(1);
select.add(option2, 0);
select.options[0].selected = true;
select.options[1].selected = true;
// Should be in tree order.
checkSelectedOptions(2, [option2, option1]);
select.add(option3, null);
checkSelectedOptions(2, [option2, option1]);
select.options[2].selected = true;
checkSelectedOptions(3, [option2, option1, option3]);
select.length = 0;
option1.selected = false;
option2.selected = false;
option3.selected = false;
var optgroup1 = document.createElement("optgroup");
optgroup1.appendChild(option1);
optgroup1.appendChild(option2);
select.add(optgroup1)
var optgroup2 = document.createElement("optgroup");
optgroup2.appendChild(option3);
select.add(optgroup2);
checkSelectedOptions(0, null);
option2.selected = true;
checkSelectedOptions(1, [option2]);
option3.selected = true;
checkSelectedOptions(2, [option2, option3]);
optgroup1.removeChild(option2);
checkSelectedOptions(1, [option3]);
document.body.removeChild(select);
option1.selected = true;
checkSelectedOptions(2, [option1, option3]);
</script>
</pre>
</body>
</html>

View File

@ -19,7 +19,7 @@
interface nsIDOMValidityState;
[scriptable, uuid(846578b2-6d4f-4399-86cc-2c05f19469d0)]
[scriptable, uuid(d8914a2d-3556-4b66-911c-a84c4394e7fa)]
interface nsIDOMHTMLSelectElement : nsISupports
{
attribute boolean autofocus;
@ -44,6 +44,7 @@ interface nsIDOMHTMLSelectElement : nsISupports
raises(DOMException);
void remove(in long index);
readonly attribute nsIDOMHTMLCollection selectedOptions;
attribute long selectedIndex;
attribute DOMString value;

View File

@ -38,7 +38,7 @@ interface HTMLSelectElement : HTMLElement {
[Throws]
setter creator void (unsigned long index, HTMLOptionElement? option);
// NYI: readonly attribute HTMLCollection selectedOptions;
readonly attribute HTMLCollection selectedOptions;
[SetterThrows, Pure]
attribute long selectedIndex;
[Pure]