LCOV - app.info - layout/style/nsCSSScanner.cpp

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