mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 556743 - Implement the labels attribute. r=smaug
This commit is contained in:
parent
71ff52def2
commit
005cba50ea
@ -677,6 +677,9 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb,
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
|
||||
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mLabelsList");
|
||||
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mLabelsList));
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
|
||||
cb.NoteXPCOMChild(mClassList.get());
|
||||
|
||||
@ -711,6 +714,7 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
|
||||
mShadowRoot = nullptr;
|
||||
mContainingShadow = nullptr;
|
||||
mChildrenList = nullptr;
|
||||
mLabelsList = nullptr;
|
||||
mCustomElementData = nullptr;
|
||||
mClassList = nullptr;
|
||||
mRegisteredIntersectionObservers.Clear();
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
class ContentUnbinder;
|
||||
class nsContentList;
|
||||
class nsLabelsNodeList;
|
||||
class nsDOMAttributeMap;
|
||||
class nsDOMTokenList;
|
||||
class nsIControllers;
|
||||
@ -313,6 +314,11 @@ public:
|
||||
*/
|
||||
RefPtr<nsDOMTokenList> mClassList;
|
||||
|
||||
/*
|
||||
* An object implementing the .labels property for this element.
|
||||
*/
|
||||
RefPtr<nsLabelsNodeList> mLabelsList;
|
||||
|
||||
/**
|
||||
* ShadowRoot bound to the element.
|
||||
*/
|
||||
|
@ -253,19 +253,6 @@ const nsCacheableFuncStringContentList::ContentListType
|
||||
nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
|
||||
#endif
|
||||
|
||||
JSObject*
|
||||
nsCacheableFuncStringNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return NodeListBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
|
||||
JSObject*
|
||||
nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
// Hashtable for storing nsCacheableFuncStringContentList
|
||||
static PLDHashTable* gFuncStringContentListHashTable;
|
||||
|
||||
@ -378,6 +365,7 @@ NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
|
||||
aString);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// nsContentList implementation
|
||||
|
||||
nsContentList::nsContentList(nsINode* aRootNode,
|
||||
@ -659,7 +647,7 @@ nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement,
|
||||
const nsAttrValue* aOldValue)
|
||||
{
|
||||
NS_PRECONDITION(aElement, "Must have a content node to work with");
|
||||
|
||||
|
||||
if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
|
||||
!MayContainRelevantNodes(aElement->GetParentNode()) ||
|
||||
!nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
|
||||
@ -805,7 +793,7 @@ nsContentList::ContentInserted(nsIDocument *aDocument,
|
||||
|
||||
ASSERT_IN_SYNC;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsContentList::ContentRemoved(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
@ -1074,3 +1062,126 @@ nsContentList::AssertInSync()
|
||||
NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------
|
||||
// nsCacheableFuncStringNodeList
|
||||
|
||||
JSObject*
|
||||
nsCacheableFuncStringNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return NodeListBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// nsCacheableFuncStringHTMLCollection
|
||||
|
||||
JSObject*
|
||||
nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// nsLabelsNodeList
|
||||
|
||||
JSObject*
|
||||
nsLabelsNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return NodeListBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::AttributeChanged(nsIDocument* aDocument, Element* aElement,
|
||||
int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
int32_t aModType,
|
||||
const nsAttrValue* aOldValue)
|
||||
{
|
||||
MOZ_ASSERT(aElement, "Must have a content node to work with");
|
||||
if (mState == LIST_DIRTY ||
|
||||
!nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to handle input type changes to or from "hidden".
|
||||
if (aElement->IsHTMLElement(nsGkAtoms::input) &&
|
||||
aAttribute == nsGkAtoms::type && aNameSpaceID == kNameSpaceID_None) {
|
||||
SetDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::ContentAppended(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
int32_t aNewIndexInContainer)
|
||||
{
|
||||
// If a labelable element is moved to outside or inside of
|
||||
// nested associated labels, we're gonna have to modify
|
||||
// the content list.
|
||||
if (mState != LIST_DIRTY ||
|
||||
nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer)) {
|
||||
SetDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::ContentInserted(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
int32_t aIndexInContainer)
|
||||
{
|
||||
// If a labelable element is moved to outside or inside of
|
||||
// nested associated labels, we're gonna have to modify
|
||||
// the content list.
|
||||
if (mState != LIST_DIRTY ||
|
||||
nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
|
||||
SetDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::ContentRemoved(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
int32_t aIndexInContainer,
|
||||
nsIContent* aPreviousSibling)
|
||||
{
|
||||
// If a labelable element is removed, we're gonna have to clean
|
||||
// the content list.
|
||||
if (mState != LIST_DIRTY ||
|
||||
nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
|
||||
SetDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::MaybeResetRoot(nsINode* aRootNode)
|
||||
{
|
||||
MOZ_ASSERT(aRootNode, "Must have root");
|
||||
if (mRootNode == aRootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRootNode->RemoveMutationObserver(this);
|
||||
mRootNode = aRootNode;
|
||||
mRootNode->AddMutationObserver(this);
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
void
|
||||
nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength)
|
||||
{
|
||||
MOZ_ASSERT(mRootNode, "Must have root");
|
||||
|
||||
// Start searching at the root.
|
||||
nsINode* cur = mRootNode;
|
||||
if (mElements.IsEmpty() && cur->IsElement() && Match(cur->AsElement())) {
|
||||
mElements.AppendElement(cur->AsElement());
|
||||
}
|
||||
|
||||
nsContentList::PopulateSelf(aNeededLength);
|
||||
}
|
||||
|
@ -376,9 +376,9 @@ protected:
|
||||
* traversed the whole document (or both).
|
||||
*
|
||||
* @param aNeededLength the length the list should have when we are
|
||||
* done (unless it exhausts the document)
|
||||
* done (unless it exhausts the document)
|
||||
*/
|
||||
void PopulateSelf(uint32_t aNeededLength);
|
||||
virtual void PopulateSelf(uint32_t aNeededLength);
|
||||
|
||||
/**
|
||||
* @param aContainer a content node which must be a descendant of
|
||||
@ -589,4 +589,40 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
class nsLabelsNodeList final : public nsContentList
|
||||
{
|
||||
public:
|
||||
nsLabelsNodeList(nsINode* aRootNode,
|
||||
nsContentListMatchFunc aFunc,
|
||||
nsContentListDestroyFunc aDestroyFunc,
|
||||
void* aData)
|
||||
: nsContentList(aRootNode, aFunc, aDestroyFunc, aData)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
/**
|
||||
* Reset root, mutation observer, and clear content list
|
||||
* if the root has been changed.
|
||||
*
|
||||
* @param aRootNode The node under which to limit our search.
|
||||
*/
|
||||
void MaybeResetRoot(nsINode* aRootNode);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Start searching at the last one if we already have nodes, otherwise
|
||||
* start searching at the root.
|
||||
*
|
||||
* @param aNeededLength The list of length should have when we are
|
||||
* done (unless it exhausts the document).
|
||||
*/
|
||||
void PopulateSelf(uint32_t aNeededLength) override;
|
||||
};
|
||||
#endif // nsContentList_h___
|
||||
|
@ -598,6 +598,7 @@ GK_ATOM(keytext, "keytext")
|
||||
GK_ATOM(keyup, "keyup")
|
||||
GK_ATOM(kind, "kind")
|
||||
GK_ATOM(label, "label")
|
||||
GK_ATOM(labels, "labels")
|
||||
GK_ATOM(lang, "lang")
|
||||
GK_ATOM(language, "language")
|
||||
GK_ATOM(last, "last")
|
||||
|
@ -8157,6 +8157,16 @@ HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<FileSystemEntry>>& aSequence)
|
||||
aSequence.AppendElements(mFileData->mEntries);
|
||||
}
|
||||
|
||||
already_AddRefed<nsINodeList>
|
||||
HTMLInputElement::GetLabels()
|
||||
{
|
||||
if (!IsLabelable()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::Labels();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -735,6 +735,8 @@ public:
|
||||
|
||||
// XPCOM GetCustomVisibility() is OK
|
||||
|
||||
already_AddRefed<nsINodeList> GetLabels();
|
||||
|
||||
// XPCOM Select() is OK
|
||||
|
||||
Nullable<uint32_t> GetSelectionStart(ErrorResult& aRv);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIDOMMouseEvent.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
// construction, destruction
|
||||
|
||||
@ -268,17 +269,23 @@ HTMLLabelElement::GetLabeledElement() const
|
||||
return GetFirstLabelableDescendant();
|
||||
}
|
||||
|
||||
// We have a @for. The id has to be linked to an element in the same document
|
||||
// We have a @for. The id has to be linked to an element in the same tree
|
||||
// and this element should be a labelable form control.
|
||||
//XXXsmaug It is unclear how this should work in case the element is in
|
||||
// Shadow DOM.
|
||||
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365.
|
||||
nsIDocument* doc = GetUncomposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
nsINode* root = SubtreeRoot();
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(root);
|
||||
Element* element = nullptr;
|
||||
|
||||
if (shadow) {
|
||||
element = shadow->GetElementById(elementId);
|
||||
} else {
|
||||
nsIDocument* doc = GetUncomposedDoc();
|
||||
if (doc) {
|
||||
element = doc->GetElementById(elementId);
|
||||
} else {
|
||||
element = nsContentUtils::MatchElementId(root->AsContent(), elementId);
|
||||
}
|
||||
}
|
||||
|
||||
Element* element = doc->GetElementById(elementId);
|
||||
if (element && element->IsLabelable()) {
|
||||
return static_cast<nsGenericHTMLElement*>(element);
|
||||
}
|
||||
|
@ -107,6 +107,7 @@
|
||||
#include "mozilla/StyleSetHandle.h"
|
||||
#include "mozilla/StyleSetHandleInlines.h"
|
||||
#include "ReferrerPolicy.h"
|
||||
#include "mozilla/dom/HTMLLabelElement.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -498,6 +499,14 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
}
|
||||
}
|
||||
|
||||
// We need to consider a labels element is moved to another subtree
|
||||
// with different root, it needs to update labels list and its root
|
||||
// as well.
|
||||
nsDOMSlots* slots = GetExistingDOMSlots();
|
||||
if (slots && slots->mLabelsList) {
|
||||
slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -518,6 +527,13 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
}
|
||||
|
||||
// We need to consider a labels element is removed from tree,
|
||||
// it needs to update labels list and its root as well.
|
||||
nsDOMSlots* slots = GetExistingDOMSlots();
|
||||
if (slots && slots->mLabelsList) {
|
||||
slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
|
||||
}
|
||||
|
||||
nsStyledElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
||||
@ -1666,6 +1682,30 @@ nsGenericHTMLElement::IsLabelable() const
|
||||
return IsAnyOfHTMLElements(nsGkAtoms::progress, nsGkAtoms::meter);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsGenericHTMLElement::MatchLabelsElement(Element* aElement, int32_t aNamespaceID,
|
||||
nsIAtom* aAtom, void* aData)
|
||||
{
|
||||
HTMLLabelElement* element = HTMLLabelElement::FromContent(aElement);
|
||||
return element && element->GetControl() == aData;
|
||||
}
|
||||
|
||||
already_AddRefed<nsINodeList>
|
||||
nsGenericHTMLElement::Labels()
|
||||
{
|
||||
MOZ_ASSERT(IsLabelable(),
|
||||
"Labels() only allow labelable elements to use it.");
|
||||
nsDOMSlots* slots = DOMSlots();
|
||||
|
||||
if (!slots->mLabelsList) {
|
||||
slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement,
|
||||
nullptr, this);
|
||||
}
|
||||
|
||||
RefPtr<nsLabelsNodeList> labels = slots->mLabelsList;
|
||||
return labels.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
nsGenericHTMLElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
||||
{
|
||||
|
@ -851,6 +851,12 @@ public:
|
||||
}
|
||||
|
||||
virtual bool IsLabelable() const override;
|
||||
|
||||
static bool MatchLabelsElement(Element* aElement, int32_t aNamespaceID,
|
||||
nsIAtom* aAtom, void* aData);
|
||||
|
||||
already_AddRefed<nsINodeList> Labels();
|
||||
|
||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
||||
|
||||
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
|
||||
|
@ -43,6 +43,5 @@ interface HTMLButtonElement : HTMLElement {
|
||||
boolean reportValidity();
|
||||
void setCustomValidity(DOMString error);
|
||||
|
||||
// Not yet implemented:
|
||||
// readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
};
|
||||
|
@ -112,7 +112,7 @@ interface HTMLInputElement : HTMLElement {
|
||||
boolean reportValidity();
|
||||
void setCustomValidity(DOMString error);
|
||||
|
||||
// Bug 850365 readonly attribute NodeList labels;
|
||||
readonly attribute NodeList? labels;
|
||||
|
||||
void select();
|
||||
|
||||
|
@ -26,9 +26,5 @@ interface HTMLMeterElement : HTMLElement {
|
||||
attribute double high;
|
||||
[SetterThrows]
|
||||
attribute double optimum;
|
||||
|
||||
/**
|
||||
* The labels attribute will be done with bug 556743.
|
||||
*/
|
||||
//readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
};
|
||||
|
@ -34,6 +34,5 @@ interface HTMLOutputElement : HTMLElement {
|
||||
boolean reportValidity();
|
||||
void setCustomValidity(DOMString error);
|
||||
|
||||
// Not yet implemented (bug 556743).
|
||||
// readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
};
|
||||
|
@ -18,9 +18,5 @@ interface HTMLProgressElement : HTMLElement {
|
||||
[SetterThrows]
|
||||
attribute double max;
|
||||
readonly attribute double position;
|
||||
|
||||
/**
|
||||
* The labels attribute will be done with bug 567740.
|
||||
*/
|
||||
//readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
};
|
||||
|
@ -54,7 +54,7 @@ interface HTMLSelectElement : HTMLElement {
|
||||
boolean reportValidity();
|
||||
void setCustomValidity(DOMString error);
|
||||
|
||||
// NYI: readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=20720
|
||||
void remove();
|
||||
|
@ -58,7 +58,7 @@ interface HTMLTextAreaElement : HTMLElement {
|
||||
boolean reportValidity();
|
||||
void setCustomValidity(DOMString error);
|
||||
|
||||
// readonly attribute NodeList labels;
|
||||
readonly attribute NodeList labels;
|
||||
|
||||
void select();
|
||||
[Throws]
|
||||
|
Loading…
Reference in New Issue
Block a user