HTML - implement the labels attribute (follow up)

This commit is contained in:
janekptacijarabaci 2017-08-18 13:39:37 +02:00 • committed by Roy Tam
commit b9e802f497
20 changed files with 87 additions and 77 deletions

View file

@ -165,10 +165,9 @@ nsIContent::DoGetID() const
} }
const nsAttrValue* const nsAttrValue*
nsIContent::DoGetClasses() const Element::DoGetClasses() const
{ {
MOZ_ASSERT(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call"); MOZ_ASSERT(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call");
MOZ_ASSERT(IsElement(), "Only elements can have classes");
if (IsSVGElement()) { if (IsSVGElement()) {
const nsAttrValue* animClass = const nsAttrValue* animClass =
@ -178,7 +177,7 @@ nsIContent::DoGetClasses() const
} }
} }
return AsElement()->GetParsedAttr(nsGkAtoms::_class); return GetParsedAttr(nsGkAtoms::_class);
} }
NS_IMETHODIMP NS_IMETHODIMP

View file

@ -544,6 +544,18 @@ public:
virtual uint32_t GetAttrCount() const override; virtual uint32_t GetAttrCount() const override;
virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual bool IsNodeOfType(uint32_t aFlags) const override;
/**
* Get the class list of this element (this corresponds to the value of the
* class attribute). This may be null if there are no classes, but that's not
* guaranteed (e.g. we could have class="").
*/
const nsAttrValue* GetClasses() const {
if (HasFlag(NODE_MAY_HAVE_CLASS)) {
return DoGetClasses();
}
return nullptr;
}
#ifdef DEBUG #ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override
{ {
@ -1371,6 +1383,12 @@ protected:
nsTArray<nsDOMSlots::IntersectionObserverRegistration>* RegisteredIntersectionObservers(); nsTArray<nsDOMSlots::IntersectionObserverRegistration>* RegisteredIntersectionObservers();
private: private:
/**
* Hook for implementing GetClasses. This is guaranteed to only be
* called if the NODE_MAY_HAVE_CLASS flag is set.
*/
const nsAttrValue* DoGetClasses() const;
/** /**
* Get this element's client area rect in app units. * Get this element's client area rect in app units.
* @return the frame's client area * @return the frame's client area

View file

@ -1831,7 +1831,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
} }
nsAutoString classes; nsAutoString classes;
const nsAttrValue* classAttrValue = tmp->GetClasses(); const nsAttrValue* classAttrValue = tmp->IsElement() ?
tmp->AsElement()->GetClasses() : nullptr;
if (classAttrValue) { if (classAttrValue) {
classes.AppendLiteral(" class='"); classes.AppendLiteral(" class='");
nsAutoString classString; nsAutoString classString;

View file

@ -18,6 +18,12 @@ class nsINode;
class nsString; class nsString;
class nsAString; class nsAString;
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
// Magic namespace id that means "match all namespaces". This is // Magic namespace id that means "match all namespaces". This is
// negative so it won't collide with actual namespace constants. // negative so it won't collide with actual namespace constants.
#define kNameSpaceID_Wildcard INT32_MIN #define kNameSpaceID_Wildcard INT32_MIN
@ -26,7 +32,7 @@ class nsAString;
// arbitrary matching algorithm. aContent is the content that may // arbitrary matching algorithm. aContent is the content that may
// match the list, while aNamespaceID, aAtom, and aData are whatever // match the list, while aNamespaceID, aAtom, and aData are whatever
// was passed to the list's constructor. // was passed to the list's constructor.
typedef bool (*nsContentListMatchFunc)(nsIContent* aContent, typedef bool (*nsContentListMatchFunc)(mozilla::dom::Element* aElement,
int32_t aNamespaceID, int32_t aNamespaceID,
nsIAtom* aAtom, nsIAtom* aAtom,
void* aData); void* aData);

View file

@ -6287,11 +6287,11 @@ struct ClassMatchingInfo {
// static // static
bool bool
nsContentUtils::MatchClassNames(nsIContent* aContent, int32_t aNamespaceID, nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
// We can't match if there are no class names // We can't match if there are no class names
const nsAttrValue* classAttr = aContent->GetClasses(); const nsAttrValue* classAttr = aElement->GetClasses();
if (!classAttr) { if (!classAttr) {
return false; return false;
} }

View file

@ -2753,7 +2753,8 @@ private:
static void DropFragmentParsers(); static void DropFragmentParsers();
static bool MatchClassNames(nsIContent* aContent, int32_t aNamespaceID, static bool MatchClassNames(mozilla::dom::Element* aElement,
int32_t aNamespaceID,
nsIAtom* aAtom, void* aData); nsIAtom* aAtom, void* aData);
static void DestroyClassNameArray(void* aData); static void DestroyClassNameArray(void* aData);
static void* AllocClassMatchingInfo(nsINode* aRootNode, static void* AllocClassMatchingInfo(nsINode* aRootNode,

View file

@ -861,18 +861,6 @@ public:
return nullptr; return nullptr;
} }
/**
* Get the class list of this content node (this corresponds to the
* value of the class attribute). This may be null if there are no
* classes, but that's not guaranteed.
*/
const nsAttrValue* GetClasses() const {
if (HasFlag(NODE_MAY_HAVE_CLASS)) {
return DoGetClasses();
}
return nullptr;
}
/** /**
* Walk aRuleWalker over the content style rules (presentational * Walk aRuleWalker over the content style rules (presentational
* hint rules) for this content node. * hint rules) for this content node.
@ -990,13 +978,6 @@ protected:
*/ */
nsIAtom* DoGetID() const; nsIAtom* DoGetID() const;
private:
/**
* Hook for implementing GetClasses. This is guaranteed to only be
* called if the NODE_MAY_HAVE_CLASS flag is set.
*/
const nsAttrValue* DoGetClasses() const;
public: public:
#ifdef DEBUG #ifdef DEBUG
/** /**

View file

@ -8,6 +8,7 @@
#include "mozilla/dom/HTMLAllCollectionBinding.h" #include "mozilla/dom/HTMLAllCollectionBinding.h"
#include "mozilla/dom/Nullable.h" #include "mozilla/dom/Nullable.h"
#include "mozilla/dom/Element.h"
#include "nsHTMLDocument.h" #include "nsHTMLDocument.h"
namespace mozilla { namespace mozilla {
@ -86,14 +87,14 @@ IsAllNamedElement(nsIContent* aContent)
} }
static bool static bool
DocAllResultMatch(nsIContent* aContent, int32_t aNamespaceID, nsIAtom* aAtom, DocAllResultMatch(Element* aElement, int32_t aNamespaceID, nsIAtom* aAtom,
void* aData) void* aData)
{ {
if (aContent->GetID() == aAtom) { if (aElement->GetID() == aAtom) {
return true; return true;
} }
nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aContent); nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aElement);
if (!elm) { if (!elm) {
return false; return false;
} }

View file

@ -35,11 +35,11 @@ NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
NS_IMPL_ELEMENT_CLONE(HTMLDataListElement) NS_IMPL_ELEMENT_CLONE(HTMLDataListElement)
bool bool
HTMLDataListElement::MatchOptions(nsIContent* aContent, int32_t aNamespaceID, HTMLDataListElement::MatchOptions(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
return aContent->NodeInfo()->Equals(nsGkAtoms::option, kNameSpaceID_XHTML) && return aElement->NodeInfo()->Equals(nsGkAtoms::option, kNameSpaceID_XHTML) &&
!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
} }
} // namespace dom } // namespace dom

View file

@ -37,8 +37,8 @@ public:
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
// This function is used to generate the nsContentList (option elements). // This function is used to generate the nsContentList (option elements).
static bool MatchOptions(nsIContent* aContent, int32_t aNamespaceID, static bool MatchOptions(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData); nsIAtom* aAtom, void* aData);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLDataListElement, NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLDataListElement,
nsGenericHTMLElement) nsGenericHTMLElement)

View file

@ -120,10 +120,10 @@ HTMLFieldSetElement::GetType(nsAString& aType)
/* static */ /* static */
bool bool
HTMLFieldSetElement::MatchListedElements(nsIContent* aContent, int32_t aNamespaceID, HTMLFieldSetElement::MatchListedElements(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aContent); nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aElement);
return formControl; return formControl;
} }

View file

@ -124,8 +124,8 @@ private:
void NotifyElementsForFirstLegendChange(bool aNotify); void NotifyElementsForFirstLegendChange(bool aNotify);
// This function is used to generate the nsContentList (listed form elements). // This function is used to generate the nsContentList (listed form elements).
static bool MatchListedElements(nsIContent* aContent, int32_t aNamespaceID, static bool MatchListedElements(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData); nsIAtom* aAtom, void* aData);
// listed form controls elements. // listed form controls elements.
RefPtr<nsContentList> mElements; RefPtr<nsContentList> mElements;

View file

@ -735,12 +735,12 @@ HTMLSelectElement::SetLength(uint32_t aLength, ErrorResult& aRv)
/* static */ /* static */
bool bool
HTMLSelectElement::MatchSelectedOptions(nsIContent* aContent, HTMLSelectElement::MatchSelectedOptions(Element* aElement,
int32_t /* unused */, int32_t /* unused */,
nsIAtom* /* unused */, nsIAtom* /* unused */,
void* /* unused*/) void* /* unused*/)
{ {
HTMLOptionElement* option = HTMLOptionElement::FromContent(aContent); HTMLOptionElement* option = HTMLOptionElement::FromContent(aElement);
return option && option->Selected(); return option && option->Selected();
} }

View file

@ -247,7 +247,7 @@ public:
mOptions->IndexedSetter(aIndex, aOption, aRv); mOptions->IndexedSetter(aIndex, aOption, aRv);
} }
static bool MatchSelectedOptions(nsIContent* aContent, int32_t, nsIAtom*, static bool MatchSelectedOptions(Element* aElement, int32_t, nsIAtom*,
void*); void*);
nsIHTMLCollection* SelectedOptions(); nsIHTMLCollection* SelectedOptions();

View file

@ -120,10 +120,10 @@ HTMLTableRowElement::SectionRowIndex() const
} }
static bool static bool
IsCell(nsIContent *aContent, int32_t aNamespaceID, IsCell(Element *aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void *aData) nsIAtom* aAtom, void *aData)
{ {
return aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th); return aElement->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
} }
nsIHTMLCollection* nsIHTMLCollection*

View file

@ -1100,31 +1100,31 @@ nsHTMLDocument::Applets()
} }
bool bool
nsHTMLDocument::MatchLinks(nsIContent *aContent, int32_t aNamespaceID, nsHTMLDocument::MatchLinks(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
nsIDocument* doc = aContent->GetUncomposedDoc(); nsIDocument* doc = aElement->GetUncomposedDoc();
if (doc) { if (doc) {
NS_ASSERTION(aContent->IsInUncomposedDoc(), NS_ASSERTION(aElement->IsInUncomposedDoc(),
"This method should never be called on content nodes that " "This method should never be called on content nodes that "
"are not in a document!"); "are not in a document!");
#ifdef DEBUG #ifdef DEBUG
{ {
nsCOMPtr<nsIHTMLDocument> htmldoc = nsCOMPtr<nsIHTMLDocument> htmldoc =
do_QueryInterface(aContent->GetUncomposedDoc()); do_QueryInterface(aElement->GetUncomposedDoc());
NS_ASSERTION(htmldoc, NS_ASSERTION(htmldoc,
"Huh, how did this happen? This should only be used with " "Huh, how did this happen? This should only be used with "
"HTML documents!"); "HTML documents!");
} }
#endif #endif
mozilla::dom::NodeInfo *ni = aContent->NodeInfo(); mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
nsIAtom *localName = ni->NameAtom(); nsIAtom *localName = ni->NameAtom();
if (ni->NamespaceID() == kNameSpaceID_XHTML && if (ni->NamespaceID() == kNameSpaceID_XHTML &&
(localName == nsGkAtoms::a || localName == nsGkAtoms::area)) { (localName == nsGkAtoms::a || localName == nsGkAtoms::area)) {
return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::href); return aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
} }
} }
@ -1148,24 +1148,24 @@ nsHTMLDocument::Links()
} }
bool bool
nsHTMLDocument::MatchAnchors(nsIContent *aContent, int32_t aNamespaceID, nsHTMLDocument::MatchAnchors(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
NS_ASSERTION(aContent->IsInUncomposedDoc(), NS_ASSERTION(aElement->IsInUncomposedDoc(),
"This method should never be called on content nodes that " "This method should never be called on content nodes that "
"are not in a document!"); "are not in a document!");
#ifdef DEBUG #ifdef DEBUG
{ {
nsCOMPtr<nsIHTMLDocument> htmldoc = nsCOMPtr<nsIHTMLDocument> htmldoc =
do_QueryInterface(aContent->GetUncomposedDoc()); do_QueryInterface(aElement->GetUncomposedDoc());
NS_ASSERTION(htmldoc, NS_ASSERTION(htmldoc,
"Huh, how did this happen? This should only be used with " "Huh, how did this happen? This should only be used with "
"HTML documents!"); "HTML documents!");
} }
#endif #endif
if (aContent->NodeInfo()->Equals(nsGkAtoms::a, kNameSpaceID_XHTML)) { if (aElement->IsHTMLElement(nsGkAtoms::a)) {
return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name); return aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
} }
return false; return false;
@ -1952,14 +1952,14 @@ nsHTMLDocument::Writeln(JSContext* cx, const Sequence<nsString>& aText,
} }
bool bool
nsHTMLDocument::MatchNameAttribute(nsIContent* aContent, int32_t aNamespaceID, nsHTMLDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
NS_PRECONDITION(aContent, "Must have content node to work with!"); NS_PRECONDITION(aElement, "Must have element to work with!");
nsString* elementName = static_cast<nsString*>(aData); nsString* elementName = static_cast<nsString*>(aData);
return return
aContent->GetNameSpaceID() == kNameSpaceID_XHTML && aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
*elementName, eCaseMatters); *elementName, eCaseMatters);
} }
@ -2279,10 +2279,10 @@ nsHTMLDocument::GetForms()
return mForms; return mForms;
} }
static bool MatchFormControls(nsIContent* aContent, int32_t aNamespaceID, static bool MatchFormControls(Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData) nsIAtom* aAtom, void* aData)
{ {
return aContent->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL); return aElement->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL);
} }
nsContentList* nsContentList*

View file

@ -261,12 +261,13 @@ protected:
nsIContent *MatchId(nsIContent *aContent, const nsAString& aId); nsIContent *MatchId(nsIContent *aContent, const nsAString& aId);
static bool MatchLinks(nsIContent *aContent, int32_t aNamespaceID, static bool MatchLinks(mozilla::dom::Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData);
static bool MatchAnchors(mozilla::dom::Element* aElement, int32_t aNamespaceID,
nsIAtom* aAtom, void* aData); nsIAtom* aAtom, void* aData);
static bool MatchAnchors(nsIContent *aContent, int32_t aNamespaceID, static bool MatchNameAttribute(mozilla::dom::Element* aElement,
nsIAtom* aAtom, void* aData); int32_t aNamespaceID,
static bool MatchNameAttribute(nsIContent* aContent, int32_t aNamespaceID, nsIAtom* aAtom, void* aData);
nsIAtom* aAtom, void* aData);
static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName); static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
static void DocumentWriteTerminationFunc(nsISupports *aRef); static void DocumentWriteTerminationFunc(nsISupports *aRef);

View file

@ -1919,26 +1919,26 @@ XULDocument::StartLayout(void)
/* static */ /* static */
bool bool
XULDocument::MatchAttribute(nsIContent* aContent, XULDocument::MatchAttribute(Element* aElement,
int32_t aNamespaceID, int32_t aNamespaceID,
nsIAtom* aAttrName, nsIAtom* aAttrName,
void* aData) void* aData)
{ {
NS_PRECONDITION(aContent, "Must have content node to work with!"); NS_PRECONDITION(aElement, "Must have content node to work with!");
nsString* attrValue = static_cast<nsString*>(aData); nsString* attrValue = static_cast<nsString*>(aData);
if (aNamespaceID != kNameSpaceID_Unknown && if (aNamespaceID != kNameSpaceID_Unknown &&
aNamespaceID != kNameSpaceID_Wildcard) { aNamespaceID != kNameSpaceID_Wildcard) {
return attrValue->EqualsLiteral("*") ? return attrValue->EqualsLiteral("*") ?
aContent->HasAttr(aNamespaceID, aAttrName) : aElement->HasAttr(aNamespaceID, aAttrName) :
aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue, aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
eCaseMatters); eCaseMatters);
} }
// Qualified name match. This takes more work. // Qualified name match. This takes more work.
uint32_t count = aContent->GetAttrCount(); uint32_t count = aElement->GetAttrCount();
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
const nsAttrName* name = aContent->GetAttrNameAt(i); const nsAttrName* name = aElement->GetAttrNameAt(i);
bool nameMatch; bool nameMatch;
if (name->IsAtom()) { if (name->IsAtom()) {
nameMatch = name->Atom() == aAttrName; nameMatch = name->Atom() == aAttrName;
@ -1950,7 +1950,7 @@ XULDocument::MatchAttribute(nsIContent* aContent,
if (nameMatch) { if (nameMatch) {
return attrValue->EqualsLiteral("*") || return attrValue->EqualsLiteral("*") ||
aContent->AttrValueIs(name->NamespaceID(), name->LocalName(), aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
*attrValue, eCaseMatters); *attrValue, eCaseMatters);
} }
} }

View file

@ -180,7 +180,7 @@ public:
NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override; NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override;
static bool static bool
MatchAttribute(nsIContent* aContent, MatchAttribute(Element* aContent,
int32_t aNameSpaceID, int32_t aNameSpaceID,
nsIAtom* aAttrName, nsIAtom* aAttrName,
void* aData); void* aData);

View file

@ -126,8 +126,10 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
contentData.AppendLiteral(" id:"); contentData.AppendLiteral(" id:");
contentData.Append(tmp); contentData.Append(tmp);
} }
if (content->GetClasses()) { const nsAttrValue* classes = content->IsElement() ?
content->GetClasses()->ToString(tmp); content->AsElement()->GetClasses() : nullptr;
if (classes) {
classes->ToString(tmp);
contentData.AppendLiteral(" class:"); contentData.AppendLiteral(" class:");
contentData.Append(tmp); contentData.Append(tmp);
} }