Bug 1370802: Parse lang attributes as atoms. r=heycam

MozReview-Commit-ID: Cnq3wB7aVB1

--HG--
extra : rebase_source : fa0252b78381bf023ab08bf2d9fd13d4c0ed57a1
This commit is contained in:
Emilio Cobos Álvarez 2017-06-15 21:48:26 +02:00
parent cba8cd8660
commit f6dc661b5b
13 changed files with 154 additions and 86 deletions

View File

@ -2657,6 +2657,11 @@ Element::ParseAttribute(int32_t aNamespaceID,
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 "

View File

@ -348,6 +348,33 @@ nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
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
{

View File

@ -928,26 +928,18 @@ public:
/**
* 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;
}

View File

@ -1219,17 +1219,28 @@ MapLangAttributeInto(const nsMappedAttributes* aAttributes, GenericSpecifiedValu
}
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);

View File

@ -53,6 +53,9 @@ public:
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);

View File

@ -40,6 +40,18 @@ GenericSpecifiedValues::SetIdentStringValueIfUnset(nsCSSPropertyID aId, const ns
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)
{

View File

@ -821,24 +821,24 @@ Gecko_MatchLang(RawGeckoElementBorrowed aElement,
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>
@ -853,6 +853,7 @@ 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()) {
@ -863,9 +864,9 @@ LangValue(Implementor* aElement)
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>

View File

@ -47,11 +47,17 @@ 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);
}
}
@ -129,4 +135,4 @@ ServoSpecifiedValues::SetBackgroundImage(nsAttrValue& aValue)
aValue.ToString(str);
Servo_DeclarationBlock_SetBackgroundImage(mDecl, str,
mPresContext->Document()->DefaultStyleAttrURLData());
}
}

View File

@ -35,6 +35,15 @@ public:
}
}
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)

View File

@ -1678,7 +1678,7 @@ IsSignificantChildMaybeThreadSafe(const nsIContent* aContent,
/* static */ bool
nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
const nsAString* aOverrideLang,
const nsIAtom* aOverrideLang,
bool aHasOverrideLang,
const char16_t* aString,
const nsIDocument* aDocument)
@ -1692,52 +1692,42 @@ nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
// 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 (begin < len) {
return true;
}
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 (nsStyleUtil::DashMatchCompare(Substring(language, begin, end - begin),
langString,
nsASCIICaseInsensitiveStringComparator())) {
return true;
}
begin = end + 1;
}
if (begin < len) {
return true;
}
return false;
}

View File

@ -172,7 +172,7 @@ public:
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);

View File

@ -136,6 +136,19 @@ struct nsRuleData final: mozilla::GenericSpecifiedValues
}
}
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);

View File

@ -3606,12 +3606,11 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, GeckoStyleContext* aContext,
// -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);
if (eCSSUnit_AtomIdent == langValue->GetUnit()) {
MOZ_ASSERT(!nsContentUtils::StringContainsASCIIUpper(
nsDependentAtomString(langValue->GetAtomValue())));
nsContentUtils::ASCIIToLower(lang);
aFont->mLanguage = NS_Atomize(lang);
aFont->mLanguage = langValue->GetAtomValue();
aFont->mExplicitLanguage = true;
}