示例#1
0
        //
        // Parse pseudo-classes and pseudo-elements
        //
        internal nsSelectorParsingStatus ParsePseudoSelector(ref int32_t       aDataMask,
                                           nsCSSSelector aSelector,
                                           bool           aIsNegated,
                                           ref string      aPseudoElement,
                                           object   aPseudoElementArgs,
                                           ref nsCSSPseudoElement aPseudoElementType)
        {
            Debug.Assert(!aIsNegated || (aPseudoElement == null && aPseudoElementArgs == null),
                       "negated selectors shouldn't have a place to store pseudo elements");
              if (! GetToken(false)) { // premature eof
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelEOF"); };
            return nsSelectorParsingStatus.Error;
              }

              // First, find out whether we are parsing a CSS3 pseudo-element
              bool parsingPseudoElement = false;
              if (mToken.IsSymbol(':')) {
            parsingPseudoElement = true;
            if (! GetToken(false)) { // premature eof
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelEOF"); };
              return nsSelectorParsingStatus.Error;
            }
              }

              // Do some sanity-checking on the token
              if (nsCSSTokenType.Ident != mToken.mType && nsCSSTokenType.Function != mToken.mType) {
            // malformed selector
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelBadName", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error;
              }

              // OK, now we know we have an mIdent.  Atomize it.  All the atoms, for
              // pseudo-classes as well as pseudo-elements, start with a single ':'
              string pseudo = String.Intern(":" + mToken.mIdentStr);

              // stash away some info about this pseudo so we only have to get it once.
              bool isTreePseudo = false;
              nsCSSPseudoElement pseudoElementType =
            nsCSSPseudoElements.GetPseudoType(pseudo);
              nsCSSPseudoClass pseudoClassType =
            nsCSSPseudoClasses.GetPseudoType(pseudo);

              // We currently allow :-moz-placeholder and .-moz-placeholder. We have to
              // be a bit stricter regarding the pseudo-element parsing rules.
              if (pseudoElementType == nsCSSPseudoElement.MozPlaceholder &&
              pseudoClassType == nsCSSPseudoClass.MozPlaceholder) {
            if (parsingPseudoElement) {
              pseudoClassType = nsCSSPseudoClass.NotPseudoClass;
            } else {
              pseudoElementType = nsCSSPseudoElement.NotPseudoElement;
            }
              }

            #if MOZ_XUL
              isTreePseudo = (pseudoElementType == nsCSSPseudoElement.Xultree);
              // If a tree pseudo-element is using the function syntax, it will
              // get isTree set here and will pass the check below that only
              // allows functions if they are in our list of things allowed to be
              // functions.  If it is _not_ using the function syntax, isTree will
              // be false, and it will still pass that check.  So the tree
              // pseudo-elements are allowed to be either functions or not, as
              // desired.
              bool isTree = (nsCSSTokenType.Function == mToken.mType) && isTreePseudo;
            #endif
              bool isPseudoElement =
            (pseudoElementType < nsCSSPseudoElement.PseudoElementCount);
              // anonymous boxes are only allowed if they're the tree boxes or we have
              // enabled unsafe rules
              bool isAnonBox = isTreePseudo ||
            (pseudoElementType == nsCSSPseudoElement.AnonBox &&
             mUnsafeRulesEnabled);
              bool isPseudoClass =
            (pseudoClassType != nsCSSPseudoClass.NotPseudoClass);

              Debug.Assert(!isPseudoClass ||
                       pseudoElementType == nsCSSPseudoElement.NotPseudoElement,
                       "Why is this atom both a pseudo-class and a pseudo-element?");
              Debug.Assert((isPseudoClass?1:0) + (isPseudoElement?1:0) + (isAnonBox?1:0) <= 1,
                       "Shouldn't be more than one of these");

              if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
            // Not a pseudo-class, not a pseudo-element.... forget it
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelUnknown", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error;
              }

              // If it's a function token, it better be on our "ok" list, and if the name
              // is that of a function pseudo it better be a function token
              if ((nsCSSTokenType.Function == mToken.mType) !=
              (
            #if MOZ_XUL
               isTree ||
            #endif
               nsCSSPseudoClass.NotPseudo == pseudoClassType ||
               nsCSSPseudoClasses.HasStringArg(pseudoClassType) ||
               nsCSSPseudoClasses.HasNthPairArg(pseudoClassType) ||
               nsCSSPseudoClasses.HasSelectorListArg(pseudoClassType))) {
            // There are no other function pseudos
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelNonFunc", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error;
              }

              // If it starts with ".", it better be a pseudo-element
              if (parsingPseudoElement &&
              !isPseudoElement &&
              !isAnonBox) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelNotPE", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error;
              }

              if (!parsingPseudoElement &&
              nsCSSPseudoClass.NotPseudo == pseudoClassType) {
            if (aIsNegated) { // :not() can't be itself negated
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelDoubleNot", mToken); };
              UngetToken();
              return nsSelectorParsingStatus.Error;
            }
            // CSS 3 Negation pseudo-class takes one simple selector as argument
            nsSelectorParsingStatus parsingStatus =
              ParseNegatedSimpleSelector(ref aDataMask, aSelector);
            if (nsSelectorParsingStatus.Continue != parsingStatus) {
              return parsingStatus;
            }
              }
              else if (!parsingPseudoElement && isPseudoClass) {
            aDataMask |= SEL_MASK_PCLASS;
            if (nsCSSTokenType.Function == mToken.mType) {
              nsSelectorParsingStatus parsingStatus;
              if (nsCSSPseudoClasses.HasStringArg(pseudoClassType)) {
                parsingStatus =
                  ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
              }
              else if (nsCSSPseudoClasses.HasNthPairArg(pseudoClassType)) {
                parsingStatus =
                  ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
              }
              else {
                Debug.Assert(nsCSSPseudoClasses.HasSelectorListArg(pseudoClassType),
                                  "unexpected pseudo with function token");
                parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
                                                                    pseudoClassType);
              }
              if (nsSelectorParsingStatus.Continue != parsingStatus) {
                if (nsSelectorParsingStatus.Error == parsingStatus) {
                  SkipUntil(')');
                }
                return parsingStatus;
              }
            }
            else {
              aSelector.AddPseudoClass(pseudoClassType);
            }
              }
              else if (isPseudoElement || isAnonBox) {
            // Pseudo-element.  Make some more sanity checks.

            if (aIsNegated) { // pseudo-elements can't be negated
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelPEInNot", mToken); };
              UngetToken();
              return nsSelectorParsingStatus.Error;
            }
            // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
            // to have a single ':' on them.  Others (CSS3+ pseudo-elements and
            // various -moz-* pseudo-elements) must have |parsingPseudoElement|
            // set.
            if (!parsingPseudoElement &&
                !nsCSSPseudoElements.IsCSS2PseudoElement(pseudo)
            #if MOZ_XUL
                && !isTreePseudo
            #endif
                ) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelNewStyleOnly", mToken); };
              UngetToken();
              return nsSelectorParsingStatus.Error;
            }

            if (0 == (aDataMask & SEL_MASK_PELEM)) {
              aDataMask |= SEL_MASK_PELEM;
              aPseudoElement = pseudo;
              aPseudoElementType = pseudoElementType;

            #if MOZ_XUL
              if (isTree) {
                // We have encountered a pseudoelement of the form
                // -moz-tree-xxxx(a,b,c).  We parse (a,b,c) and add each
                // item in the list to the pseudoclass list.  They will be pulled
                // from the list later along with the pseudo-element.
                if (!ParseTreePseudoElement(aPseudoElementArgs)) {
                  return nsSelectorParsingStatus.Error;
                }
              }
            #endif

              // the next *non*whitespace token must be '{' or ',' or EOF
              if (!GetToken(true)) { // premature eof is ok (here!)
                return nsSelectorParsingStatus.Done;
              }
              if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
                UngetToken();
                return nsSelectorParsingStatus.Done;
              }
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelTrailing", mToken); };
              UngetToken();
              return nsSelectorParsingStatus.Error;
            }
            else {  // multiple pseudo elements, not legal
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoSelMultiplePE", mToken); };
              UngetToken();
              return nsSelectorParsingStatus.Error;
            }
              }
            #if DEBUG
              else {
            // We should never end up here.  Indeed, if we ended up here, we know (from
            // the current if/else cascade) that !isPseudoElement and !isAnonBox.  But
            // then due to our earlier check we know that isPseudoClass.  Since we
            // didn't fall into the isPseudoClass case in this cascade, we must have
            // parsingPseudoElement.  But we've already checked the
            // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
            // it's happened.
            Debug.Fail("How did this happen?");
              }
            #endif
              return nsSelectorParsingStatus.Continue;
        }
示例#2
0
        internal nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector aSelector,
                                                      nsCSSPseudoClass aType)
        {
            int32_t[] numbers = { 0, 0 };
              bool lookForB = true;

              // Follow the whitespace rules as proposed in
              // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html

              if (! GetToken(true)) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgEOF"); };
            return nsSelectorParsingStatus.Error;
              }

              if (nsCSSTokenType.Ident == mToken.mType || nsCSSTokenType.Dimension == mToken.mType) {
            // The CSS tokenization doesn't handle :nth-child() containing - well:
            //   2n-1 is a dimension
            //   n-1 is an identifier
            // The easiest way to deal with that is to push everything from the
            // minus on back onto the scanner's pushback buffer.
            uint32_t truncAt = 0;
            if (StringBeginsWith(mToken.mIdentStr, "n-")) {
              truncAt = 1;
            } else if (StringBeginsWith(mToken.mIdentStr, "-n-")) {
              truncAt = 2;
            }
            if (truncAt != 0) {
              mScanner.Backup(mToken.mIdentStr.Length() - truncAt);
              mToken.mIdentStr = mToken.mIdentStr.Substring(0, truncAt);
            }
              }

              if (nsCSSTokenType.Ident == mToken.mType) {
            if (mToken.mIdentStr.LowerCaseEqualsLiteral("odd")) {
              numbers[0] = 2;
              numbers[1] = 1;
              lookForB = false;
            }
            else if (mToken.mIdentStr.LowerCaseEqualsLiteral("even")) {
              numbers[0] = 2;
              numbers[1] = 0;
              lookForB = false;
            }
            else if (mToken.mIdentStr.LowerCaseEqualsLiteral("n")) {
              numbers[0] = 1;
            }
            else if (mToken.mIdentStr.LowerCaseEqualsLiteral("-n")) {
              numbers[0] = -1;
            }
            else {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotNth", mToken); };
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
              }
              else if (nsCSSTokenType.Number == mToken.mType) {
            if (!mToken.mIntegerValid) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotNth", mToken); };
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
            numbers[1] = mToken.mInteger;
            lookForB = false;
              }
              else if (nsCSSTokenType.Dimension == mToken.mType) {
            if (!mToken.mIntegerValid || !mToken.mIdentStr.LowerCaseEqualsLiteral("n")) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotNth", mToken); };
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
            numbers[0] = mToken.mInteger;
              }
              // XXX If it's a ')', is that valid?  (as 0n+0)
              else {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotNth", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }

              if (! GetToken(true)) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgEOF"); };
            return nsSelectorParsingStatus.Error;
              }
              if (lookForB && !mToken.IsSymbol(')')) {
            // The '+' or '-' sign can optionally be separated by whitespace.
            // If it is separated by whitespace from what follows it, it appears
            // as a separate token rather than part of the number token.
            bool haveSign = false;
            int32_t sign = 1;
            if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
              haveSign = true;
              if (mToken.IsSymbol('-')) {
                sign = -1;
              }
              if (! GetToken(true)) {
                { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgEOF"); };
                return nsSelectorParsingStatus.Error;
              }
            }
            if (nsCSSTokenType.Number != mToken.mType ||
                !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotNth", mToken); };
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
            numbers[1] = mToken.mInteger * sign;
            if (! GetToken(true)) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgEOF"); };
              return nsSelectorParsingStatus.Error;
            }
              }
              if (!mToken.IsSymbol(')')) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassNoClose", mToken); };
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }
              aSelector.AddPseudoClass(aType, numbers);
              return nsSelectorParsingStatus.Continue;
        }
示例#3
0
        //
        // Parse the argument of a pseudo-class that has a selector list argument.
        // Such selector lists cannot contain combinators, but can contain
        // anything that goes between a pair of combinators.
        //
        internal nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector aSelector,
                                                           nsCSSPseudoClass aType)
        {
            var slist = new nsCSSSelectorList();
              if (! ParseSelectorList(ref slist, ')')) {
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }

              // Check that none of the selectors in the list have combinators or
              // pseudo-elements.
              for (nsCSSSelectorList l = slist; l != null; l = l.mNext) {
            nsCSSSelector s = l.mSelectors;
            if (s.mNext != null || s.IsPseudoElement()) {
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
              }

              // Add the pseudo with the selector list parameter
              aSelector.AddPseudoClass(aType, slist);

              // close the parenthesis
              if (!ExpectSymbol(')', true)) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassNoClose", mToken); };
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }

              return nsSelectorParsingStatus.Continue;
        }
示例#4
0
        //
        // Parse the argument of a pseudo-class that has an ident arg
        //
        internal nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector aSelector,
                                                    nsCSSPseudoClass aType)
        {
            if (! GetToken(true)) { // premature eof
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgEOF"); };
            return nsSelectorParsingStatus.Error;
              }
              // We expect an identifier with a language abbreviation
              if (nsCSSTokenType.Ident != mToken.mType) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassArgNotIdent", mToken); };
            UngetToken();
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }

              // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'.
              if (aType == nsCSSPseudoClass.MozLocaleDir ||
              aType == nsCSSPseudoClass.Dir) {
            mToken.mIdentStr = mToken.mIdentStr.ToLower(); // case insensitive
            if (!mToken.mIdentStr.EqualsLiteral("ltr") &&
                !mToken.mIdentStr.EqualsLiteral("rtl")) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEBadDirValue", mToken); };
              return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
            }
              }

              // Add the pseudo with the language parameter
              aSelector.AddPseudoClass(aType, mToken.mIdentStr);

              // close the parenthesis
              if (!ExpectSymbol(')', true)) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEPseudoClassNoClose", mToken); };
            return nsSelectorParsingStatus.Error; // our caller calls SkipUntil(')')
              }

              return nsSelectorParsingStatus.Continue;
        }