diff options
25 files changed, 516 insertions, 95 deletions
diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 293177ce7..3112515ff 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -574,6 +574,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()); @@ -602,6 +605,7 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL) mShadowRoot = nullptr; mContainingShadow = nullptr; mChildrenList = nullptr; + mLabelsList = nullptr; mCustomElementData = nullptr; mClassList = nullptr; } diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 3cb5575fe..1cd8033bb 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -24,6 +24,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. */ diff --git a/dom/base/nsContentList.cpp b/dom/base/nsContentList.cpp index 09e949009..43e65777d 100644 --- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -254,19 +254,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; @@ -379,6 +366,7 @@ NS_GetFuncStringHTMLCollection(nsINode* aRootNode, aString); } +//----------------------------------------------------- // nsContentList implementation nsContentList::nsContentList(nsINode* aRootNode, @@ -660,7 +648,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)) { @@ -806,7 +794,7 @@ nsContentList::ContentInserted(nsIDocument *aDocument, ASSERT_IN_SYNC; } - + void nsContentList::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, @@ -1075,3 +1063,128 @@ 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; + } + + 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); +} diff --git a/dom/base/nsContentList.h b/dom/base/nsContentList.h index 3878074b2..83d27da95 100644 --- a/dom/base/nsContentList.h +++ b/dom/base/nsContentList.h @@ -371,9 +371,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 @@ -584,4 +584,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___ diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 91427fabb..e4ae7ede8 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -526,6 +526,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") diff --git a/dom/base/test/test_bug1375050.html b/dom/base/test/test_bug1375050.html new file mode 100644 index 000000000..b91b859d0 --- /dev/null +++ b/dom/base/test/test_bug1375050.html @@ -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>
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 78f74ae0c..d46eccdbc 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -8800,6 +8800,16 @@ HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<FileSystemEntry>>& aSequence) aSequence.AppendElements(mEntries); } +already_AddRefed<nsINodeList> +HTMLInputElement::GetLabels() +{ + if (!IsLabelable()) { + return nullptr; + } + + return nsGenericHTMLElement::Labels(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index e5d670e08..9ca876aee 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -704,6 +704,8 @@ public: // XPCOM GetCustomVisibility() is OK + already_AddRefed<nsINodeList> GetLabels(); + // XPCOM Select() is OK Nullable<int32_t> GetSelectionStart(ErrorResult& aRv); diff --git a/dom/html/HTMLLabelElement.cpp b/dom/html/HTMLLabelElement.cpp index c1d22b0a6..d1c037336 100644 --- a/dom/html/HTMLLabelElement.cpp +++ b/dom/html/HTMLLabelElement.cpp @@ -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); } diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index d75001a83..2f890325a 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -108,6 +108,7 @@ #include "mozilla/StyleSetHandle.h" #include "mozilla/StyleSetHandleInlines.h" #include "ReferrerPolicy.h" +#include "mozilla/dom/HTMLLabelElement.h" using namespace mozilla; 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; } @@ -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); } @@ -1701,6 +1717,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 { diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 3cca41c3d..0635c27e1 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -834,6 +834,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 */); diff --git a/dom/html/test/forms/test_button_attributes_reflection.html b/dom/html/test/forms/test_button_attributes_reflection.html index 26858e939..4e702b3ac 100644 --- a/dom/html/test/forms/test_button_attributes_reflection.html +++ b/dom/html/test/forms/test_button_attributes_reflection.html @@ -128,9 +128,12 @@ is(typeof(document.createElement("button").setCustomValidity), "function", "button.setCustomValidity should be a function"); // .labels -todo("labels" in document.createElement("button"), - "button.labels isn't implemented yet"); - +ok("labels" in document.createElement("button"), + "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> </pre> </body> diff --git a/dom/webidl/HTMLButtonElement.webidl b/dom/webidl/HTMLButtonElement.webidl index c50e09ae0..579efa39c 100644 --- a/dom/webidl/HTMLButtonElement.webidl +++ b/dom/webidl/HTMLButtonElement.webidl @@ -44,6 +44,5 @@ interface HTMLButtonElement : HTMLElement { boolean reportValidity(); void setCustomValidity(DOMString error); -// Not yet implemented: -// readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index d3d537f84..050d19510 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -111,7 +111,7 @@ interface HTMLInputElement : HTMLElement { boolean reportValidity(); void setCustomValidity(DOMString error); - // Bug 850365 readonly attribute NodeList labels; + readonly attribute NodeList? labels; void select(); diff --git a/dom/webidl/HTMLMeterElement.webidl b/dom/webidl/HTMLMeterElement.webidl index 1f80764e9..104e00353 100644 --- a/dom/webidl/HTMLMeterElement.webidl +++ b/dom/webidl/HTMLMeterElement.webidl @@ -26,8 +26,5 @@ interface HTMLMeterElement : HTMLElement { [SetterThrows] attribute double optimum; - /** - * The labels attribute will be done with bug 556743. - */ - //readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/dom/webidl/HTMLOutputElement.webidl b/dom/webidl/HTMLOutputElement.webidl index 05dcf1800..d0e4ecbe6 100644 --- a/dom/webidl/HTMLOutputElement.webidl +++ b/dom/webidl/HTMLOutputElement.webidl @@ -33,6 +33,5 @@ interface HTMLOutputElement : HTMLElement { boolean reportValidity(); void setCustomValidity(DOMString error); -// Not yet implemented (bug 556743). -// readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/dom/webidl/HTMLProgressElement.webidl b/dom/webidl/HTMLProgressElement.webidl index 3d1000d22..028728e22 100644 --- a/dom/webidl/HTMLProgressElement.webidl +++ b/dom/webidl/HTMLProgressElement.webidl @@ -17,9 +17,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; }; diff --git a/dom/webidl/HTMLSelectElement.webidl b/dom/webidl/HTMLSelectElement.webidl index 74fc7b2cf..b18ca3634 100644 --- a/dom/webidl/HTMLSelectElement.webidl +++ b/dom/webidl/HTMLSelectElement.webidl @@ -53,7 +53,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(); diff --git a/dom/webidl/HTMLTextAreaElement.webidl b/dom/webidl/HTMLTextAreaElement.webidl index b1005ed42..4df687a0b 100644 --- a/dom/webidl/HTMLTextAreaElement.webidl +++ b/dom/webidl/HTMLTextAreaElement.webidl @@ -57,7 +57,7 @@ interface HTMLTextAreaElement : HTMLElement { boolean reportValidity(); void setCustomValidity(DOMString error); - // readonly attribute NodeList labels; + readonly attribute NodeList labels; void select(); [Throws] diff --git a/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini b/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini deleted file mode 100644 index e1a2e38b5..000000000 --- a/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini +++ /dev/null @@ -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 - diff --git a/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini b/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini index bfa61edb7..363324f2e 100644 --- a/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini +++ b/testing/web-platform/meta/html/semantics/forms/the-label-element/labelable-elements.html.ini @@ -1,29 +1,7 @@ [labelable-elements.html] type: testharness - [Check if the output element can access 'labels'] - 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'] + [Check if the keygen element is not a labelable element] expected: FAIL [Check if the keygen element can access 'labels'] expected: FAIL - - [Check if the meter element can access 'labels'] - expected: FAIL - diff --git a/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/button_labels.html.ini b/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/button_labels.html.ini deleted file mode 100644 index e4a30320e..000000000 --- a/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/button_labels.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[button_labels.html] - type: testharness - [Forms] - expected: FAIL - diff --git a/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/input_labels.html.ini b/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/input_labels.html.ini deleted file mode 100644 index da0fb96d7..000000000 --- a/testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/input_labels.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[input_labels.html] - type: testharness - [Forms] - expected: FAIL - diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html new file mode 100644 index 000000000..56b52c951 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/iframe-label-attributes.html @@ -0,0 +1,8 @@ +<html>
+ <body>
+ <label>
+ <div id="div1"></div>
+ </label>
+ <label for="test13"></label>
+ </body>
+</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html index 826533e0c..2910f2c01 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html +++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html @@ -32,10 +32,53 @@ <label id="lbl5" for="test7"></label> <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> <label id="lbl6" for="test7"></label> - +<div id="content" style="display: none"> <script> //control attribute @@ -57,6 +100,7 @@ }, "A label element not in a document can not label any element in the document."); test(function () { + var labels = document.getElementById("test3").labels; assert_equals(document.getElementById("lbl1").control, document.getElementById("test3"), "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")); assert_equals(document.getElementById("lbl1").control, input, "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(); }, "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."); @@ -101,6 +149,168 @@ }, "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 () { 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_equals(labels.length, 1, "The form control has an ancestor with no explicit associated label, and is the first labelable descendant."); |