LCOV - code coverage report
Current view: directory - layout/style - nsCSSScanner.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 663 142 21.4 %
Date: 2012-06-02 Functions: 44 19 43.2 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   L. David Baron <dbaron@dbaron.org>
      24                 :  *   Daniel Glazman <glazman@netscape.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : 
      41                 : /* tokenization of CSS style sheets */
      42                 : 
      43                 : #include <math.h> // must be first due to symbol conflicts
      44                 : 
      45                 : #include "nsCSSScanner.h"
      46                 : #include "nsString.h"
      47                 : #include "nsCRT.h"
      48                 : #include "mozilla/Util.h"
      49                 : 
      50                 : // for #ifdef CSS_REPORT_PARSE_ERRORS
      51                 : #include "nsCOMPtr.h"
      52                 : #include "nsIServiceManager.h"
      53                 : #include "nsIComponentManager.h"
      54                 : #include "nsReadableUtils.h"
      55                 : #include "nsIURI.h"
      56                 : #include "nsIConsoleService.h"
      57                 : #include "nsIScriptError.h"
      58                 : #include "nsIStringBundle.h"
      59                 : #include "nsIDocument.h"
      60                 : #include "mozilla/Services.h"
      61                 : #include "mozilla/css/Loader.h"
      62                 : #include "nsCSSStyleSheet.h"
      63                 : #include "mozilla/Preferences.h"
      64                 : 
      65                 : using namespace mozilla;
      66                 : 
      67                 : #ifdef CSS_REPORT_PARSE_ERRORS
      68                 : static bool gReportErrors = true;
      69                 : static nsIConsoleService *gConsoleService;
      70                 : static nsIFactory *gScriptErrorFactory;
      71                 : static nsIStringBundle *gStringBundle;
      72                 : #endif
      73                 : 
      74                 : static const PRUint8 IS_HEX_DIGIT  = 0x01;
      75                 : static const PRUint8 START_IDENT   = 0x02;
      76                 : static const PRUint8 IS_IDENT      = 0x04;
      77                 : static const PRUint8 IS_WHITESPACE = 0x08;
      78                 : 
      79                 : #define W   IS_WHITESPACE
      80                 : #define I   IS_IDENT
      81                 : #define S            START_IDENT
      82                 : #define SI  IS_IDENT|START_IDENT
      83                 : #define XI  IS_IDENT            |IS_HEX_DIGIT
      84                 : #define XSI IS_IDENT|START_IDENT|IS_HEX_DIGIT
      85                 : 
      86                 : static const PRUint8 gLexTable[] = {
      87                 : //                                     TAB LF      FF  CR
      88                 :    0,  0,  0,  0,  0,  0,  0,  0,  0,  W,  W,  0,  W,  W,  0,  0,
      89                 : //
      90                 :    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      91                 : // SPC !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
      92                 :    W,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  I,  0,  0,
      93                 : // 0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
      94                 :    XI, XI, XI, XI, XI, XI, XI, XI, XI, XI, 0,  0,  0,  0,  0,  0,
      95                 : // @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
      96                 :    0,  XSI,XSI,XSI,XSI,XSI,XSI,SI, SI, SI, SI, SI, SI, SI, SI, SI,
      97                 : // P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
      98                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, 0,  S,  0,  0,  SI,
      99                 : // `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
     100                 :    0,  XSI,XSI,XSI,XSI,XSI,XSI,SI, SI, SI, SI, SI, SI, SI, SI, SI,
     101                 : // p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~
     102                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, 0,  0,  0,  0,  0,
     103                 : // U+008*
     104                 :    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     105                 : // U+009*
     106                 :    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     107                 : // U+00A*
     108                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     109                 : // U+00B*
     110                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     111                 : // U+00C*
     112                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     113                 : // U+00D*
     114                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     115                 : // U+00E*
     116                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     117                 : // U+00F*
     118                 :    SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI, SI,
     119                 : };
     120                 : 
     121                 : MOZ_STATIC_ASSERT(NS_ARRAY_LENGTH(gLexTable) == 256,
     122                 :                   "gLexTable expected to cover all 2^8 possible PRUint8s");
     123                 : 
     124                 : #undef W
     125                 : #undef S
     126                 : #undef I
     127                 : #undef XI
     128                 : #undef SI
     129                 : #undef XSI
     130                 : 
     131                 : static inline bool
     132             550 : IsIdentStart(PRInt32 aChar)
     133                 : {
     134                 :   return aChar >= 0 &&
     135             550 :     (aChar >= 256 || (gLexTable[aChar] & START_IDENT) != 0);
     136                 : }
     137                 : 
     138                 : static inline bool
     139             550 : StartsIdent(PRInt32 aFirstChar, PRInt32 aSecondChar)
     140                 : {
     141             550 :   return IsIdentStart(aFirstChar) ||
     142             550 :     (aFirstChar == '-' && IsIdentStart(aSecondChar));
     143                 : }
     144                 : 
     145                 : static inline bool
     146             330 : IsWhitespace(PRInt32 ch) {
     147             330 :   return PRUint32(ch) < 256 && (gLexTable[ch] & IS_WHITESPACE) != 0;
     148                 : }
     149                 : 
     150                 : static inline bool
     151             330 : IsDigit(PRInt32 ch) {
     152             330 :   return (ch >= '0') && (ch <= '9');
     153                 : }
     154                 : 
     155                 : static inline bool
     156               0 : IsHexDigit(PRInt32 ch) {
     157               0 :   return PRUint32(ch) < 256 && (gLexTable[ch] & IS_HEX_DIGIT) != 0;
     158                 : }
     159                 : 
     160                 : static inline bool
     161            3474 : IsIdent(PRInt32 ch) {
     162            3474 :   return ch >= 0 && (ch >= 256 || (gLexTable[ch] & IS_IDENT) != 0);
     163                 : }
     164                 : 
     165                 : static inline PRUint32
     166               0 : DecimalDigitValue(PRInt32 ch)
     167                 : {
     168               0 :   return ch - '0';
     169                 : }
     170                 : 
     171                 : static inline PRUint32
     172               0 : HexDigitValue(PRInt32 ch)
     173                 : {
     174               0 :   if (IsDigit(ch)) {
     175               0 :     return DecimalDigitValue(ch);
     176                 :   } else {
     177                 :     // Note: c&7 just keeps the low three bits which causes
     178                 :     // upper and lower case alphabetics to both yield their
     179                 :     // "relative to 10" value for computing the hex value.
     180               0 :     return (ch & 0x7) + 9;
     181                 :   }
     182                 : }
     183                 : 
     184              12 : nsCSSToken::nsCSSToken()
     185                 : {
     186              12 :   mType = eCSSToken_Symbol;
     187              12 : }
     188                 : 
     189                 : void
     190               0 : nsCSSToken::AppendToString(nsString& aBuffer)
     191                 : {
     192               0 :   switch (mType) {
     193                 :     case eCSSToken_AtKeyword:
     194               0 :       aBuffer.Append(PRUnichar('@')); // fall through intentional
     195                 :     case eCSSToken_Ident:
     196                 :     case eCSSToken_WhiteSpace:
     197                 :     case eCSSToken_Function:
     198                 :     case eCSSToken_HTMLComment:
     199                 :     case eCSSToken_URange:
     200               0 :       aBuffer.Append(mIdent);
     201               0 :       if (mType == eCSSToken_Function)
     202               0 :         aBuffer.Append(PRUnichar('('));
     203               0 :       break;
     204                 :     case eCSSToken_URL:
     205                 :     case eCSSToken_Bad_URL:
     206               0 :       aBuffer.AppendLiteral("url(");
     207               0 :       if (mSymbol != PRUnichar(0)) {
     208               0 :         aBuffer.Append(mSymbol);
     209                 :       }
     210               0 :       aBuffer.Append(mIdent);
     211               0 :       if (mSymbol != PRUnichar(0)) {
     212               0 :         aBuffer.Append(mSymbol);
     213                 :       }
     214               0 :       if (mType == eCSSToken_URL) {
     215               0 :         aBuffer.Append(PRUnichar(')'));
     216                 :       }
     217               0 :       break;
     218                 :     case eCSSToken_Number:
     219               0 :       if (mIntegerValid) {
     220               0 :         aBuffer.AppendInt(mInteger, 10);
     221                 :       }
     222                 :       else {
     223               0 :         aBuffer.AppendFloat(mNumber);
     224                 :       }
     225               0 :       break;
     226                 :     case eCSSToken_Percentage:
     227               0 :       NS_ASSERTION(!mIntegerValid, "How did a percentage token get this set?");
     228               0 :       aBuffer.AppendFloat(mNumber * 100.0f);
     229               0 :       aBuffer.Append(PRUnichar('%'));
     230               0 :       break;
     231                 :     case eCSSToken_Dimension:
     232               0 :       if (mIntegerValid) {
     233               0 :         aBuffer.AppendInt(mInteger, 10);
     234                 :       }
     235                 :       else {
     236               0 :         aBuffer.AppendFloat(mNumber);
     237                 :       }
     238               0 :       aBuffer.Append(mIdent);
     239               0 :       break;
     240                 :     case eCSSToken_String:
     241               0 :       aBuffer.Append(mSymbol);
     242               0 :       aBuffer.Append(mIdent); // fall through intentional
     243                 :     case eCSSToken_Symbol:
     244               0 :       aBuffer.Append(mSymbol);
     245               0 :       break;
     246                 :     case eCSSToken_ID:
     247                 :     case eCSSToken_Ref:
     248               0 :       aBuffer.Append(PRUnichar('#'));
     249               0 :       aBuffer.Append(mIdent);
     250               0 :       break;
     251                 :     case eCSSToken_Includes:
     252               0 :       aBuffer.AppendLiteral("~=");
     253               0 :       break;
     254                 :     case eCSSToken_Dashmatch:
     255               0 :       aBuffer.AppendLiteral("|=");
     256               0 :       break;
     257                 :     case eCSSToken_Beginsmatch:
     258               0 :       aBuffer.AppendLiteral("^=");
     259               0 :       break;
     260                 :     case eCSSToken_Endsmatch:
     261               0 :       aBuffer.AppendLiteral("$=");
     262               0 :       break;
     263                 :     case eCSSToken_Containsmatch:
     264               0 :       aBuffer.AppendLiteral("*=");
     265               0 :       break;
     266                 :     case eCSSToken_Bad_String:
     267               0 :       aBuffer.Append(mSymbol);
     268               0 :       aBuffer.Append(mIdent);
     269               0 :       break;
     270                 :     default:
     271               0 :       NS_ERROR("invalid token type");
     272               0 :       break;
     273                 :   }
     274               0 : }
     275                 : 
     276              12 : nsCSSScanner::nsCSSScanner()
     277                 :   : mReadPointer(nsnull)
     278                 :   , mSVGMode(false)
     279                 : #ifdef CSS_REPORT_PARSE_ERRORS
     280                 :   , mError(mErrorBuf, ArrayLength(mErrorBuf), 0)
     281                 :   , mInnerWindowID(0)
     282                 :   , mWindowIDCached(false)
     283                 :   , mSheet(nsnull)
     284              12 :   , mLoader(nsnull)
     285                 : #endif
     286                 : {
     287              12 :   MOZ_COUNT_CTOR(nsCSSScanner);
     288              12 :   mPushback = mLocalPushback;
     289              12 :   mPushbackSize = ArrayLength(mLocalPushback);
     290                 :   // No need to init the other members, since they represent state
     291                 :   // which can get cleared.  We'll init them every time Init() is
     292                 :   // called.
     293              12 : }
     294                 : 
     295              24 : nsCSSScanner::~nsCSSScanner()
     296                 : {
     297              12 :   MOZ_COUNT_DTOR(nsCSSScanner);
     298              12 :   Close();
     299              12 :   if (mLocalPushback != mPushback) {
     300               0 :     delete [] mPushback;
     301                 :   }
     302              12 : }
     303                 : 
     304                 : #ifdef CSS_REPORT_PARSE_ERRORS
     305                 : #define CSS_ERRORS_PREF "layout.css.report_errors"
     306                 : 
     307                 : static int
     308               0 : CSSErrorsPrefChanged(const char *aPref, void *aClosure)
     309                 : {
     310               0 :   gReportErrors = Preferences::GetBool(CSS_ERRORS_PREF, true);
     311               0 :   return NS_OK;
     312                 : }
     313                 : #endif
     314                 : 
     315                 : /* static */ bool
     316               0 : nsCSSScanner::InitGlobals()
     317                 : {
     318                 : #ifdef CSS_REPORT_PARSE_ERRORS
     319               0 :   if (gConsoleService && gScriptErrorFactory)
     320               0 :     return true;
     321                 :   
     322               0 :   nsresult rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &gConsoleService);
     323               0 :   NS_ENSURE_SUCCESS(rv, false);
     324                 : 
     325               0 :   rv = CallGetClassObject(NS_SCRIPTERROR_CONTRACTID, &gScriptErrorFactory);
     326               0 :   NS_ENSURE_SUCCESS(rv, false);
     327               0 :   NS_ASSERTION(gConsoleService && gScriptErrorFactory,
     328                 :                "unexpected null pointer without failure");
     329                 : 
     330               0 :   Preferences::RegisterCallback(CSSErrorsPrefChanged, CSS_ERRORS_PREF);
     331               0 :   CSSErrorsPrefChanged(CSS_ERRORS_PREF, nsnull);
     332                 : #endif
     333               0 :   return true;
     334                 : }
     335                 : 
     336                 : /* static */ void
     337            1403 : nsCSSScanner::ReleaseGlobals()
     338                 : {
     339                 : #ifdef CSS_REPORT_PARSE_ERRORS
     340            1403 :   Preferences::UnregisterCallback(CSSErrorsPrefChanged, CSS_ERRORS_PREF);
     341            1403 :   NS_IF_RELEASE(gConsoleService);
     342            1403 :   NS_IF_RELEASE(gScriptErrorFactory);
     343            1403 :   NS_IF_RELEASE(gStringBundle);
     344                 : #endif
     345            1403 : }
     346                 : 
     347                 : void
     348             110 : nsCSSScanner::Init(const nsAString& aBuffer,
     349                 :                    nsIURI* aURI, PRUint32 aLineNumber,
     350                 :                    nsCSSStyleSheet* aSheet, mozilla::css::Loader* aLoader)
     351                 : {
     352             110 :   NS_PRECONDITION(!mReadPointer, "Should not have an existing input buffer!");
     353                 : 
     354             110 :   mReadPointer = aBuffer.BeginReading();
     355             110 :   mCount = aBuffer.Length();
     356                 : 
     357                 : #ifdef CSS_REPORT_PARSE_ERRORS
     358                 :   // If aURI is the same as mURI, no need to reget mFileName -- it
     359                 :   // shouldn't have changed.
     360             110 :   if (aURI != mURI) {
     361             110 :     mURI = aURI;
     362             110 :     if (aURI) {
     363             110 :       aURI->GetSpec(mFileName);
     364                 :     } else {
     365               0 :       mFileName.Adopt(NS_strdup("from DOM"));
     366                 :     }
     367                 :   }
     368                 : #endif // CSS_REPORT_PARSE_ERRORS
     369             110 :   mLineNumber = aLineNumber;
     370                 : 
     371                 :   // Reset variables that we use to keep track of our progress through the input
     372             110 :   mOffset = 0;
     373             110 :   mPushbackCount = 0;
     374                 : 
     375                 : #ifdef CSS_REPORT_PARSE_ERRORS
     376             110 :   mColNumber = 0;
     377             110 :   mSheet = aSheet;
     378             110 :   mLoader = aLoader;
     379                 : #endif
     380             110 : }
     381                 : 
     382                 : #ifdef CSS_REPORT_PARSE_ERRORS
     383                 : 
     384                 : // @see REPORT_UNEXPECTED_EOF in nsCSSParser.cpp
     385                 : #define REPORT_UNEXPECTED_EOF(lf_) \
     386                 :   ReportUnexpectedEOF(#lf_)
     387                 : 
     388                 : void
     389               0 : nsCSSScanner::AddToError(const nsSubstring& aErrorText)
     390                 : {
     391               0 :   if (mError.IsEmpty()) {
     392               0 :     mErrorLineNumber = mLineNumber;
     393               0 :     mErrorColNumber = mColNumber;
     394               0 :     mError = aErrorText;
     395                 :   } else {
     396               0 :     mError.Append(NS_LITERAL_STRING("  ") + aErrorText);
     397                 :   }
     398               0 : }
     399                 : 
     400                 : void
     401             110 : nsCSSScanner::ClearError()
     402                 : {
     403             110 :   mError.Truncate();
     404             110 : }
     405                 : 
     406                 : void
     407               0 : nsCSSScanner::OutputError()
     408                 : {
     409               0 :   if (mError.IsEmpty()) return;
     410                 :  
     411                 :   // Log it to the Error console
     412                 : 
     413               0 :   if (InitGlobals() && gReportErrors) {
     414               0 :     if (!mWindowIDCached) {
     415               0 :       if (mSheet) {
     416               0 :         mInnerWindowID = mSheet->FindOwningWindowInnerID();
     417                 :       }
     418               0 :       if (mInnerWindowID == 0 && mLoader) {
     419               0 :         nsIDocument* doc = mLoader->GetDocument();
     420               0 :         if (doc) {
     421               0 :           mInnerWindowID = doc->InnerWindowID();
     422                 :         }
     423                 :       }
     424               0 :       mWindowIDCached = true;
     425                 :     }
     426                 : 
     427                 :     nsresult rv;
     428                 :     nsCOMPtr<nsIScriptError> errorObject =
     429               0 :       do_CreateInstance(gScriptErrorFactory, &rv);
     430                 : 
     431               0 :     if (NS_SUCCEEDED(rv)) {
     432               0 :       rv = errorObject->InitWithWindowID(mError.get(),
     433               0 :                                          NS_ConvertUTF8toUTF16(mFileName).get(),
     434               0 :                                          EmptyString().get(),
     435                 :                                          mErrorLineNumber,
     436                 :                                          mErrorColNumber,
     437                 :                                          nsIScriptError::warningFlag,
     438                 :                                          "CSS Parser",
     439               0 :                                          mInnerWindowID);
     440               0 :       if (NS_SUCCEEDED(rv)) {
     441               0 :         gConsoleService->LogMessage(errorObject);
     442                 :       }
     443                 :     }
     444                 :   }
     445               0 :   ClearError();
     446                 : }
     447                 : 
     448                 : static bool
     449               0 : InitStringBundle()
     450                 : {
     451               0 :   if (gStringBundle)
     452               0 :     return true;
     453                 : 
     454                 :   nsCOMPtr<nsIStringBundleService> sbs =
     455               0 :     mozilla::services::GetStringBundleService();
     456               0 :   if (!sbs)
     457               0 :     return false;
     458                 : 
     459                 :   nsresult rv = 
     460               0 :     sbs->CreateBundle("chrome://global/locale/css.properties", &gStringBundle);
     461               0 :   if (NS_FAILED(rv)) {
     462               0 :     gStringBundle = nsnull;
     463               0 :     return false;
     464                 :   }
     465                 : 
     466               0 :   return true;
     467                 : }
     468                 : 
     469                 : #define ENSURE_STRINGBUNDLE \
     470                 :   PR_BEGIN_MACRO if (!InitStringBundle()) return; PR_END_MACRO
     471                 : 
     472                 : // aMessage must take no parameters
     473               0 : void nsCSSScanner::ReportUnexpected(const char* aMessage)
     474                 : {
     475               0 :   ENSURE_STRINGBUNDLE;
     476                 : 
     477               0 :   nsXPIDLString str;
     478               0 :   gStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     479               0 :                                    getter_Copies(str));
     480               0 :   AddToError(str);
     481                 : }
     482                 :   
     483                 : void
     484               0 : nsCSSScanner::ReportUnexpectedParams(const char* aMessage,
     485                 :                                      const PRUnichar **aParams,
     486                 :                                      PRUint32 aParamsLength)
     487                 : {
     488               0 :   NS_PRECONDITION(aParamsLength > 0, "use the non-params version");
     489               0 :   ENSURE_STRINGBUNDLE;
     490                 : 
     491               0 :   nsXPIDLString str;
     492               0 :   gStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
     493                 :                                       aParams, aParamsLength,
     494               0 :                                       getter_Copies(str));
     495               0 :   AddToError(str);
     496                 : }
     497                 : 
     498                 : // aLookingFor is a plain string, not a format string
     499                 : void
     500               0 : nsCSSScanner::ReportUnexpectedEOF(const char* aLookingFor)
     501                 : {
     502               0 :   ENSURE_STRINGBUNDLE;
     503                 : 
     504               0 :   nsXPIDLString innerStr;
     505               0 :   gStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aLookingFor).get(),
     506               0 :                                    getter_Copies(innerStr));
     507                 : 
     508                 :   const PRUnichar *params[] = {
     509               0 :     innerStr.get()
     510               0 :   };
     511               0 :   nsXPIDLString str;
     512               0 :   gStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
     513                 :                                       params, ArrayLength(params),
     514               0 :                                       getter_Copies(str));
     515               0 :   AddToError(str);
     516                 : }
     517                 : 
     518                 : // aLookingFor is a single character
     519                 : void
     520               0 : nsCSSScanner::ReportUnexpectedEOF(PRUnichar aLookingFor)
     521                 : {
     522               0 :   ENSURE_STRINGBUNDLE;
     523                 : 
     524                 :   const PRUnichar lookingForStr[] = {
     525                 :     PRUnichar('\''), aLookingFor, PRUnichar('\''), PRUnichar(0)
     526               0 :   };
     527               0 :   const PRUnichar *params[] = { lookingForStr };
     528               0 :   nsXPIDLString str;
     529               0 :   gStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
     530                 :                                       params, ArrayLength(params),
     531               0 :                                       getter_Copies(str));
     532               0 :   AddToError(str);
     533                 : }
     534                 : 
     535                 : // aMessage must take 1 parameter (for the string representation of the
     536                 : // unexpected token)
     537                 : void
     538               0 : nsCSSScanner::ReportUnexpectedToken(nsCSSToken& tok,
     539                 :                                     const char *aMessage)
     540                 : {
     541               0 :   ENSURE_STRINGBUNDLE;
     542                 :   
     543               0 :   nsAutoString tokenString;
     544               0 :   tok.AppendToString(tokenString);
     545                 : 
     546                 :   const PRUnichar *params[] = {
     547               0 :     tokenString.get()
     548               0 :   };
     549                 : 
     550               0 :   ReportUnexpectedParams(aMessage, params);
     551                 : }
     552                 : 
     553                 : // aParams's first entry must be null, and we'll fill in the token
     554                 : void
     555               0 : nsCSSScanner::ReportUnexpectedTokenParams(nsCSSToken& tok,
     556                 :                                           const char* aMessage,
     557                 :                                           const PRUnichar **aParams,
     558                 :                                           PRUint32 aParamsLength)
     559                 : {
     560               0 :   NS_PRECONDITION(aParamsLength > 1, "use the non-params version");
     561               0 :   NS_PRECONDITION(aParams[0] == nsnull, "first param should be empty");
     562                 : 
     563               0 :   ENSURE_STRINGBUNDLE;
     564                 :   
     565               0 :   nsAutoString tokenString;
     566               0 :   tok.AppendToString(tokenString);
     567               0 :   aParams[0] = tokenString.get();
     568                 : 
     569               0 :   ReportUnexpectedParams(aMessage, aParams, aParamsLength);
     570                 : }
     571                 : 
     572                 : #else
     573                 : 
     574                 : #define REPORT_UNEXPECTED_EOF(lf_)
     575                 : 
     576                 : #endif // CSS_REPORT_PARSE_ERRORS
     577                 : 
     578                 : void
     579             122 : nsCSSScanner::Close()
     580                 : {
     581             122 :   mReadPointer = nsnull;
     582                 : 
     583                 :   // Clean things up so we don't hold on to memory if our parser gets recycled.
     584                 : #ifdef CSS_REPORT_PARSE_ERRORS
     585             122 :   mFileName.Truncate();
     586             122 :   mURI = nsnull;
     587             122 :   mError.Truncate();
     588             122 :   mInnerWindowID = 0;
     589             122 :   mWindowIDCached = false;
     590             122 :   mSheet = nsnull;
     591             122 :   mLoader = nsnull;
     592                 : #endif
     593             122 :   if (mPushback != mLocalPushback) {
     594               0 :     delete [] mPushback;
     595               0 :     mPushback = mLocalPushback;
     596               0 :     mPushbackSize = ArrayLength(mLocalPushback);
     597                 :   }
     598             122 : }
     599                 : 
     600                 : // Returns -1 on error or eof
     601                 : PRInt32
     602            2310 : nsCSSScanner::Read()
     603                 : {
     604                 :   PRInt32 rv;
     605            2310 :   if (0 < mPushbackCount) {
     606             880 :     rv = PRInt32(mPushback[--mPushbackCount]);
     607                 :   } else {
     608            1430 :     if (mOffset == mCount) {
     609             660 :       return -1;
     610                 :     }
     611             770 :     rv = PRInt32(mReadPointer[mOffset++]);
     612                 :     // There are four types of newlines in CSS: "\r", "\n", "\r\n", and "\f".
     613                 :     // To simplify dealing with newlines, they are all normalized to "\n" here
     614             770 :     if (rv == '\r') {
     615               0 :       if (mOffset < mCount && mReadPointer[mOffset] == '\n') {
     616               0 :         mOffset++;
     617                 :       }
     618               0 :       rv = '\n';
     619             770 :     } else if (rv == '\f') {
     620               0 :       rv = '\n';
     621                 :     }
     622             770 :     if (rv == '\n') {
     623                 :       // 0 is a magical line number meaning that we don't know (i.e., script)
     624               0 :       if (mLineNumber != 0)
     625               0 :         ++mLineNumber;
     626                 : #ifdef CSS_REPORT_PARSE_ERRORS
     627               0 :       mColNumber = 0;
     628                 :     } else {
     629             770 :       mColNumber++;
     630                 : #endif
     631                 :     }
     632                 :   }
     633            1650 :   return rv;
     634                 : }
     635                 : 
     636                 : PRInt32
     637             770 : nsCSSScanner::Peek()
     638                 : {
     639             770 :   if (0 == mPushbackCount) {
     640             660 :     PRInt32 ch = Read();
     641             660 :     if (ch < 0) {
     642             110 :       return -1;
     643                 :     }
     644             550 :     mPushback[0] = PRUnichar(ch);
     645             550 :     mPushbackCount++;
     646                 :   }
     647             660 :   return PRInt32(mPushback[mPushbackCount - 1]);
     648                 : }
     649                 : 
     650                 : void
     651             330 : nsCSSScanner::Pushback(PRUnichar aChar)
     652                 : {
     653             330 :   if (mPushbackCount == mPushbackSize) { // grow buffer
     654               0 :     PRUnichar*  newPushback = new PRUnichar[mPushbackSize + 4];
     655               0 :     if (nsnull == newPushback) {
     656               0 :       return;
     657                 :     }
     658               0 :     mPushbackSize += 4;
     659               0 :     memcpy(newPushback, mPushback, sizeof(PRUnichar) * mPushbackCount);
     660               0 :     if (mPushback != mLocalPushback) {
     661               0 :       delete [] mPushback;
     662                 :     }
     663               0 :     mPushback = newPushback;
     664                 :   }
     665             330 :   mPushback[mPushbackCount++] = aChar;
     666                 : }
     667                 : 
     668                 : bool
     669               0 : nsCSSScanner::LookAhead(PRUnichar aChar)
     670                 : {
     671               0 :   PRInt32 ch = Read();
     672               0 :   if (ch < 0) {
     673               0 :     return false;
     674                 :   }
     675               0 :   if (ch == aChar) {
     676               0 :     return true;
     677                 :   }
     678               0 :   Pushback(ch);
     679               0 :   return false;
     680                 : }
     681                 : 
     682                 : bool
     683               0 : nsCSSScanner::LookAheadOrEOF(PRUnichar aChar)
     684                 : {
     685               0 :   PRInt32 ch = Read();
     686               0 :   if (ch < 0) {
     687               0 :     return true;
     688                 :   }
     689               0 :   if (ch == aChar) {
     690               0 :     return true;
     691                 :   }
     692               0 :   Pushback(ch);
     693               0 :   return false;
     694                 : }
     695                 : 
     696                 : void
     697             220 : nsCSSScanner::EatWhiteSpace()
     698                 : {
     699               0 :   for (;;) {
     700             220 :     PRInt32 ch = Read();
     701             220 :     if (ch < 0) {
     702               0 :       break;
     703                 :     }
     704             220 :     if ((ch != ' ') && (ch != '\n') && (ch != '\t')) {
     705             220 :       Pushback(ch);
     706             220 :       break;
     707                 :     }
     708                 :   }
     709             220 : }
     710                 : 
     711                 : bool
     712             990 : nsCSSScanner::Next(nsCSSToken& aToken)
     713                 : {
     714               0 :   for (;;) { // Infinite loop so we can restart after comments.
     715             990 :     PRInt32 ch = Read();
     716             990 :     if (ch < 0) {
     717             440 :       return false;
     718                 :     }
     719                 : 
     720                 :     // UNICODE-RANGE
     721             550 :     if ((ch == 'u' || ch == 'U') && Peek() == '+')
     722               0 :       return ParseURange(ch, aToken);
     723                 : 
     724                 :     // IDENT
     725             550 :     if (StartsIdent(ch, Peek()))
     726             220 :       return ParseIdent(ch, aToken);
     727                 : 
     728                 :     // AT_KEYWORD
     729             330 :     if (ch == '@') {
     730               0 :       PRInt32 nextChar = Read();
     731               0 :       if (nextChar >= 0) {
     732               0 :         PRInt32 followingChar = Peek();
     733               0 :         Pushback(nextChar);
     734               0 :         if (StartsIdent(nextChar, followingChar))
     735               0 :           return ParseAtKeyword(ch, aToken);
     736                 :       }
     737                 :     }
     738                 : 
     739                 :     // NUMBER or DIM
     740             330 :     if ((ch == '.') || (ch == '+') || (ch == '-')) {
     741               0 :       PRInt32 nextChar = Peek();
     742               0 :       if (IsDigit(nextChar)) {
     743               0 :         return ParseNumber(ch, aToken);
     744                 :       }
     745               0 :       else if (('.' == nextChar) && ('.' != ch)) {
     746               0 :         nextChar = Read();
     747               0 :         PRInt32 followingChar = Peek();
     748               0 :         Pushback(nextChar);
     749               0 :         if (IsDigit(followingChar))
     750               0 :           return ParseNumber(ch, aToken);
     751                 :       }
     752                 :     }
     753             330 :     if (IsDigit(ch)) {
     754               0 :       return ParseNumber(ch, aToken);
     755                 :     }
     756                 : 
     757                 :     // ID
     758             330 :     if (ch == '#') {
     759               0 :       return ParseRef(ch, aToken);
     760                 :     }
     761                 : 
     762                 :     // STRING
     763             330 :     if ((ch == '"') || (ch == '\'')) {
     764               0 :       return ParseString(ch, aToken);
     765                 :     }
     766                 : 
     767                 :     // WS
     768             330 :     if (IsWhitespace(ch)) {
     769             220 :       aToken.mType = eCSSToken_WhiteSpace;
     770             220 :       aToken.mIdent.Assign(PRUnichar(ch));
     771             220 :       EatWhiteSpace();
     772             220 :       return true;
     773                 :     }
     774             110 :     if (ch == '/' && !IsSVGMode()) {
     775               0 :       PRInt32 nextChar = Peek();
     776               0 :       if (nextChar == '*') {
     777               0 :         Read();
     778                 :         // FIXME: Editor wants comments to be preserved (bug 60290).
     779               0 :         if (!SkipCComment()) {
     780               0 :           return false;
     781                 :         }
     782               0 :         continue; // start again at the beginning
     783                 :       }
     784                 :     }
     785             110 :     if (ch == '<') {  // consume HTML comment tags
     786               0 :       if (LookAhead('!')) {
     787               0 :         if (LookAhead('-')) {
     788               0 :           if (LookAhead('-')) {
     789               0 :             aToken.mType = eCSSToken_HTMLComment;
     790               0 :             aToken.mIdent.AssignLiteral("<!--");
     791               0 :             return true;
     792                 :           }
     793               0 :           Pushback('-');
     794                 :         }
     795               0 :         Pushback('!');
     796                 :       }
     797                 :     }
     798             110 :     if (ch == '-') {  // check for HTML comment end
     799               0 :       if (LookAhead('-')) {
     800               0 :         if (LookAhead('>')) {
     801               0 :           aToken.mType = eCSSToken_HTMLComment;
     802               0 :           aToken.mIdent.AssignLiteral("-->");
     803               0 :           return true;
     804                 :         }
     805               0 :         Pushback('-');
     806                 :       }
     807                 :     }
     808                 : 
     809                 :     // INCLUDES ("~=") and DASHMATCH ("|=")
     810             110 :     if (( ch == '|' ) || ( ch == '~' ) || ( ch == '^' ) ||
     811                 :         ( ch == '$' ) || ( ch == '*' )) {
     812               0 :       PRInt32 nextChar = Read();
     813               0 :       if ( nextChar == '=' ) {
     814               0 :         if (ch == '~') {
     815               0 :           aToken.mType = eCSSToken_Includes;
     816                 :         }
     817               0 :         else if (ch == '|') {
     818               0 :           aToken.mType = eCSSToken_Dashmatch;
     819                 :         }
     820               0 :         else if (ch == '^') {
     821               0 :           aToken.mType = eCSSToken_Beginsmatch;
     822                 :         }
     823               0 :         else if (ch == '$') {
     824               0 :           aToken.mType = eCSSToken_Endsmatch;
     825                 :         }
     826               0 :         else if (ch == '*') {
     827               0 :           aToken.mType = eCSSToken_Containsmatch;
     828                 :         }
     829               0 :         return true;
     830               0 :       } else if (nextChar >= 0) {
     831               0 :         Pushback(nextChar);
     832                 :       }
     833                 :     }
     834             110 :     aToken.mType = eCSSToken_Symbol;
     835             110 :     aToken.mSymbol = ch;
     836             110 :     return true;
     837                 :   }
     838                 : }
     839                 : 
     840                 : bool
     841               0 : nsCSSScanner::NextURL(nsCSSToken& aToken)
     842                 : {
     843               0 :   EatWhiteSpace();
     844                 : 
     845               0 :   PRInt32 ch = Read();
     846               0 :   if (ch < 0) {
     847               0 :     return false;
     848                 :   }
     849                 : 
     850                 :   // STRING
     851               0 :   if ((ch == '"') || (ch == '\'')) {
     852                 : #ifdef DEBUG
     853                 :     bool ok =
     854                 : #endif
     855               0 :       ParseString(ch, aToken);
     856               0 :     NS_ABORT_IF_FALSE(ok, "ParseString should never fail, "
     857                 :                           "since there's always something read");
     858                 : 
     859               0 :     NS_ABORT_IF_FALSE(aToken.mType == eCSSToken_String ||
     860                 :                       aToken.mType == eCSSToken_Bad_String,
     861                 :                       "unexpected token type");
     862               0 :     if (NS_LIKELY(aToken.mType == eCSSToken_String)) {
     863               0 :       EatWhiteSpace();
     864               0 :       if (LookAheadOrEOF(')')) {
     865               0 :         aToken.mType = eCSSToken_URL;
     866                 :       } else {
     867               0 :         aToken.mType = eCSSToken_Bad_URL;
     868                 :       }
     869                 :     } else {
     870               0 :       aToken.mType = eCSSToken_Bad_URL;
     871                 :     }
     872               0 :     return true;
     873                 :   }
     874                 : 
     875                 :   // Process a url lexical token. A CSS1 url token can contain
     876                 :   // characters beyond identifier characters (e.g. '/', ':', etc.)
     877                 :   // Because of this the normal rules for tokenizing the input don't
     878                 :   // apply very well. To simplify the parser and relax some of the
     879                 :   // requirements on the scanner we parse url's here. If we find a
     880                 :   // malformed URL then we emit a token of type "Bad_URL" so that
     881                 :   // the CSS1 parser can ignore the invalid input.  The parser must
     882                 :   // treat a Bad_URL token like a Function token, and process
     883                 :   // tokens until a matching parenthesis.
     884                 : 
     885               0 :   aToken.mType = eCSSToken_Bad_URL;
     886               0 :   aToken.mSymbol = PRUnichar(0);
     887               0 :   nsString& ident = aToken.mIdent;
     888               0 :   ident.SetLength(0);
     889                 : 
     890               0 :   Pushback(ch);
     891                 : 
     892                 :   // start of a non-quoted url (which may be empty)
     893               0 :   bool ok = true;
     894               0 :   for (;;) {
     895               0 :     ch = Read();
     896               0 :     if (ch < 0) break;
     897               0 :     if (ch == '\\') {
     898               0 :       if (!ParseAndAppendEscape(ident, false)) {
     899               0 :         ok = false;
     900               0 :         Pushback(ch);
     901               0 :         break;
     902                 :       }
     903               0 :     } else if (IsWhitespace(ch)) {
     904                 :       // Whitespace is allowed at the end of the URL
     905               0 :       EatWhiteSpace();
     906                 :       // Consume the close paren if we have it; if not we're an invalid URL.
     907               0 :       ok = LookAheadOrEOF(')');
     908               0 :       break;
     909               0 :     } else if (ch == '"' || ch == '\'' || ch == '(' || ch < PRUnichar(' ')) {
     910                 :       // This is an invalid URL spec
     911               0 :       ok = false;
     912               0 :       Pushback(ch); // push it back so the parser can match tokens and
     913                 :                     // then closing parenthesis
     914               0 :       break;
     915               0 :     } else if (ch == ')') {
     916                 :       // All done
     917               0 :       break;
     918                 :     } else {
     919                 :       // A regular url character.
     920               0 :       ident.Append(PRUnichar(ch));
     921                 :     }
     922                 :   }
     923                 : 
     924                 :   // If the result of the above scanning is ok then change the token
     925                 :   // type to a useful one.
     926               0 :   if (ok) {
     927               0 :     aToken.mType = eCSSToken_URL;
     928                 :   }
     929               0 :   return true;
     930                 : }
     931                 : 
     932                 : 
     933                 : /**
     934                 :  * Returns whether an escape was succesfully parsed; if it was not,
     935                 :  * the backslash needs to be its own symbol token.
     936                 :  */
     937                 : bool
     938               0 : nsCSSScanner::ParseAndAppendEscape(nsString& aOutput, bool aInString)
     939                 : {
     940               0 :   PRInt32 ch = Read();
     941               0 :   if (ch < 0) {
     942               0 :     return false;
     943                 :   }
     944               0 :   if (IsHexDigit(ch)) {
     945               0 :     PRInt32 rv = 0;
     946                 :     int i;
     947               0 :     Pushback(ch);
     948               0 :     for (i = 0; i < 6; i++) { // up to six digits
     949               0 :       ch = Read();
     950               0 :       if (ch < 0) {
     951                 :         // Whoops: error or premature eof
     952               0 :         break;
     953                 :       }
     954               0 :       if (!IsHexDigit(ch) && !IsWhitespace(ch)) {
     955               0 :         Pushback(ch);
     956               0 :         break;
     957               0 :       } else if (IsHexDigit(ch)) {
     958               0 :         rv = rv * 16 + HexDigitValue(ch);
     959                 :       } else {
     960               0 :         NS_ASSERTION(IsWhitespace(ch), "bad control flow");
     961                 :         // single space ends escape
     962               0 :         break;
     963                 :       }
     964                 :     }
     965               0 :     if (6 == i) { // look for trailing whitespace and eat it
     966               0 :       ch = Peek();
     967               0 :       if (IsWhitespace(ch)) {
     968               0 :         (void) Read();
     969                 :       }
     970                 :     }
     971               0 :     NS_ASSERTION(rv >= 0, "How did rv become negative?");
     972                 :     // "[at most six hexadecimal digits following a backslash] stand
     973                 :     // for the ISO 10646 character with that number, which must not be
     974                 :     // zero. (It is undefined in CSS 2.1 what happens if a style sheet
     975                 :     // does contain a character with Unicode codepoint zero.)"
     976                 :     //   -- CSS2.1 section 4.1.3
     977                 :     //
     978                 :     // Silently deleting \0 opens a content-filtration loophole (see
     979                 :     // bug 228856), so what we do instead is pretend the "cancels the
     980                 :     // meaning of special characters" rule applied.
     981               0 :     if (rv > 0) {
     982               0 :       AppendUCS4ToUTF16(ENSURE_VALID_CHAR(rv), aOutput);
     983                 :     } else {
     984               0 :       while (i--)
     985               0 :         aOutput.Append('0');
     986               0 :       if (IsWhitespace(ch))
     987               0 :         Pushback(ch);
     988                 :     }
     989               0 :     return true;
     990                 :   } 
     991                 :   // "Any character except a hexidecimal digit can be escaped to
     992                 :   // remove its special meaning by putting a backslash in front"
     993                 :   // -- CSS1 spec section 7.1
     994               0 :   if (ch == '\n') {
     995               0 :     if (!aInString) {
     996                 :       // Outside of strings (which includes url() that contains a
     997                 :       // string), escaped newlines aren't special, and just tokenize as
     998                 :       // eCSSToken_Symbol (DELIM).
     999               0 :       Pushback(ch);
    1000               0 :       return false;
    1001                 :     }
    1002                 :     // In strings (and in url() containing a string), escaped newlines
    1003                 :     // are just dropped to allow splitting over multiple lines.
    1004                 :   } else {
    1005               0 :     aOutput.Append(ch);
    1006                 :   }
    1007                 : 
    1008               0 :   return true;
    1009                 : }
    1010                 : 
    1011                 : /**
    1012                 :  * Gather up the characters in an identifier. The identfier was
    1013                 :  * started by "aChar" which will be appended to aIdent. The result
    1014                 :  * will be aIdent with all of the identifier characters appended
    1015                 :  * until the first non-identifier character is seen. The termination
    1016                 :  * character is unread for the future re-reading.
    1017                 :  *
    1018                 :  * Returns failure when the character sequence does not form an ident at
    1019                 :  * all, in which case the caller is responsible for pushing back or
    1020                 :  * otherwise handling aChar.  (This occurs only when aChar is '\'.)
    1021                 :  */
    1022                 : bool
    1023             220 : nsCSSScanner::GatherIdent(PRInt32 aChar, nsString& aIdent)
    1024                 : {
    1025             220 :   if (aChar == '\\') {
    1026               0 :     if (!ParseAndAppendEscape(aIdent, false)) {
    1027               0 :       return false;
    1028                 :     }
    1029                 :   }
    1030             220 :   else if (0 < aChar) {
    1031             220 :     aIdent.Append(aChar);
    1032                 :   }
    1033             220 :   for (;;) {
    1034                 :     // If nothing in pushback, first try to get as much as possible in one go
    1035             440 :     if (!mPushbackCount && mOffset < mCount) {
    1036                 :       // See how much we can consume and append in one go
    1037             220 :       PRUint32 n = mOffset;
    1038                 :       // Count number of Ident characters that can be processed
    1039            3474 :       while (n < mCount && IsIdent(mReadPointer[n])) {
    1040            3034 :         ++n;
    1041                 :       }
    1042                 :       // Add to the token what we have so far
    1043             220 :       if (n > mOffset) {
    1044                 : #ifdef CSS_REPORT_PARSE_ERRORS
    1045             220 :         mColNumber += n - mOffset;
    1046                 : #endif
    1047             220 :         aIdent.Append(&mReadPointer[mOffset], n - mOffset);
    1048             220 :         mOffset = n;
    1049                 :       }
    1050                 :     }
    1051                 : 
    1052             440 :     aChar = Read();
    1053             440 :     if (aChar < 0) break;
    1054             330 :     if (aChar == '\\') {
    1055               0 :       if (!ParseAndAppendEscape(aIdent, false)) {
    1056               0 :         Pushback(aChar);
    1057               0 :         break;
    1058                 :       }
    1059             330 :     } else if (IsIdent(aChar)) {
    1060             220 :       aIdent.Append(PRUnichar(aChar));
    1061                 :     } else {
    1062             110 :       Pushback(aChar);
    1063             110 :       break;
    1064                 :     }
    1065                 :   }
    1066             220 :   return true;
    1067                 : }
    1068                 : 
    1069                 : bool
    1070               0 : nsCSSScanner::ParseRef(PRInt32 aChar, nsCSSToken& aToken)
    1071                 : {
    1072                 :   // Fall back for when we don't have name characters following:
    1073               0 :   aToken.mType = eCSSToken_Symbol;
    1074               0 :   aToken.mSymbol = aChar;
    1075                 : 
    1076               0 :   PRInt32 ch = Read();
    1077               0 :   if (ch < 0) {
    1078               0 :     return true;
    1079                 :   }
    1080               0 :   if (IsIdent(ch) || ch == '\\') {
    1081                 :     // First char after the '#' is a valid ident char (or an escape),
    1082                 :     // so it makes sense to keep going
    1083                 :     nsCSSTokenType type =
    1084               0 :       StartsIdent(ch, Peek()) ? eCSSToken_ID : eCSSToken_Ref;
    1085               0 :     aToken.mIdent.SetLength(0);
    1086               0 :     if (GatherIdent(ch, aToken.mIdent)) {
    1087               0 :       aToken.mType = type;
    1088               0 :       return true;
    1089                 :     }
    1090                 :   }
    1091                 : 
    1092                 :   // No ident chars after the '#'.  Just unread |ch| and get out of here.
    1093               0 :   Pushback(ch);
    1094               0 :   return true;
    1095                 : }
    1096                 : 
    1097                 : bool
    1098             220 : nsCSSScanner::ParseIdent(PRInt32 aChar, nsCSSToken& aToken)
    1099                 : {
    1100             220 :   nsString& ident = aToken.mIdent;
    1101             220 :   ident.SetLength(0);
    1102             220 :   if (!GatherIdent(aChar, ident)) {
    1103               0 :     aToken.mType = eCSSToken_Symbol;
    1104               0 :     aToken.mSymbol = aChar;
    1105               0 :     return true;
    1106                 :   }
    1107                 : 
    1108             220 :   nsCSSTokenType tokenType = eCSSToken_Ident;
    1109                 :   // look for functions (ie: "ident(")
    1110             220 :   if (Peek() == PRUnichar('(')) {
    1111               0 :     Read();
    1112               0 :     tokenType = eCSSToken_Function;
    1113                 : 
    1114               0 :     if (ident.LowerCaseEqualsLiteral("url")) {
    1115               0 :       NextURL(aToken); // ignore return value, since *we* read something
    1116               0 :       return true;
    1117                 :     }
    1118                 :   }
    1119                 : 
    1120             220 :   aToken.mType = tokenType;
    1121             220 :   return true;
    1122                 : }
    1123                 : 
    1124                 : bool
    1125               0 : nsCSSScanner::ParseAtKeyword(PRInt32 aChar, nsCSSToken& aToken)
    1126                 : {
    1127               0 :   aToken.mIdent.SetLength(0);
    1128               0 :   aToken.mType = eCSSToken_AtKeyword;
    1129               0 :   if (!GatherIdent(0, aToken.mIdent)) {
    1130               0 :     aToken.mType = eCSSToken_Symbol;
    1131               0 :     aToken.mSymbol = PRUnichar('@');
    1132                 :   }
    1133               0 :   return true;
    1134                 : }
    1135                 : 
    1136                 : bool
    1137               0 : nsCSSScanner::ParseNumber(PRInt32 c, nsCSSToken& aToken)
    1138                 : {
    1139               0 :   NS_PRECONDITION(c == '.' || c == '+' || c == '-' || IsDigit(c),
    1140                 :                   "Why did we get called?");
    1141               0 :   aToken.mHasSign = (c == '+' || c == '-');
    1142                 : 
    1143                 :   // Our sign.
    1144               0 :   PRInt32 sign = c == '-' ? -1 : 1;
    1145                 :   // Absolute value of the integer part of the mantissa.  This is a double so
    1146                 :   // we don't run into overflow issues for consumers that only care about our
    1147                 :   // floating-point value while still being able to express the full PRInt32
    1148                 :   // range for consumers who want integers.
    1149               0 :   double intPart = 0;
    1150                 :   // Fractional part of the mantissa.  This is a double so that when we convert
    1151                 :   // to float at the end we'll end up rounding to nearest float instead of
    1152                 :   // truncating down (as we would if fracPart were a float and we just
    1153                 :   // effectively lost the last several digits).
    1154               0 :   double fracPart = 0;
    1155                 :   // Absolute value of the power of 10 that we should multiply by (only
    1156                 :   // relevant for numbers in scientific notation).  Has to be a signed integer,
    1157                 :   // because multiplication of signed by unsigned converts the unsigned to
    1158                 :   // signed, so if we plan to actually multiply by expSign...
    1159               0 :   PRInt32 exponent = 0;
    1160                 :   // Sign of the exponent.
    1161               0 :   PRInt32 expSign = 1;
    1162                 : 
    1163               0 :   if (aToken.mHasSign) {
    1164               0 :     NS_ASSERTION(c != '.', "How did that happen?");
    1165               0 :     c = Read();
    1166                 :   }
    1167                 : 
    1168               0 :   bool gotDot = (c == '.');
    1169                 : 
    1170               0 :   if (!gotDot) {
    1171                 :     // Parse the integer part of the mantisssa
    1172               0 :     NS_ASSERTION(IsDigit(c), "Why did we get called?");
    1173               0 :     do {
    1174               0 :       intPart = 10*intPart + DecimalDigitValue(c);
    1175               0 :       c = Read();
    1176                 :       // The IsDigit check will do the right thing even if Read() returns < 0
    1177                 :     } while (IsDigit(c));
    1178                 : 
    1179               0 :     gotDot = (c == '.') && IsDigit(Peek());
    1180                 :   }
    1181                 : 
    1182               0 :   if (gotDot) {
    1183                 :     // Parse the fractional part of the mantissa.
    1184               0 :     c = Read();
    1185               0 :     NS_ASSERTION(IsDigit(c), "How did we get here?");
    1186                 :     // Power of ten by which we need to divide our next digit
    1187               0 :     float divisor = 10;
    1188               0 :     do {
    1189               0 :       fracPart += DecimalDigitValue(c) / divisor;
    1190               0 :       divisor *= 10;
    1191               0 :       c = Read();
    1192                 :       // The IsDigit check will do the right thing even if Read() returns < 0
    1193                 :     } while (IsDigit(c));
    1194                 :   }
    1195                 : 
    1196               0 :   bool gotE = false;
    1197               0 :   if (IsSVGMode() && (c == 'e' || c == 'E')) {
    1198               0 :     PRInt32 nextChar = Peek();
    1199               0 :     PRInt32 expSignChar = 0;
    1200               0 :     if (nextChar == '-' || nextChar == '+') {
    1201               0 :       expSignChar = Read();
    1202               0 :       nextChar = Peek();
    1203                 :     }
    1204               0 :     if (IsDigit(nextChar)) {
    1205               0 :       gotE = true;
    1206               0 :       if (expSignChar == '-') {
    1207               0 :         expSign = -1;
    1208                 :       }
    1209                 : 
    1210               0 :       c = Read();
    1211               0 :       NS_ASSERTION(IsDigit(c), "Peek() must have lied");
    1212               0 :       do {
    1213               0 :         exponent = 10*exponent + DecimalDigitValue(c);
    1214               0 :         c = Read();
    1215                 :         // The IsDigit check will do the right thing even if Read() returns < 0
    1216                 :       } while (IsDigit(c));
    1217                 :     } else {
    1218               0 :       if (expSignChar) {
    1219               0 :         Pushback(expSignChar);
    1220                 :       }
    1221                 :     }
    1222                 :   }
    1223                 : 
    1224               0 :   nsCSSTokenType type = eCSSToken_Number;
    1225                 : 
    1226                 :   // Set mIntegerValid for all cases (except %, below) because we need
    1227                 :   // it for the "2n" in :nth-child(2n).
    1228               0 :   aToken.mIntegerValid = false;
    1229                 : 
    1230                 :   // Time to reassemble our number.
    1231               0 :   float value = float(sign * (intPart + fracPart));
    1232               0 :   if (gotE) {
    1233                 :     // pow(), not powf(), because at least wince doesn't have the latter.
    1234                 :     // And explicitly cast everything to doubles to avoid issues with
    1235                 :     // overloaded pow() on Windows.
    1236               0 :     value *= pow(10.0, double(expSign * exponent));
    1237               0 :   } else if (!gotDot) {
    1238                 :     // Clamp values outside of integer range.
    1239               0 :     if (sign > 0) {
    1240               0 :       aToken.mInteger = PRInt32(NS_MIN(intPart, double(PR_INT32_MAX)));
    1241                 :     } else {
    1242               0 :       aToken.mInteger = PRInt32(NS_MAX(-intPart, double(PR_INT32_MIN)));
    1243                 :     }
    1244               0 :     aToken.mIntegerValid = true;
    1245                 :   }
    1246                 : 
    1247               0 :   nsString& ident = aToken.mIdent;
    1248               0 :   ident.Truncate();
    1249                 : 
    1250                 :   // Look at character that terminated the number
    1251               0 :   if (c >= 0) {
    1252               0 :     if (StartsIdent(c, Peek())) {
    1253               0 :       if (GatherIdent(c, ident)) {
    1254               0 :         type = eCSSToken_Dimension;
    1255                 :       }
    1256               0 :     } else if ('%' == c) {
    1257               0 :       type = eCSSToken_Percentage;
    1258               0 :       value = value / 100.0f;
    1259               0 :       aToken.mIntegerValid = false;
    1260                 :     } else {
    1261                 :       // Put back character that stopped numeric scan
    1262               0 :       Pushback(c);
    1263                 :     }
    1264                 :   }
    1265               0 :   aToken.mNumber = value;
    1266               0 :   aToken.mType = type;
    1267               0 :   return true;
    1268                 : }
    1269                 : 
    1270                 : bool
    1271               0 : nsCSSScanner::SkipCComment()
    1272                 : {
    1273               0 :   for (;;) {
    1274               0 :     PRInt32 ch = Read();
    1275               0 :     if (ch < 0) break;
    1276               0 :     if (ch == '*') {
    1277               0 :       if (LookAhead('/')) {
    1278               0 :         return true;
    1279                 :       }
    1280                 :     }
    1281                 :   }
    1282                 : 
    1283               0 :   REPORT_UNEXPECTED_EOF(PECommentEOF);
    1284               0 :   return false;
    1285                 : }
    1286                 : 
    1287                 : bool
    1288               0 : nsCSSScanner::ParseString(PRInt32 aStop, nsCSSToken& aToken)
    1289                 : {
    1290               0 :   aToken.mIdent.SetLength(0);
    1291               0 :   aToken.mType = eCSSToken_String;
    1292               0 :   aToken.mSymbol = PRUnichar(aStop); // remember how it's quoted
    1293               0 :   for (;;) {
    1294                 :     // If nothing in pushback, first try to get as much as possible in one go
    1295               0 :     if (!mPushbackCount && mOffset < mCount) {
    1296                 :       // See how much we can consume and append in one go
    1297               0 :       PRUint32 n = mOffset;
    1298                 :       // Count number of characters that can be processed
    1299               0 :       for (;n < mCount; ++n) {
    1300               0 :         PRUnichar nextChar = mReadPointer[n];
    1301               0 :         if ((nextChar == aStop) || (nextChar == '\\') ||
    1302                 :             (nextChar == '\n') || (nextChar == '\r') || (nextChar == '\f')) {
    1303               0 :           break;
    1304                 :         }
    1305                 : #ifdef CSS_REPORT_PARSE_ERRORS
    1306               0 :         ++mColNumber;
    1307                 : #endif
    1308                 :       }
    1309                 :       // Add to the token what we have so far
    1310               0 :       if (n > mOffset) {
    1311               0 :         aToken.mIdent.Append(&mReadPointer[mOffset], n - mOffset);
    1312               0 :         mOffset = n;
    1313                 :       }
    1314                 :     }
    1315               0 :     PRInt32 ch = Read();
    1316               0 :     if (ch < 0 || ch == aStop) {
    1317               0 :       break;
    1318                 :     }
    1319               0 :     if (ch == '\n') {
    1320               0 :       aToken.mType = eCSSToken_Bad_String;
    1321                 : #ifdef CSS_REPORT_PARSE_ERRORS
    1322               0 :       ReportUnexpectedToken(aToken, "SEUnterminatedString");
    1323                 : #endif
    1324               0 :       break;
    1325                 :     }
    1326               0 :     if (ch == '\\') {
    1327               0 :       if (!ParseAndAppendEscape(aToken.mIdent, true)) {
    1328               0 :         aToken.mType = eCSSToken_Bad_String;
    1329               0 :         Pushback(ch);
    1330                 : #ifdef CSS_REPORT_PARSE_ERRORS
    1331                 :         // For strings, the only case where ParseAndAppendEscape will
    1332                 :         // return false is when there's a backslash to start an escape
    1333                 :         // immediately followed by end-of-stream.  In that case, the
    1334                 :         // correct tokenization is badstring *followed* by a DELIM for
    1335                 :         // the backslash, but as far as the author is concerned, it
    1336                 :         // works pretty much the same as an unterminated string, so we
    1337                 :         // use the same error message.
    1338               0 :         ReportUnexpectedToken(aToken, "SEUnterminatedString");
    1339                 : #endif
    1340               0 :         break;
    1341                 :       }
    1342                 :     } else {
    1343               0 :       aToken.mIdent.Append(ch);
    1344                 :     }
    1345                 :   }
    1346               0 :   return true;
    1347                 : }
    1348                 : 
    1349                 : // UNICODE-RANGE tokens match the regular expression
    1350                 : //
    1351                 : //     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
    1352                 : //
    1353                 : // However, some such tokens are "invalid".  There are three valid forms:
    1354                 : //
    1355                 : //     u+[0-9a-f]{x}              1 <= x <= 6
    1356                 : //     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
    1357                 : //     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
    1358                 : //
    1359                 : // All unicode-range tokens have their text recorded in mIdent; valid ones
    1360                 : // are also decoded into mInteger and mInteger2, and mIntegerValid is set.
    1361                 : 
    1362                 : bool
    1363               0 : nsCSSScanner::ParseURange(PRInt32 aChar, nsCSSToken& aResult)
    1364                 : {
    1365               0 :   PRInt32 intro2 = Read();
    1366               0 :   PRInt32 ch = Peek();
    1367                 : 
    1368                 :   // We should only ever be called if these things are true.
    1369               0 :   NS_ASSERTION(aChar == 'u' || aChar == 'U',
    1370                 :                "unicode-range called with improper introducer (U)");
    1371               0 :   NS_ASSERTION(intro2 == '+',
    1372                 :                "unicode-range called with improper introducer (+)");
    1373                 : 
    1374                 :   // If the character immediately after the '+' is not a hex digit or
    1375                 :   // '?', this is not really a unicode-range token; push everything
    1376                 :   // back and scan the U as an ident.
    1377               0 :   if (!IsHexDigit(ch) && ch != '?') {
    1378               0 :     Pushback(intro2);
    1379               0 :     Pushback(aChar);
    1380               0 :     return ParseIdent(aChar, aResult);
    1381                 :   }
    1382                 : 
    1383               0 :   aResult.mIdent.Truncate();
    1384               0 :   aResult.mIdent.Append(aChar);
    1385               0 :   aResult.mIdent.Append(intro2);
    1386                 : 
    1387               0 :   bool valid = true;
    1388               0 :   bool haveQues = false;
    1389               0 :   PRUint32 low = 0;
    1390               0 :   PRUint32 high = 0;
    1391               0 :   int i = 0;
    1392                 : 
    1393               0 :   for (;;) {
    1394               0 :     ch = Read();
    1395               0 :     i++;
    1396               0 :     if (i == 7 || !(IsHexDigit(ch) || ch == '?')) {
    1397                 :       break;
    1398                 :     }
    1399                 : 
    1400               0 :     aResult.mIdent.Append(ch);
    1401               0 :     if (IsHexDigit(ch)) {
    1402               0 :       if (haveQues) {
    1403               0 :         valid = false; // all question marks should be at the end
    1404                 :       }
    1405               0 :       low = low*16 + HexDigitValue(ch);
    1406               0 :       high = high*16 + HexDigitValue(ch);
    1407                 :     } else {
    1408               0 :       haveQues = true;
    1409               0 :       low = low*16 + 0x0;
    1410               0 :       high = high*16 + 0xF;
    1411                 :     }
    1412                 :   }
    1413                 : 
    1414               0 :   if (ch == '-' && IsHexDigit(Peek())) {
    1415               0 :     if (haveQues) {
    1416               0 :       valid = false;
    1417                 :     }
    1418                 : 
    1419               0 :     aResult.mIdent.Append(ch);
    1420               0 :     high = 0;
    1421               0 :     i = 0;
    1422               0 :     for (;;) {
    1423               0 :       ch = Read();
    1424               0 :       i++;
    1425               0 :       if (i == 7 || !IsHexDigit(ch)) {
    1426                 :         break;
    1427                 :       }
    1428               0 :       aResult.mIdent.Append(ch);
    1429               0 :       high = high*16 + HexDigitValue(ch);
    1430                 :     }
    1431                 :   }
    1432               0 :   Pushback(ch);
    1433                 : 
    1434               0 :   aResult.mInteger = low;
    1435               0 :   aResult.mInteger2 = high;
    1436               0 :   aResult.mIntegerValid = valid;
    1437               0 :   aResult.mType = eCSSToken_URange;
    1438               0 :   return true;
    1439                 : }

Generated by: LCOV version 1.7