--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2652,16 +2652,21 @@ Element::SetAttrAndNotify(int32_t aNames
}
bool
Element::ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
+ if (aAttribute == nsGkAtoms::lang) {
+ aResult.ParseAtom(aValue);
+ return true;
+ }
+
if (aNamespaceID == kNameSpaceID_None) {
MOZ_ASSERT(aAttribute != nsGkAtoms::_class,
"The class attribute should be preparsed and therefore should "
"never be passed to Element::ParseAttribute");
if (aAttribute == nsGkAtoms::id) {
// Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id.
if (aValue.IsEmpty()) {
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -343,16 +343,43 @@ nsIContent::LookupNamespaceURIInternal(c
const nsIContent* content = this;
do {
if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
return NS_OK;
} while ((content = content->GetParent()));
return NS_ERROR_FAILURE;
}
+nsIAtom*
+nsIContent::GetLang() const
+{
+ for (const auto* content = this; content; content = content->GetParent()) {
+ if (!content->GetAttrCount() || !content->IsElement()) {
+ continue;
+ }
+
+ auto* element = content->AsElement();
+
+ // xml:lang has precedence over lang on HTML elements (see
+ // XHTML1 section C.7).
+ const nsAttrValue* attr =
+ element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
+ if (!attr && element->SupportsLangAttr()) {
+ attr = element->GetParsedAttr(nsGkAtoms::lang);
+ }
+ if (attr) {
+ MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
+ MOZ_ASSERT(attr->GetAtomValue());
+ return attr->GetAtomValue();
+ }
+ }
+
+ return nullptr;
+}
+
already_AddRefed<nsIURI>
nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
{
if (IsInAnonymousSubtree() && IsAnonymousContentInSVGUseSubtree()) {
nsIContent* bindingParent = GetBindingParent();
MOZ_ASSERT(bindingParent);
SVGUseElement* useElement = static_cast<SVGUseElement*>(bindingParent);
// XXX Ignore xml:base as we are removing it.
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -923,36 +923,28 @@ public:
bool SupportsLangAttr() const {
return IsHTMLElement() || IsSVGElement() || IsXULElement();
}
/**
* Determining language. Look at the nearest ancestor element that has a lang
* attribute in the XML namespace or is an HTML/SVG element and has a lang in
- * no namespace attribute. Returns false if no language was specified.
+ * no namespace attribute.
+ *
+ * Returns null if no language was specified. Can return the empty atom.
*/
+ nsIAtom* GetLang() const;
+
bool GetLang(nsAString& aResult) const {
- for (const nsIContent* content = this; content; content = content->GetParent()) {
- if (content->GetAttrCount() > 0) {
- // xml:lang has precedence over lang on HTML elements (see
- // XHTML1 section C.7).
- bool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang,
- aResult);
- if (!hasAttr && content->SupportsLangAttr()) {
- hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
- aResult);
- }
- NS_ASSERTION(hasAttr || aResult.IsEmpty(),
- "GetAttr that returns false should not make string non-empty");
- if (hasAttr) {
- return true;
- }
- }
+ if (auto* lang = GetLang()) {
+ aResult.Assign(nsDependentAtomString(lang));
+ return true;
}
+
return false;
}
// Returns true if this element is native-anonymous scrollbar content.
bool IsNativeScrollbarContent() const {
return IsNativeAnonymous() &&
IsAnyOfXULElements(nsGkAtoms::scrollbar,
nsGkAtoms::resizer,
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1214,27 +1214,38 @@ static inline void
MapLangAttributeInto(const nsMappedAttributes* aAttributes, GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font) |
NS_STYLE_INHERIT_BIT(Text))) {
return;
}
const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang);
- if (!langValue || langValue->Type() != nsAttrValue::eString) {
+ if (!langValue) {
return;
}
-
+ MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font))) {
- aData->SetIdentStringValueIfUnset(eCSSProperty__x_lang,
- langValue->GetStringValue());
+ nsIAtom* atom = langValue->GetAtomValue();
+
+ const nsDependentAtomString atomString(atom);
+ Maybe<nsCOMPtr<nsIAtom>> lowerAtom;
+ if (nsContentUtils::StringContainsASCIIUpper(atomString)) {
+ nsAutoString dest;
+ dest.SetCapacity(atomString.Length());
+ nsContentUtils::ASCIIToLower(atomString, dest);
+ lowerAtom.emplace(NS_AtomizeMainThread(dest));
+ }
+
+ aData->SetIdentAtomValueIfUnset(
+ eCSSProperty__x_lang, lowerAtom ? lowerAtom->get() : atom);
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_text_emphasis_position)) {
- const nsAString& lang = langValue->GetStringValue();
+ const nsIAtom* lang = langValue->GetAtomValue();
if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH);
} else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
// This branch is currently no part of the spec.
// See bug 1040668 comment 69 and comment 75.
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
--- a/layout/style/GenericSpecifiedValues.h
+++ b/layout/style/GenericSpecifiedValues.h
@@ -48,16 +48,19 @@ public:
inline nsPresContext* PresContext() {
return mPresContext;
}
// Set a property to an identifier (string)
inline void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue);
inline void SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue);
+ inline void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue);
+ inline void SetIdentAtomValueIfUnset(nsCSSPropertyID aId, nsIAtom* aValue);
+
// Set a property to a keyword (usually NS_STYLE_* or StyleFoo::*)
inline void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue);
inline void SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue);
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetKeywordValue(nsCSSPropertyID aId, T aValue) {
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
--- a/layout/style/GenericSpecifiedValuesInlines.h
+++ b/layout/style/GenericSpecifiedValuesInlines.h
@@ -36,16 +36,28 @@ GenericSpecifiedValues::SetIdentStringVa
void
GenericSpecifiedValues::SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue)
{
MOZ_STYLO_FORWARD(SetIdentStringValueIfUnset, (aId, aValue))
}
void
+GenericSpecifiedValues::SetIdentAtomValueIfUnset(nsCSSPropertyID aId, nsIAtom* aValue)
+{
+ MOZ_STYLO_FORWARD(SetIdentAtomValueIfUnset, (aId, aValue))
+}
+
+void
+GenericSpecifiedValues::SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
+{
+ MOZ_STYLO_FORWARD(SetIdentAtomValue, (aId, aValue))
+}
+
+void
GenericSpecifiedValues::SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
{
// there are some static asserts in MOZ_STYLO_FORWARD which
// won't work with the overloaded SetKeywordValue function,
// so we copy its expansion and use SetIntValue for decltype
// instead
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_GECKO_TYPE::SetKeywordValue)>
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -816,61 +816,62 @@ Gecko_MatchLang(RawGeckoElementBorrowed
MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang),
"aHasOverrideLang should only be set when aOverrideLang is null");
if (!aHasOverrideLang) {
return nsCSSRuleProcessor::LangPseudoMatches(aElement, nullptr, false,
aValue, aElement->OwnerDoc());
}
- if (aOverrideLang) {
- nsDependentAtomString overrideLang(aOverrideLang);
- return nsCSSRuleProcessor::LangPseudoMatches(aElement, &overrideLang, true,
- aValue, aElement->OwnerDoc());
- }
-
- return nsCSSRuleProcessor::LangPseudoMatches(aElement, nullptr, true,
+ return nsCSSRuleProcessor::LangPseudoMatches(aElement, aOverrideLang, true,
aValue, aElement->OwnerDoc());
}
nsIAtom*
Gecko_GetXMLLangValue(RawGeckoElementBorrowed aElement)
{
- nsString string;
- if (aElement->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, string)) {
- return NS_Atomize(string).take();
+ const nsAttrValue* attr =
+ aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
+
+ if (!attr) {
+ return nullptr;
}
- return nullptr;
+
+ MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
+
+ nsCOMPtr<nsIAtom> atom = attr->GetAtomValue();
+ return atom.forget().take();
}
template <typename Implementor>
static nsIAtom*
AtomAttrValue(Implementor* aElement, nsIAtom* aName)
{
const nsAttrValue* attr = aElement->GetParsedAttr(aName);
return attr ? attr->GetAtomValue() : nullptr;
}
template <typename Implementor>
static nsIAtom*
LangValue(Implementor* aElement)
{
+ // TODO(emilio): Deduplicate a bit with nsIContent::GetLang().
const nsAttrValue* attr =
aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
if (!attr && aElement->SupportsLangAttr()) {
attr = aElement->GetParsedAttr(nsGkAtoms::lang);
}
if (!attr) {
return nullptr;
}
- nsString lang;
- attr->ToString(lang);
- return NS_Atomize(lang).take();
+ MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
+ nsCOMPtr<nsIAtom> atom = attr->GetAtomValue();
+ return atom.forget().take();
}
template <typename Implementor, typename MatchFn>
static bool
DoMatch(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
{
if (aNS) {
int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS,
--- a/layout/style/ServoSpecifiedValues.cpp
+++ b/layout/style/ServoSpecifiedValues.cpp
@@ -42,21 +42,27 @@ ServoSpecifiedValues::PropertyIsSet(nsCS
return false;
}
void
ServoSpecifiedValues::SetIdentStringValue(nsCSSPropertyID aId,
const nsString& aValue)
{
nsCOMPtr<nsIAtom> atom = NS_Atomize(aValue);
- Servo_DeclarationBlock_SetIdentStringValue(mDecl, aId, atom);
+ SetIdentAtomValue(aId, atom);
+}
+
+void
+ServoSpecifiedValues::SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
+{
+ Servo_DeclarationBlock_SetIdentStringValue(mDecl, aId, aValue);
if (aId == eCSSProperty__x_lang) {
// This forces the lang prefs result to be cached
// so that we can access them off main thread during traversal
- mPresContext->ForceCacheLang(atom);
+ mPresContext->ForceCacheLang(aValue);
}
}
void
ServoSpecifiedValues::SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
{
Servo_DeclarationBlock_SetKeywordValue(mDecl, aId, aValue);
}
@@ -124,9 +130,9 @@ ServoSpecifiedValues::SetTextDecorationC
void
ServoSpecifiedValues::SetBackgroundImage(nsAttrValue& aValue)
{
nsAutoString str;
aValue.ToString(str);
Servo_DeclarationBlock_SetBackgroundImage(mDecl, str,
mPresContext->Document()->DefaultStyleAttrURLData());
-}
\ No newline at end of file
+}
--- a/layout/style/ServoSpecifiedValues.h
+++ b/layout/style/ServoSpecifiedValues.h
@@ -30,16 +30,25 @@ public:
void SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue)
{
if (!PropertyIsSet(aId)) {
SetIdentStringValue(aId, aValue);
}
}
+ void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue);
+
+ void SetIdentAtomValueIfUnset(nsCSSPropertyID aId, nsIAtom* aValue)
+ {
+ if (!PropertyIsSet(aId)) {
+ SetIdentAtomValue(aId, aValue);
+ }
+ }
+
void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue);
void SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue)
{
if (!PropertyIsSet(aId)) {
SetKeywordValue(aId, aValue);
}
}
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1673,76 +1673,66 @@ IsSignificantChildMaybeThreadSafe(const
} else {
auto content = const_cast<nsIContent*>(aContent);
return IsSignificantChild(content, aTextIsSignificant, aWhitespaceIsSignificant);
}
}
/* static */ bool
nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
- const nsAString* aOverrideLang,
+ const nsIAtom* aOverrideLang,
bool aHasOverrideLang,
const char16_t* aString,
const nsIDocument* aDocument)
{
NS_ASSERTION(aString, "null lang parameter");
if (!aString || !*aString) {
return false;
}
// We have to determine the language of the current element. Since
// this is currently no property and since the language is inherited
// from the parent we have to be prepared to look at all parent
// nodes. The language itself is encoded in the LANG attribute.
- bool haveLanguage = false;
- nsAutoString language;
- if (aHasOverrideLang) {
- if (aOverrideLang) {
- language = *aOverrideLang;
- haveLanguage = true;
- }
- } else {
- haveLanguage = aElement->GetLang(language);
- }
-
- if (haveLanguage) {
- return nsStyleUtil::DashMatchCompare(language,
+ if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) {
+ return nsStyleUtil::DashMatchCompare(nsDependentAtomString(language),
nsDependentString(aString),
nsASCIICaseInsensitiveStringComparator());
}
- if (aDocument) {
- // Try to get the language from the HTTP header or if this
- // is missing as well from the preferences.
- // The content language can be a comma-separated list of
- // language codes.
- aDocument->GetContentLanguage(language);
-
- nsDependentString langString(aString);
- language.StripWhitespace();
- int32_t begin = 0;
- int32_t len = language.Length();
- while (begin < len) {
- int32_t end = language.FindChar(char16_t(','), begin);
- if (end == kNotFound) {
- end = len;
- }
- if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
- end-begin),
- langString,
- nsASCIICaseInsensitiveStringComparator())) {
- return true;
- }
- begin = end + 1;
+ if (!aDocument) {
+ return false;
+ }
+
+ // Try to get the language from the HTTP header or if this
+ // is missing as well from the preferences.
+ // The content language can be a comma-separated list of
+ // language codes.
+ nsAutoString language;
+ aDocument->GetContentLanguage(language);
+
+ nsDependentString langString(aString);
+ language.StripWhitespace();
+ int32_t begin = 0;
+ int32_t len = language.Length();
+ while (begin < len) {
+ int32_t end = language.FindChar(char16_t(','), begin);
+ if (end == kNotFound) {
+ end = len;
}
- if (begin < len) {
+ if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end - begin),
+ langString,
+ nsASCIICaseInsensitiveStringComparator())) {
return true;
}
+ begin = end + 1;
}
-
+ if (begin < len) {
+ return true;
+ }
return false;
}
/* static */ bool
nsCSSRuleProcessor::StringPseudoMatches(const mozilla::dom::Element* aElement,
CSSPseudoClassType aPseudo,
const char16_t* aString,
const nsIDocument* aDocument,
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -167,17 +167,17 @@ public:
const char16_t* aString,
const nsIDocument* aDocument,
bool aForStyling,
mozilla::EventStates aStateMask,
bool* aSetSlowSelectorFlag,
bool* const aDependence = nullptr);
static bool LangPseudoMatches(const mozilla::dom::Element* aElement,
- const nsAString* aOverrideLang,
+ const nsIAtom* aOverrideLang,
bool aHasOverrideLang,
const char16_t* aString,
const nsIDocument* aDocument);
// nsIStyleRuleProcessor
virtual void RulesMatching(ElementRuleProcessorData* aData) override;
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override;
--- a/layout/style/nsRuleData.h
+++ b/layout/style/nsRuleData.h
@@ -131,16 +131,29 @@ struct nsRuleData final: mozilla::Generi
void SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue)
{
if (!PropertyIsSet(aId)) {
SetIdentStringValue(aId, aValue);
}
}
+ void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
+ {
+ nsCOMPtr<nsIAtom> atom = aValue;
+ ValueFor(aId)->SetAtomIdentValue(atom.forget());
+ }
+
+ void SetIdentAtomValueIfUnset(nsCSSPropertyID aId, nsIAtom* aValue)
+ {
+ if (!PropertyIsSet(aId)) {
+ SetIdentAtomValue(aId, aValue);
+ }
+ }
+
void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
{
ValueFor(aId)->SetIntValue(aValue, eCSSUnit_Enumerated);
}
void SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue)
{
if (!PropertyIsSet(aId)) {
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3560,22 +3560,21 @@ nsRuleNode::SetFont(nsPresContext* aPres
// mLanguage must be set before before any of the CalcLengthWith calls
// (direct calls or calls via SetFontSize) for the cases where |aParentFont|
// is the same as |aFont|.
//
// -x-lang: string, inherit
// This is not a real CSS property, it is an HTML attribute mapped to CSS.
const nsCSSValue* langValue = aRuleData->ValueForLang();
- if (eCSSUnit_Ident == langValue->GetUnit()) {
- nsAutoString lang;
- langValue->GetStringValue(lang);
-
- nsContentUtils::ASCIIToLower(lang);
- aFont->mLanguage = NS_Atomize(lang);
+ if (eCSSUnit_AtomIdent == langValue->GetUnit()) {
+ MOZ_ASSERT(!nsContentUtils::StringContainsASCIIUpper(
+ nsDependentAtomString(langValue->GetAtomValue())));
+
+ aFont->mLanguage = langValue->GetAtomValue();
aFont->mExplicitLanguage = true;
}
const nsFont* defaultVariableFont =
aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
aFont->mLanguage);
// -moz-system-font: enum (never inherit!)