CLOSED TREE Backed out changeset d66c3f19a210 (bug 1235261) Backed out changeset 467d945426bb (bug 1235261) Backed out changeset 32b61df13142 (bug 1235261) Backed out changeset c50bb8ed4196 (bug 1235261) Backed out changeset 0ff0fa6fe81f (bug 1235261) Backed out changeset df70e89669da (bug 1235261) Backed out changeset 064969357fc9 (bug 1235261)
381 lines
9.2 KiB
C++
381 lines
9.2 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "mozilla/dom/HTMLOptionsCollection.h"
|
|
|
|
#include "HTMLOptGroupElement.h"
|
|
#include "mozAutoDocUpdate.h"
|
|
#include "mozilla/dom/BindingUtils.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/HTMLOptionElement.h"
|
|
#include "mozilla/dom/HTMLOptionsCollectionBinding.h"
|
|
#include "mozilla/dom/HTMLSelectElement.h"
|
|
#include "nsContentCreatorFunctions.h"
|
|
#include "nsError.h"
|
|
#include "nsFormSubmission.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIComboboxControlFrame.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMHTMLOptGroupElement.h"
|
|
#include "nsIFormControlFrame.h"
|
|
#include "nsIForm.h"
|
|
#include "nsIFormProcessor.h"
|
|
#include "nsIListControlFrame.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsMappedAttributes.h"
|
|
#include "nsRuleData.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "jsfriendapi.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement* aSelect)
|
|
{
|
|
// Do not maintain a reference counted reference. When
|
|
// the select goes away, it will let us know.
|
|
mSelect = aSelect;
|
|
}
|
|
|
|
HTMLOptionsCollection::~HTMLOptionsCollection()
|
|
{
|
|
DropReference();
|
|
}
|
|
|
|
void
|
|
HTMLOptionsCollection::DropReference()
|
|
{
|
|
// Drop our (non ref-counted) reference
|
|
mSelect = nullptr;
|
|
}
|
|
|
|
nsresult
|
|
HTMLOptionsCollection::GetOptionIndex(Element* aOption,
|
|
int32_t aStartIndex,
|
|
bool aForward,
|
|
int32_t* aIndex)
|
|
{
|
|
// NOTE: aIndex shouldn't be set if the returned value isn't NS_OK.
|
|
|
|
int32_t index;
|
|
|
|
// Make the common case fast
|
|
if (aStartIndex == 0 && aForward) {
|
|
index = mElements.IndexOf(aOption);
|
|
if (index == -1) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aIndex = index;
|
|
return NS_OK;
|
|
}
|
|
|
|
int32_t high = mElements.Length();
|
|
int32_t step = aForward ? 1 : -1;
|
|
|
|
for (index = aStartIndex; index < high && index > -1; index += step) {
|
|
if (mElements[index] == aOption) {
|
|
*aIndex = index;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLOptionsCollection, mElements)
|
|
|
|
// nsISupports
|
|
|
|
// QueryInterface implementation for HTMLOptionsCollection
|
|
NS_INTERFACE_TABLE_HEAD(HTMLOptionsCollection)
|
|
NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
|
|
NS_INTERFACE_TABLE(HTMLOptionsCollection,
|
|
nsIHTMLCollection,
|
|
nsIDOMHTMLOptionsCollection,
|
|
nsIDOMHTMLCollection)
|
|
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLOptionsCollection)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLOptionsCollection)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLOptionsCollection)
|
|
|
|
|
|
JSObject*
|
|
HTMLOptionsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return HTMLOptionsCollectionBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::GetLength(uint32_t* aLength)
|
|
{
|
|
*aLength = mElements.Length();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::SetLength(uint32_t aLength)
|
|
{
|
|
if (!mSelect) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
return mSelect->SetLength(aLength);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::SetOption(uint32_t aIndex,
|
|
nsIDOMHTMLOptionElement* aOption)
|
|
{
|
|
if (!mSelect) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// if the new option is null, just remove this option. Note that it's safe
|
|
// to pass a too-large aIndex in here.
|
|
if (!aOption) {
|
|
mSelect->Remove(aIndex);
|
|
|
|
// We're done.
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
uint32_t index = uint32_t(aIndex);
|
|
|
|
// Now we're going to be setting an option in our collection
|
|
if (index > mElements.Length()) {
|
|
// Fill our array with blank options up to (but not including, since we're
|
|
// about to change it) aIndex, for compat with other browsers.
|
|
rv = SetLength(index);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
NS_ASSERTION(index <= mElements.Length(), "SetLength lied");
|
|
|
|
nsCOMPtr<nsIDOMNode> ret;
|
|
if (index == mElements.Length()) {
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aOption);
|
|
rv = mSelect->AppendChild(node, getter_AddRefs(ret));
|
|
} else {
|
|
// Find the option they're talking about and replace it
|
|
// hold a strong reference to follow COM rules.
|
|
RefPtr<HTMLOptionElement> refChild = ItemAsOption(index);
|
|
NS_ENSURE_TRUE(refChild, NS_ERROR_UNEXPECTED);
|
|
|
|
nsCOMPtr<nsINode> parent = refChild->GetParent();
|
|
if (parent) {
|
|
nsCOMPtr<nsINode> node = do_QueryInterface(aOption);
|
|
ErrorResult res;
|
|
parent->ReplaceChild(*node, *refChild, res);
|
|
rv = res.StealNSResult();
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int32_t
|
|
HTMLOptionsCollection::GetSelectedIndex(ErrorResult& aError)
|
|
{
|
|
if (!mSelect) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return 0;
|
|
}
|
|
|
|
int32_t selectedIndex;
|
|
aError = mSelect->GetSelectedIndex(&selectedIndex);
|
|
return selectedIndex;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::GetSelectedIndex(int32_t* aSelectedIndex)
|
|
{
|
|
ErrorResult rv;
|
|
*aSelectedIndex = GetSelectedIndex(rv);
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
void
|
|
HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex,
|
|
ErrorResult& aError)
|
|
{
|
|
if (!mSelect) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
aError = mSelect->SetSelectedIndex(aSelectedIndex);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex)
|
|
{
|
|
ErrorResult rv;
|
|
SetSelectedIndex(aSelectedIndex, rv);
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::Item(uint32_t aIndex, nsIDOMNode** aReturn)
|
|
{
|
|
nsISupports* item = GetElementAt(aIndex);
|
|
if (!item) {
|
|
*aReturn = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
return CallQueryInterface(item, aReturn);
|
|
}
|
|
|
|
Element*
|
|
HTMLOptionsCollection::GetElementAt(uint32_t aIndex)
|
|
{
|
|
return ItemAsOption(aIndex);
|
|
}
|
|
|
|
HTMLOptionElement*
|
|
HTMLOptionsCollection::NamedGetter(const nsAString& aName, bool& aFound)
|
|
{
|
|
uint32_t count = mElements.Length();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
HTMLOptionElement* content = mElements.ElementAt(i);
|
|
if (content &&
|
|
(content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aName,
|
|
eCaseMatters) ||
|
|
content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, aName,
|
|
eCaseMatters))) {
|
|
aFound = true;
|
|
return content;
|
|
}
|
|
}
|
|
|
|
aFound = false;
|
|
return nullptr;
|
|
}
|
|
|
|
nsINode*
|
|
HTMLOptionsCollection::GetParentObject()
|
|
{
|
|
return mSelect;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::NamedItem(const nsAString& aName,
|
|
nsIDOMNode** aReturn)
|
|
{
|
|
NS_IF_ADDREF(*aReturn = GetNamedItem(aName));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
HTMLOptionsCollection::GetSupportedNames(unsigned aFlags,
|
|
nsTArray<nsString>& aNames)
|
|
{
|
|
if (!(aFlags & JSITER_HIDDEN)) {
|
|
return;
|
|
}
|
|
|
|
nsAutoTArray<nsIAtom*, 8> atoms;
|
|
for (uint32_t i = 0; i < mElements.Length(); ++i) {
|
|
HTMLOptionElement* content = mElements.ElementAt(i);
|
|
if (content) {
|
|
// Note: HasName means the names is exposed on the document,
|
|
// which is false for options, so we don't check it here.
|
|
const nsAttrValue* val = content->GetParsedAttr(nsGkAtoms::name);
|
|
if (val && val->Type() == nsAttrValue::eAtom) {
|
|
nsIAtom* name = val->GetAtomValue();
|
|
if (!atoms.Contains(name)) {
|
|
atoms.AppendElement(name);
|
|
}
|
|
}
|
|
if (content->HasID()) {
|
|
nsIAtom* id = content->GetID();
|
|
if (!atoms.Contains(id)) {
|
|
atoms.AppendElement(id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t atomsLen = atoms.Length();
|
|
nsString* names = aNames.AppendElements(atomsLen);
|
|
for (uint32_t i = 0; i < atomsLen; ++i) {
|
|
atoms[i]->ToString(names[i]);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::GetSelect(nsIDOMHTMLSelectElement** aReturn)
|
|
{
|
|
NS_IF_ADDREF(*aReturn = mSelect);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::Add(nsIDOMHTMLOptionElement* aOption,
|
|
nsIVariant* aBefore)
|
|
{
|
|
if (!aOption) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
if (!mSelect) {
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMHTMLElement> elem = do_QueryInterface(aOption);
|
|
return mSelect->Add(elem, aBefore);
|
|
}
|
|
|
|
void
|
|
HTMLOptionsCollection::Add(const HTMLOptionOrOptGroupElement& aElement,
|
|
const Nullable<HTMLElementOrLong>& aBefore,
|
|
ErrorResult& aError)
|
|
{
|
|
if (!mSelect) {
|
|
aError.Throw(NS_ERROR_NOT_INITIALIZED);
|
|
return;
|
|
}
|
|
|
|
mSelect->Add(aElement, aBefore, aError);
|
|
}
|
|
|
|
void
|
|
HTMLOptionsCollection::Remove(int32_t aIndex, ErrorResult& aError)
|
|
{
|
|
if (!mSelect) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
uint32_t len = 0;
|
|
mSelect->GetLength(&len);
|
|
if (aIndex < 0 || (uint32_t)aIndex >= len)
|
|
aIndex = 0;
|
|
|
|
aError = mSelect->Remove(aIndex);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLOptionsCollection::Remove(int32_t aIndex)
|
|
{
|
|
ErrorResult rv;
|
|
Remove(aIndex, rv);
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|