Add support for :dir(ltr/rtl) CSS selector, layout/style part. Bug 562159, r=bz

This commit is contained in:
Simon Montagu 2012-08-07 21:44:24 -07:00
parent 4a67200814
commit 1c6ba0a43d
7 changed files with 105 additions and 22 deletions

View File

@ -248,6 +248,10 @@ private:
#define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(41) #define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(41)
// Platform does not support plugin content (some mobile platforms) // Platform does not support plugin content (some mobile platforms)
#define NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM NS_DEFINE_EVENT_STATE_MACRO(42) #define NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM NS_DEFINE_EVENT_STATE_MACRO(42)
// Element is ltr (for :dir pseudo-class)
#define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(43)
// Element is rtl (for :dir pseudo-class)
#define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(44)
/** /**
* NOTE: do not go over 63 without updating nsEventStates::InternalType! * NOTE: do not go over 63 without updating nsEventStates::InternalType!

View File

@ -49,6 +49,7 @@ public:
{ {
NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML, NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML,
"Unexpected namespace"); "Unexpected namespace");
AddStatesSilently(NS_EVENT_STATE_LTR);
} }
/** Typesafe, non-refcounting cast from nsIContent. Cheaper than QI. **/ /** Typesafe, non-refcounting cast from nsIContent. Cheaper than QI. **/

View File

@ -114,6 +114,7 @@ PEMQExpectedFeatureValue=Found invalid value for media feature.
PEBadFontBlockStart=Expected '{' to begin @font-face rule but found '%1$S'. PEBadFontBlockStart=Expected '{' to begin @font-face rule but found '%1$S'.
PEBadFontBlockEnd=Expected '}' to end @font-face rule but found '%1$S'. PEBadFontBlockEnd=Expected '}' to end @font-face rule but found '%1$S'.
PEAnonBoxNotAlone=Did not expect anonymous box. PEAnonBoxNotAlone=Did not expect anonymous box.
PEBadDirValue=Expected 'ltr' or 'rtl' in direction selector but found '%1$S'.
PESupportsConditionStartEOF='not' or '(' PESupportsConditionStartEOF='not' or '('
PESupportsConditionInParensStartEOF='not', '(' or identifier PESupportsConditionInParensStartEOF='not', '(' or identifier
PESupportsConditionNotEOF='not' PESupportsConditionNotEOF='not'

View File

@ -3595,10 +3595,12 @@ CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
} }
// -moz-locale-dir can only have values of 'ltr' or 'rtl'. // -moz-locale-dir and :dir can only have values of 'ltr' or 'rtl'.
if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir) { if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
aType == nsCSSPseudoClasses::ePseudoClass_dir) {
if (!mToken.mIdent.EqualsLiteral("ltr") && if (!mToken.mIdent.EqualsLiteral("ltr") &&
!mToken.mIdent.EqualsLiteral("rtl")) { !mToken.mIdent.EqualsLiteral("rtl")) {
REPORT_UNEXPECTED_TOKEN(PEBadDirValue);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
} }
} }

View File

@ -9,27 +9,45 @@
* This file contains the list of nsIAtoms and their values for CSS * This file contains the list of nsIAtoms and their values for CSS
* pseudo-classes. It is designed to be used as inline input to * pseudo-classes. It is designed to be used as inline input to
* nsCSSPseudoClasses.cpp *only* through the magic of C preprocessing. * nsCSSPseudoClasses.cpp *only* through the magic of C preprocessing.
* All entries must be enclosed in the macros CSS_PSEUDO_CLASS or * All entries must be enclosed in the macros CSS_PSEUDO_CLASS,
* CSS_STATE_PSEUDO_CLASS which will have cruel and unusual things * CSS_STATE_DEPENDENT_PSEUDO_CLASS, or CSS_STATE_PSEUDO_CLASS which
* done to it. The entries should be kept in some sort of logical * will have cruel and unusual things done to them. The entries should
* order. The first argument to CSS_PSEUDO_CLASS is the C++ * be kept in some sort of logical order. The first argument to
* identifier of the atom. The second argument is the string value of * CSS_PSEUDO_CLASS is the C++ identifier of the atom. The second
* the atom. CSS_STATE_PSEUDO_CLASS also takes the name of the state * argument is the string value of the atom.
* bits that the class corresponds to. Only one of the bits needs to * CSS_STATE_DEPENDENT_PSEUDO_CLASS and CSS_STATE_PSEUDO_CLASS also take
* match for the pseudo-class to match. If CSS_STATE_PSEUDO_CLASS is * the name of the state bits that the class corresponds to. Only one
* not defined, it'll be automatically defined to CSS_PSEUDO_CLASS. * of the bits needs to match for a CSS_STATE_PSEUDO_CLASS to match;
* CSS_STATE_DEPENDENT_PSEUDO_CLASS matching depends on a customized per-class
* algorithm which should be defined in SelectorMatches() in
* nsCSSRuleProcessor.cpp.
*
* If CSS_STATE_PSEUDO_CLASS is not defined, it'll be automatically
* defined to CSS_STATE_DEPENDENT_PSEUDO_CLASS;
* if CSS_STATE_DEPENDENT_PSEUDO_CLASS is not defined, it'll be
* automatically defined to CSS_PSEUDO_CLASS.
*/ */
// OUTPUT_CLASS=nsCSSPseudoClasses // OUTPUT_CLASS=nsCSSPseudoClasses
// MACRO_NAME=CSS_PSEUDO_CLASS // MACRO_NAME=CSS_PSEUDO_CLASS
#ifdef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS
#error "CSS_STATE_DEPENDENT_PSEUDO_CLASS shouldn't be defined"
#endif
#ifndef CSS_STATE_DEPENDENT_PSEUDO_CLASS
#define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _bit) \
CSS_PSEUDO_CLASS(_name, _value)
#define DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS
#endif
#ifdef DEFINED_CSS_STATE_PSEUDO_CLASS #ifdef DEFINED_CSS_STATE_PSEUDO_CLASS
#error "This shouldn't be defined" #error "CSS_STATE_PSEUDO_CLASS shouldn't be defined"
#endif #endif
#ifndef CSS_STATE_PSEUDO_CLASS #ifndef CSS_STATE_PSEUDO_CLASS
#define CSS_STATE_PSEUDO_CLASS(_name, _value, _bit) \ #define CSS_STATE_PSEUDO_CLASS(_name, _value, _bit) \
CSS_PSEUDO_CLASS(_name, _value) CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _bit)
#define DEFINED_CSS_STATE_PSEUDO_CLASS #define DEFINED_CSS_STATE_PSEUDO_CLASS
#endif #endif
@ -90,6 +108,11 @@ CSS_PSEUDO_CLASS(mozTableBorderNonzero, ":-moz-table-border-nonzero")
// it doesn't actually get directly matched on in SelectorMatches. // it doesn't actually get directly matched on in SelectorMatches.
CSS_PSEUDO_CLASS(notPseudo, ":not") CSS_PSEUDO_CLASS(notPseudo, ":not")
// :dir(ltr) and :dir(rtl) match elements whose resolved directionality
// in the markup language is ltr or rtl respectively
CSS_STATE_DEPENDENT_PSEUDO_CLASS(dir, ":dir",
NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL)
CSS_STATE_PSEUDO_CLASS(link, ":link", NS_EVENT_STATE_UNVISITED) CSS_STATE_PSEUDO_CLASS(link, ":link", NS_EVENT_STATE_UNVISITED)
// what matches :link or :visited // what matches :link or :visited
CSS_STATE_PSEUDO_CLASS(mozAnyLink, ":-moz-any-link", CSS_STATE_PSEUDO_CLASS(mozAnyLink, ":-moz-any-link",
@ -178,3 +201,8 @@ CSS_STATE_PSEUDO_CLASS(mozMeterSubSubOptimum, ":-moz-meter-sub-sub-optimum",
#undef DEFINED_CSS_STATE_PSEUDO_CLASS #undef DEFINED_CSS_STATE_PSEUDO_CLASS
#undef CSS_STATE_PSEUDO_CLASS #undef CSS_STATE_PSEUDO_CLASS
#endif #endif
#ifdef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS
#undef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS
#undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
#endif

View File

@ -42,7 +42,8 @@ nsCSSPseudoClasses::HasStringArg(Type aType)
return aType == ePseudoClass_lang || return aType == ePseudoClass_lang ||
aType == ePseudoClass_mozEmptyExceptChildrenWithLocalname || aType == ePseudoClass_mozEmptyExceptChildrenWithLocalname ||
aType == ePseudoClass_mozSystemMetric || aType == ePseudoClass_mozSystemMetric ||
aType == ePseudoClass_mozLocaleDir; aType == ePseudoClass_mozLocaleDir ||
aType == ePseudoClass_dir;
} }
bool bool

View File

@ -1308,7 +1308,7 @@ struct NodeMatchContext {
// event-state-dependent selector for any value of that event state. // event-state-dependent selector for any value of that event state.
// So mStateMask contains the states that should NOT be tested. // So mStateMask contains the states that should NOT be tested.
// //
// NOTE: For |aStateMask| to work correctly, it's important that any // NOTE: For |mStateMask| to work correctly, it's important that any
// change that changes multiple state bits include all those state // change that changes multiple state bits include all those state
// bits in the notification. Otherwise, if multiple states change but // bits in the notification. Otherwise, if multiple states change but
// we do separate notifications then we might determine the style is // we do separate notifications then we might determine the style is
@ -1533,7 +1533,21 @@ checkGenericEmptyMatches(Element* aElement,
return (child == nullptr); return (child == nullptr);
} }
// An array of the states that are relevant for various pseudoclasses. // Arrays of the states that are relevant for various pseudoclasses.
static const nsEventStates sPseudoClassStateDependences[] = {
#define CSS_PSEUDO_CLASS(_name, _value) \
nsEventStates(),
#define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _states) \
_states,
#include "nsCSSPseudoClassList.h"
#undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
#undef CSS_PSEUDO_CLASS
// Add more entries for our fake values to make sure we can't
// index out of bounds into this array no matter what.
nsEventStates(),
nsEventStates()
};
static const nsEventStates sPseudoClassStates[] = { static const nsEventStates sPseudoClassStates[] = {
#define CSS_PSEUDO_CLASS(_name, _value) \ #define CSS_PSEUDO_CLASS(_name, _value) \
nsEventStates(), nsEventStates(),
@ -1556,7 +1570,7 @@ MOZ_STATIC_ASSERT(NS_ARRAY_LENGTH(sPseudoClassStates) ==
// * when non-null, it indicates that we're processing a negation, // * when non-null, it indicates that we're processing a negation,
// which is done only when SelectorMatches calls itself recursively // which is done only when SelectorMatches calls itself recursively
// * what it points to should be set to true whenever a test is skipped // * what it points to should be set to true whenever a test is skipped
// because of aStateMask // because of aNodeMatchContent.mStateMask
static bool SelectorMatches(Element* aElement, static bool SelectorMatches(Element* aElement,
nsCSSSelector* aSelector, nsCSSSelector* aSelector,
NodeMatchContext& aNodeMatchContext, NodeMatchContext& aNodeMatchContext,
@ -1997,6 +2011,38 @@ static bool SelectorMatches(Element* aElement,
} }
break; break;
case nsCSSPseudoClasses::ePseudoClass_dir:
{
if (aDependence) {
nsEventStates states
= sPseudoClassStateDependences[pseudoClass->mType];
if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) {
*aDependence = true;
return false;
}
}
// if we only had to consider HTML, directionality would be exclusively
// LTR or RTL, and this could be just
//
// if (dirString.EqualsLiteral("rtl") !=
// aElement->StyleState().HasState(NS_EVENT_STATE_RTL)
//
// However, in markup languages where there is no direction attribute
// we have to consider the possibility that neither dir(rtl) nor
// dir(ltr) matches.
nsEventStates state = aElement->StyleState();
bool elementIsRTL = state.HasState(NS_EVENT_STATE_RTL);
bool elementIsLTR = state.HasState(NS_EVENT_STATE_LTR);
nsDependentString dirString(pseudoClass->u.mString);
if ((dirString.EqualsLiteral("rtl") && !elementIsRTL) ||
(dirString.EqualsLiteral("ltr") && !elementIsLTR)) {
return false;
}
}
break;
default: default:
NS_ABORT_IF_FALSE(false, "How did that happen?"); NS_ABORT_IF_FALSE(false, "How did that happen?");
} }
@ -2128,10 +2174,10 @@ static bool SelectorMatches(Element* aElement,
bool dependence = false; bool dependence = false;
result = !SelectorMatches(aElement, negation, aNodeMatchContext, result = !SelectorMatches(aElement, negation, aNodeMatchContext,
aTreeMatchContext, &dependence); aTreeMatchContext, &dependence);
// If the selector does match due to the dependence on aStateMask, // If the selector does match due to the dependence on
// then we want to keep result true so that the final result of // aNodeMatchContext.mStateMask, then we want to keep result true
// SelectorMatches is true. Doing so tells StateEnumFunc that // so that the final result of SelectorMatches is true. Doing so
// there is a dependence on the state. // tells StateEnumFunc that there is a dependence on the state.
result = result || dependence; result = result || dependence;
} }
} }
@ -2665,7 +2711,7 @@ nsEventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) { if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
continue; continue;
} }
states |= sPseudoClassStates[pseudoClass->mType]; states |= sPseudoClassStateDependences[pseudoClass->mType];
} }
return states; return states;
} }