mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-07 15:57:31 +09:00
HTML - implement the labels attribute
This commit is contained in:
parent
91e5a3a71e
commit
5b24f05ce7
25 changed files with 516 additions and 95 deletions
|
|
@ -574,6 +574,9 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb,
|
||||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
|
||||||
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, 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");
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
|
||||||
cb.NoteXPCOMChild(mClassList.get());
|
cb.NoteXPCOMChild(mClassList.get());
|
||||||
|
|
||||||
|
|
@ -602,6 +605,7 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
|
||||||
mShadowRoot = nullptr;
|
mShadowRoot = nullptr;
|
||||||
mContainingShadow = nullptr;
|
mContainingShadow = nullptr;
|
||||||
mChildrenList = nullptr;
|
mChildrenList = nullptr;
|
||||||
|
mLabelsList = nullptr;
|
||||||
mCustomElementData = nullptr;
|
mCustomElementData = nullptr;
|
||||||
mClassList = nullptr;
|
mClassList = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
class ContentUnbinder;
|
class ContentUnbinder;
|
||||||
class nsContentList;
|
class nsContentList;
|
||||||
|
class nsLabelsNodeList;
|
||||||
class nsDOMAttributeMap;
|
class nsDOMAttributeMap;
|
||||||
class nsDOMTokenList;
|
class nsDOMTokenList;
|
||||||
class nsIControllers;
|
class nsIControllers;
|
||||||
|
|
@ -313,6 +314,11 @@ public:
|
||||||
*/
|
*/
|
||||||
RefPtr<nsDOMTokenList> mClassList;
|
RefPtr<nsDOMTokenList> mClassList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An object implementing the .labels property for this element.
|
||||||
|
*/
|
||||||
|
RefPtr<nsLabelsNodeList> mLabelsList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ShadowRoot bound to the element.
|
* ShadowRoot bound to the element.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -254,19 +254,6 @@ const nsCacheableFuncStringContentList::ContentListType
|
||||||
nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
|
nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
|
||||||
#endif
|
#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
|
// Hashtable for storing nsCacheableFuncStringContentList
|
||||||
static PLDHashTable* gFuncStringContentListHashTable;
|
static PLDHashTable* gFuncStringContentListHashTable;
|
||||||
|
|
||||||
|
|
@ -379,6 +366,7 @@ NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
|
||||||
aString);
|
aString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
// nsContentList implementation
|
// nsContentList implementation
|
||||||
|
|
||||||
nsContentList::nsContentList(nsINode* aRootNode,
|
nsContentList::nsContentList(nsINode* aRootNode,
|
||||||
|
|
@ -660,7 +648,7 @@ nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement,
|
||||||
const nsAttrValue* aOldValue)
|
const nsAttrValue* aOldValue)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(aElement, "Must have a content node to work with");
|
NS_PRECONDITION(aElement, "Must have a content node to work with");
|
||||||
|
|
||||||
if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
|
if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
|
||||||
!MayContainRelevantNodes(aElement->GetParentNode()) ||
|
!MayContainRelevantNodes(aElement->GetParentNode()) ||
|
||||||
!nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
|
!nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
|
||||||
|
|
@ -806,7 +794,7 @@ nsContentList::ContentInserted(nsIDocument *aDocument,
|
||||||
|
|
||||||
ASSERT_IN_SYNC;
|
ASSERT_IN_SYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsContentList::ContentRemoved(nsIDocument *aDocument,
|
nsContentList::ContentRemoved(nsIDocument *aDocument,
|
||||||
nsIContent* aContainer,
|
nsIContent* aContainer,
|
||||||
|
|
@ -1075,3 +1063,128 @@ nsContentList::AssertInSync()
|
||||||
NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
|
NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mRootNode) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -371,9 +371,9 @@ protected:
|
||||||
* traversed the whole document (or both).
|
* traversed the whole document (or both).
|
||||||
*
|
*
|
||||||
* @param aNeededLength the length the list should have when we are
|
* @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
|
* @param aContainer a content node which must be a descendant of
|
||||||
|
|
@ -584,4 +584,40 @@ public:
|
||||||
#endif
|
#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___
|
#endif // nsContentList_h___
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,7 @@ GK_ATOM(keytext, "keytext")
|
||||||
GK_ATOM(keyup, "keyup")
|
GK_ATOM(keyup, "keyup")
|
||||||
GK_ATOM(kind, "kind")
|
GK_ATOM(kind, "kind")
|
||||||
GK_ATOM(label, "label")
|
GK_ATOM(label, "label")
|
||||||
|
GK_ATOM(labels, "labels")
|
||||||
GK_ATOM(lang, "lang")
|
GK_ATOM(lang, "lang")
|
||||||
GK_ATOM(language, "language")
|
GK_ATOM(language, "language")
|
||||||
GK_ATOM(last, "last")
|
GK_ATOM(last, "last")
|
||||||
|
|
|
||||||
33
dom/base/test/test_bug1375050.html
Normal file
33
dom/base/test/test_bug1375050.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=1375050
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 1375050</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
<script type="application/javascript">
|
||||||
|
try { o1 = document.createElement('input'); } catch(e) { console.log(e); };
|
||||||
|
try { o2 = document.createElement('col'); } catch(e) { console.log(e); };
|
||||||
|
try { o4 = document.createRange(); } catch(e) { console.log(e); };
|
||||||
|
try { document.documentElement.appendChild(o1); } catch(e) { console.log(e); };
|
||||||
|
try { for (let p in o1) { let x = o1[p] }; } catch(e) { console.log(e); };
|
||||||
|
try { o4.selectNode(o1); } catch(e) { console.log(e); };
|
||||||
|
try { o6 = document.createComment(" x"); } catch(e) { console.log(e); }
|
||||||
|
try { o4.surroundContents(o6); } catch(e) { console.log(e); }
|
||||||
|
try { o7 = document.implementation.createDocument('', '', null).adoptNode(o1); } catch(e) { console.log(e);};
|
||||||
|
try { o2.appendChild(o1); } catch(e) { console.log(e); };
|
||||||
|
ok(true, "Didn't crash.");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375050">Mozilla Bug 1375050</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -8800,6 +8800,16 @@ HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<FileSystemEntry>>& aSequence)
|
||||||
aSequence.AppendElements(mEntries);
|
aSequence.AppendElements(mEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsINodeList>
|
||||||
|
HTMLInputElement::GetLabels()
|
||||||
|
{
|
||||||
|
if (!IsLabelable()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsGenericHTMLElement::Labels();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -704,6 +704,8 @@ public:
|
||||||
|
|
||||||
// XPCOM GetCustomVisibility() is OK
|
// XPCOM GetCustomVisibility() is OK
|
||||||
|
|
||||||
|
already_AddRefed<nsINodeList> GetLabels();
|
||||||
|
|
||||||
// XPCOM Select() is OK
|
// XPCOM Select() is OK
|
||||||
|
|
||||||
Nullable<int32_t> GetSelectionStart(ErrorResult& aRv);
|
Nullable<int32_t> GetSelectionStart(ErrorResult& aRv);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "nsFocusManager.h"
|
#include "nsFocusManager.h"
|
||||||
#include "nsIDOMMouseEvent.h"
|
#include "nsIDOMMouseEvent.h"
|
||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
|
|
||||||
// construction, destruction
|
// construction, destruction
|
||||||
|
|
||||||
|
|
@ -268,17 +269,23 @@ HTMLLabelElement::GetLabeledElement() const
|
||||||
return GetFirstLabelableDescendant();
|
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.
|
// and this element should be a labelable form control.
|
||||||
//XXXsmaug It is unclear how this should work in case the element is in
|
nsINode* root = SubtreeRoot();
|
||||||
// Shadow DOM.
|
ShadowRoot* shadow = ShadowRoot::FromNode(root);
|
||||||
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365.
|
Element* element = nullptr;
|
||||||
nsIDocument* doc = GetUncomposedDoc();
|
|
||||||
if (!doc) {
|
if (shadow) {
|
||||||
return nullptr;
|
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()) {
|
if (element && element->IsLabelable()) {
|
||||||
return static_cast<nsGenericHTMLElement*>(element);
|
return static_cast<nsGenericHTMLElement*>(element);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@
|
||||||
#include "mozilla/StyleSetHandle.h"
|
#include "mozilla/StyleSetHandle.h"
|
||||||
#include "mozilla/StyleSetHandleInlines.h"
|
#include "mozilla/StyleSetHandleInlines.h"
|
||||||
#include "ReferrerPolicy.h"
|
#include "ReferrerPolicy.h"
|
||||||
|
#include "mozilla/dom/HTMLLabelElement.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
@ -493,6 +494,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;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -513,6 +522,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);
|
nsStyledElement::UnbindFromTree(aDeep, aNullParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1701,6 +1717,30 @@ nsGenericHTMLElement::IsLabelable() const
|
||||||
return IsAnyOfHTMLElements(nsGkAtoms::progress, nsGkAtoms::meter);
|
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
|
bool
|
||||||
nsGenericHTMLElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
nsGenericHTMLElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -834,6 +834,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool IsLabelable() const override;
|
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;
|
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
||||||
|
|
||||||
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
|
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
|
||||||
|
|
|
||||||
|
|
@ -128,9 +128,12 @@ is(typeof(document.createElement("button").setCustomValidity), "function",
|
||||||
"button.setCustomValidity should be a function");
|
"button.setCustomValidity should be a function");
|
||||||
|
|
||||||
// .labels
|
// .labels
|
||||||
todo("labels" in document.createElement("button"),
|
ok("labels" in document.createElement("button"),
|
||||||
"button.labels isn't implemented yet");
|
"button.labels should be an IDL attribute of the button element");
|
||||||
|
is(typeof(document.createElement("button").labels), "object",
|
||||||
|
"button.labels should be an object");
|
||||||
|
ok(document.createElement("button").labels instanceof NodeList,
|
||||||
|
"button.labels sohuld be an instance of NodeList");
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,5 @@ interface HTMLButtonElement : HTMLElement {
|
||||||
boolean reportValidity();
|
boolean reportValidity();
|
||||||
void setCustomValidity(DOMString error);
|
void setCustomValidity(DOMString error);
|
||||||
|
|
||||||
// Not yet implemented:
|
readonly attribute NodeList labels;
|
||||||
// readonly attribute NodeList labels;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ interface HTMLInputElement : HTMLElement {
|
||||||
boolean reportValidity();
|
boolean reportValidity();
|
||||||
void setCustomValidity(DOMString error);
|
void setCustomValidity(DOMString error);
|
||||||
|
|
||||||
// Bug 850365 readonly attribute NodeList labels;
|
readonly attribute NodeList? labels;
|
||||||
|
|
||||||
void select();
|
void select();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,5 @@ interface HTMLMeterElement : HTMLElement {
|
||||||
[SetterThrows]
|
[SetterThrows]
|
||||||
attribute double optimum;
|
attribute double optimum;
|
||||||
|
|
||||||
/**
|
readonly attribute NodeList labels;
|
||||||
* The labels attribute will be done with bug 556743.
|
|
||||||
*/
|
|
||||||
//readonly attribute NodeList labels;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,5 @@ interface HTMLOutputElement : HTMLElement {
|
||||||
boolean reportValidity();
|
boolean reportValidity();
|
||||||
void setCustomValidity(DOMString error);
|
void setCustomValidity(DOMString error);
|
||||||
|
|
||||||
// Not yet implemented (bug 556743).
|
readonly attribute NodeList labels;
|
||||||
// readonly attribute NodeList labels;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,5 @@ interface HTMLProgressElement : HTMLElement {
|
||||||
[SetterThrows]
|
[SetterThrows]
|
||||||
attribute double max;
|
attribute double max;
|
||||||
readonly attribute double position;
|
readonly attribute double position;
|
||||||
|
readonly attribute NodeList labels;
|
||||||
/**
|
|
||||||
* The labels attribute will be done with bug 567740.
|
|
||||||
*/
|
|
||||||
//readonly attribute NodeList labels;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ interface HTMLSelectElement : HTMLElement {
|
||||||
boolean reportValidity();
|
boolean reportValidity();
|
||||||
void setCustomValidity(DOMString error);
|
void setCustomValidity(DOMString error);
|
||||||
|
|
||||||
// NYI: readonly attribute NodeList labels;
|
readonly attribute NodeList labels;
|
||||||
|
|
||||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=20720
|
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=20720
|
||||||
void remove();
|
void remove();
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ interface HTMLTextAreaElement : HTMLElement {
|
||||||
boolean reportValidity();
|
boolean reportValidity();
|
||||||
void setCustomValidity(DOMString error);
|
void setCustomValidity(DOMString error);
|
||||||
|
|
||||||
// readonly attribute NodeList labels;
|
readonly attribute NodeList labels;
|
||||||
|
|
||||||
void select();
|
void select();
|
||||||
[Throws]
|
[Throws]
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
[label-attributes.html]
|
|
||||||
type: testharness
|
|
||||||
[A non-control follows by a control with same ID.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A form control has multiple labels.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A form control has no label 1.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A form control has no label 2.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A form control has an implicit label.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
@ -1,29 +1,7 @@
|
||||||
[labelable-elements.html]
|
[labelable-elements.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[Check if the output element can access 'labels']
|
[Check if the keygen element is not a labelable element]
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the progress element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the select element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the textarea element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the button element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the hidden input element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Check if the input element in radio state can access 'labels']
|
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Check if the keygen element can access 'labels']
|
[Check if the keygen element can access 'labels']
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Check if the meter element can access 'labels']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
[button_labels.html]
|
|
||||||
type: testharness
|
|
||||||
[Forms]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
[input_labels.html]
|
|
||||||
type: testharness
|
|
||||||
[Forms]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<label>
|
||||||
|
<div id="div1"></div>
|
||||||
|
</label>
|
||||||
|
<label for="test13"></label>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -32,10 +32,53 @@
|
||||||
|
|
||||||
<label id="lbl5" for="test7"></label>
|
<label id="lbl5" for="test7"></label>
|
||||||
<input id="test7">
|
<input id="test7">
|
||||||
|
|
||||||
|
<label id="lbl7">
|
||||||
|
<label id="lbl8">
|
||||||
|
<div id="div1">
|
||||||
|
<input id="test8">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
<div id="div2"></div>
|
||||||
|
|
||||||
|
<label id="lbl9">
|
||||||
|
<label id="lbl10" for="test10">
|
||||||
|
<div id="div3">
|
||||||
|
<input id="test9">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
<div id="div4"><input id="test10"></div>
|
||||||
|
|
||||||
|
<label id="lbl11">
|
||||||
|
<object id="obj">
|
||||||
|
<input id="test11">
|
||||||
|
<input id="test12">
|
||||||
|
</object>
|
||||||
|
</label>
|
||||||
|
<label id="lbl12" for="test12"><div id="div5"></div></label>
|
||||||
|
|
||||||
|
<label id="lbl13">
|
||||||
|
<p id="p1">
|
||||||
|
<input id="test13">
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div id="div6">
|
||||||
|
<div id="div7">
|
||||||
|
<label id="lbl14">
|
||||||
|
<label id="lbl15" for="test15">
|
||||||
|
<input id="test14">
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input id="test15">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<label id="lbl6" for="test7"></label>
|
<label id="lbl6" for="test7"></label>
|
||||||
|
<div id="content" style="display: none">
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
//control attribute
|
//control attribute
|
||||||
|
|
@ -57,6 +100,7 @@
|
||||||
}, "A label element not in a document can not label any element in the document.");
|
}, "A label element not in a document can not label any element in the document.");
|
||||||
|
|
||||||
test(function () {
|
test(function () {
|
||||||
|
var labels = document.getElementById("test3").labels;
|
||||||
assert_equals(document.getElementById("lbl1").control, document.getElementById("test3"),
|
assert_equals(document.getElementById("lbl1").control, document.getElementById("test3"),
|
||||||
"The first labelable descendant of a label element should be its labeled control.");
|
"The first labelable descendant of a label element should be its labeled control.");
|
||||||
|
|
||||||
|
|
@ -64,6 +108,10 @@
|
||||||
document.getElementById("lbl1").insertBefore(input, document.getElementById("test2"));
|
document.getElementById("lbl1").insertBefore(input, document.getElementById("test2"));
|
||||||
assert_equals(document.getElementById("lbl1").control, input,
|
assert_equals(document.getElementById("lbl1").control, input,
|
||||||
"The first labelable descendant of a label element in tree order should be its labeled control.");
|
"The first labelable descendant of a label element in tree order should be its labeled control.");
|
||||||
|
assert_equals(input.labels.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
|
||||||
|
assert_equals(labels.length, 0,
|
||||||
|
"The number of labels should be 0 if it's not the first labelable descendant of a label element.");
|
||||||
input.remove();
|
input.remove();
|
||||||
}, "The labeled control for a label element that has no 'for' attribute is the first labelable element which is a descendant of that label element.");
|
}, "The labeled control for a label element that has no 'for' attribute is the first labelable element which is a descendant of that label element.");
|
||||||
|
|
||||||
|
|
@ -100,6 +148,168 @@
|
||||||
newLabel.remove();
|
newLabel.remove();
|
||||||
}, "A form control has multiple labels.");
|
}, "A form control has multiple labels.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
var labels = document.getElementById("test8").labels;
|
||||||
|
assert_true(labels instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(labels.length, 2,
|
||||||
|
"The form control has two ancestors with no explicit associated label, and is the first labelable descendant.");
|
||||||
|
assert_array_equals(labels, [document.getElementById("lbl7"), document.getElementById("lbl8")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
|
||||||
|
document.getElementById('div2').insertBefore(document.getElementById('div1'), document.getElementById('div2').firstChild);
|
||||||
|
assert_equals(labels.length, 0,
|
||||||
|
"The number of labels should be 0 after the labelable element is moved to outside of nested associated labels.");
|
||||||
|
}, "A labelable element is moved to outside of nested associated labels.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
var labels1 = document.getElementById("test9").labels;
|
||||||
|
var labels2 = document.getElementById("test10").labels;
|
||||||
|
assert_true(labels1 instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_true(labels2 instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(labels1.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
|
||||||
|
assert_equals(labels2.length, 1,
|
||||||
|
"The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
|
||||||
|
assert_array_equals(labels1, [document.getElementById("lbl9")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
assert_array_equals(labels2, [document.getElementById("lbl10")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
document.getElementById('div3').insertBefore(document.getElementById('div4'), document.getElementById('div3').firstChild);
|
||||||
|
assert_equals(labels1.length, 0,
|
||||||
|
"The number of labels should be 0 if it's not the first labelable descendant of a label element.");
|
||||||
|
assert_equals(labels2.length, 2,
|
||||||
|
"The form control has an ancestor with an explicit associated label, and is the first labelable descendant.");
|
||||||
|
}, "A labelable element is moved to inside of nested associated labels.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
var labels1 = document.getElementById("test11").labels;
|
||||||
|
var labels2 = document.getElementById("test12").labels;
|
||||||
|
assert_true(labels1 instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_true(labels2 instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(labels1.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and it is the first labelable descendant.");
|
||||||
|
assert_equals(labels2.length, 1,
|
||||||
|
"The number of labels should be 1 since there is a label with a 'for' attribute associated with this labelable element.");
|
||||||
|
assert_array_equals(labels1, [document.getElementById("lbl11")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
assert_array_equals(labels2, [document.getElementById("lbl12")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
document.getElementById('div5').appendChild(document.getElementById('obj'));
|
||||||
|
assert_equals(labels1.length, 0,
|
||||||
|
"The number of labels should be 0 after the labelable element is moved to outside of associated label.");
|
||||||
|
assert_equals(labels2.length, 1,
|
||||||
|
"The number of labels should be 1 after the labelable element is moved to outside of associated label.");
|
||||||
|
}, "A labelable element which is a descendant of non-labelable element is moved to outside of associated label.");
|
||||||
|
|
||||||
|
async_test(function () {
|
||||||
|
var labels = document.getElementById("test13").labels;
|
||||||
|
assert_true(labels instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(labels.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
|
||||||
|
assert_array_equals(labels, [document.getElementById("lbl13")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
let iframe = document.createElement('iframe');
|
||||||
|
|
||||||
|
iframe.onload = this.step_func_done(() => {
|
||||||
|
iframe.contentWindow.document.getElementById("div1").appendChild(document.getElementById("p1"));
|
||||||
|
assert_equals(labels.length, 2,
|
||||||
|
"The number of labels should be 2 after the labelable element is moved to iframe.");
|
||||||
|
});
|
||||||
|
|
||||||
|
iframe.setAttribute('src', 'http://web-platform.test:8000/html/semantics/forms/the-label-element/iframe-label-attributes.html');
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
}, "A labelable element is moved to iframe.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
var labels1 = document.getElementById("test14").labels;
|
||||||
|
var labels2 = document.getElementById("test15").labels;
|
||||||
|
assert_true(labels1 instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(labels1.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
|
||||||
|
assert_equals(labels2.length, 1,
|
||||||
|
"The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
|
||||||
|
assert_array_equals(labels1, [document.getElementById("lbl14")],
|
||||||
|
"The labels for a form control should be returned in tree order.");
|
||||||
|
|
||||||
|
document.getElementById('div6').removeChild(document.getElementById('div7'));
|
||||||
|
assert_equals(labels1.length, 0,
|
||||||
|
"The number of labels should be 0 after the labelable element is removed.");
|
||||||
|
assert_equals(labels2.length, 0,
|
||||||
|
"The number of labels should be 0 since there is no label with a 'for' attribute associated with this labelable element.");
|
||||||
|
}, "A div element which contains labelable element is removed.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
// <label><input id="test16"><label for="test16"></label></label>
|
||||||
|
var label1 = document.createElement('label');
|
||||||
|
label1.innerHTML = "<input id='test16'>";
|
||||||
|
var label2 = document.createElement('label');
|
||||||
|
label2.htmlFor = "test16";
|
||||||
|
label1.appendChild(label2);
|
||||||
|
|
||||||
|
var input = label1.firstChild;
|
||||||
|
var labels = input.labels;
|
||||||
|
|
||||||
|
assert_equals(labels.length, 2,
|
||||||
|
"The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
|
||||||
|
assert_true(labels instanceof NodeList,
|
||||||
|
"A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
assert_equals(label1.control, input, "The first labelable descendant of a label element should be its labeled control.");
|
||||||
|
assert_equals(label2.control, input, "The labeled cotrol should be associated with the control whose ID is equal to the value of the 'for' attribute.");
|
||||||
|
}, "A labelable element not in a document can label element in the same tree.");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
var isShadowDOMV0;
|
||||||
|
if ("createShadowRoot" in document.getElementById('content')) {
|
||||||
|
isShadowDOMV0 = true;
|
||||||
|
}
|
||||||
|
var root1;
|
||||||
|
if (isShadowDOMV0) {
|
||||||
|
root1 = document.getElementById('content').createShadowRoot();
|
||||||
|
} else {
|
||||||
|
root1 = document.getElementById('content').attachShadow({mode: 'open'});
|
||||||
|
}
|
||||||
|
assert_true(root1 instanceof DocumentFragment,
|
||||||
|
"ShadowRoot should be an instance of DocumentFragment.");
|
||||||
|
// <label><input id="shadow1"/></label><div id="div1"></div>
|
||||||
|
var label1 = document.createElement('label');
|
||||||
|
var input1 = document.createElement('input');
|
||||||
|
input1.setAttribute("id", "shadow1");
|
||||||
|
label1.appendChild(input1);
|
||||||
|
root1.appendChild(label1);
|
||||||
|
|
||||||
|
var div1 = document.createElement('div');
|
||||||
|
label1.appendChild(div1);
|
||||||
|
// <label for="shadow2"></label><input id="shadow2"/>
|
||||||
|
var root2;
|
||||||
|
if (isShadowDOMV0) {
|
||||||
|
root2 = div1.createShadowRoot();
|
||||||
|
} else {
|
||||||
|
root2 = div1.attachShadow({mode: 'open'});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true(root2 instanceof DocumentFragment,
|
||||||
|
"ShadowRoot should be an instance of DocumentFragment.");
|
||||||
|
var label2 = document.createElement('label');
|
||||||
|
label2.setAttribute("for", "shadow2");
|
||||||
|
|
||||||
|
var input2 = document.createElement('input');
|
||||||
|
input2.setAttribute("id", "shadow2");
|
||||||
|
root2.appendChild(label2);
|
||||||
|
root2.appendChild(input2);
|
||||||
|
|
||||||
|
assert_equals(root1.getElementById("shadow1").labels.length, 1,
|
||||||
|
"The form control has an ancestor with no explicit associated label, and it is the first labelable descendant.");
|
||||||
|
assert_equals(root2.getElementById("shadow2").labels.length, 1,
|
||||||
|
"The number of labels should be 1 since there is a label with a 'for' attribute associated with this labelable element.");
|
||||||
|
}, "A labelable element inside the shadow DOM.");
|
||||||
|
|
||||||
test(function () {
|
test(function () {
|
||||||
var labels = document.getElementById("test3").labels;
|
var labels = document.getElementById("test3").labels;
|
||||||
assert_true(labels instanceof NodeList, "A form control's 'labels' property should be an instance of a NodeList.");
|
assert_true(labels instanceof NodeList, "A form control's 'labels' property should be an instance of a NodeList.");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue