LCOV - app.info - content/smil/nsSMILParserUtils.cpp

LCOV - code coverage report
| Current view: | directory - content/smil - nsSMILParserUtils.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 410 | 0 | 0.0 %
| Date: | 2012-07-07 | Functions: | 26 | 0 | 0.0 %

           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 the Mozilla SMIL module.
          16                 :  *
          17                 :  * The Initial Developer of the Original Code is Brian Birtles.
          18                 :  * Portions created by the Initial Developer are Copyright (C) 2005
          19                 :  * the Initial Developer. All Rights Reserved.
          20                 :  *
          21                 :  * Contributor(s):
          22                 :  *   Brian Birtles <birtles@gmail.com>
          23                 :  *
          24                 :  * Alternatively, the contents of this file may be used under the terms of
          25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          28                 :  * of those above. If you wish to allow use of your version of this file only
          29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          30                 :  * use your version of this file under the terms of the MPL, indicate your
          31                 :  * decision by deleting the provisions above and replace them with the notice
          32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          33                 :  * the provisions above, a recipient may use your version of this file under
          34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          35                 :  *
          36                 :  * ***** END LICENSE BLOCK ***** */
          37                 :
          38                 : #include "nsSMILParserUtils.h"
          39                 : #include "nsISMILAttr.h"
          40                 : #include "nsSMILValue.h"
          41                 : #include "nsSMILTimeValue.h"
          42                 : #include "nsSMILTimeValueSpecParams.h"
          43                 : #include "nsSMILTypes.h"
          44                 : #include "nsSMILRepeatCount.h"
          45                 : #include "nsContentUtils.h"
          46                 : #include "nsString.h"
          47                 : #include "prdtoa.h"
          48                 : #include "nsCRT.h"
          49                 : #include "nsCOMPtr.h"
          50                 : #include "prlong.h"
          51                 : #include "nsCharSeparatedTokenizer.h"
          52                 :
          53                 : //------------------------------------------------------------------------------
          54                 : // Helper functions and Constants
          55                 :
          56                 : namespace {
          57                 :
          58                 : const PRUint32 MSEC_PER_SEC  = 1000;
          59                 : const PRUint32 MSEC_PER_MIN  = 1000 * 60;
          60                 : const PRUint32 MSEC_PER_HOUR = 1000 * 60 * 60;
          61                 : const PRInt32  DECIMAL_BASE  = 10;
          62                 :
          63                 : #define ACCESSKEY_PREFIX_LC NS_LITERAL_STRING("accesskey(") // SMIL2+
          64                 : #define ACCESSKEY_PREFIX_CC NS_LITERAL_STRING("accessKey(") // SVG/SMIL ANIM
          65                 : #define REPEAT_PREFIX    NS_LITERAL_STRING("repeat(")
          66                 : #define WALLCLOCK_PREFIX NS_LITERAL_STRING("wallclock(")
          67                 :
          68                 : // NS_IS_SPACE relies on isspace which may return true for \xB and \xC but
          69                 : // SMILANIM does not consider these characters to be whitespace.
          70                 : inline bool
          71               0 : IsSpace(const PRUnichar c)
          72                 : {
          73               0 :   return (c == 0x9 || c == 0xA || c == 0xD || c == 0x20);
          74                 : }
          75                 :
          76                 : template<class T>
          77                 : inline void
          78               0 : SkipBeginWsp(T& aStart, T aEnd)
          79                 : {
          80               0 :   while (aStart != aEnd && IsSpace(*aStart)) {
          81               0 :     ++aStart;
          82                 :   }
          83               0 : }
          84                 :
          85                 : inline void
          86               0 : SkipBeginEndWsp(const PRUnichar*& aStart, const PRUnichar*& aEnd)
          87                 : {
          88               0 :   SkipBeginWsp(aStart, aEnd);
          89               0 :   while (aEnd != aStart && IsSpace(*(aEnd - 1))) {
          90               0 :     --aEnd;
          91                 :   }
          92               0 : }
          93                 :
          94                 : double
          95               0 : GetFloat(const char*& aStart, const char* aEnd, nsresult* aErrorCode)
          96                 : {
          97                 :   char* floatEnd;
          98               0 :   double value = PR_strtod(aStart, &floatEnd);
          99                 :
         100                 :   nsresult rv;
         101                 :
         102               0 :   if (floatEnd == aStart || floatEnd > aEnd) {
         103               0 :     rv = NS_ERROR_FAILURE;
         104                 :   } else {
         105               0 :     aStart = floatEnd;
         106               0 :     rv = NS_OK;
         107                 :   }
         108                 :
         109               0 :   if (aErrorCode) {
         110               0 :     *aErrorCode = rv;
         111                 :   }
         112                 :
         113               0 :   return value;
         114                 : }
         115                 :
         116                 : size_t
         117               0 : GetUnsignedInt(const nsAString& aStr, PRUint32& aResult)
         118                 : {
         119               0 :   NS_ConvertUTF16toUTF8 cstr(aStr);
         120               0 :   const char* str = cstr.get();
         121                 :
         122                 :   char* rest;
         123               0 :   PRInt32 value = strtol(str, &rest, DECIMAL_BASE);
         124                 :
         125               0 :   if (rest == str || value < 0)
         126               0 :     return 0;
         127                 :
         128               0 :   aResult = static_cast<PRUint32>(value);
         129               0 :   return rest - str;
         130                 : }
         131                 :
         132                 : bool
         133               0 : GetUnsignedIntAndEndParen(const nsAString& aStr, PRUint32& aResult)
         134                 : {
         135               0 :   size_t intLen = GetUnsignedInt(aStr, aResult);
         136                 :
         137               0 :   const PRUnichar* start = aStr.BeginReading();
         138               0 :   const PRUnichar* end = aStr.EndReading();
         139                 :
         140                 :   // Make sure the string is only digit+')'
         141               0 :   if (intLen == 0 || start + intLen + 1 != end || *(start + intLen) != ')')
         142               0 :     return false;
         143                 :
         144               0 :   return true;
         145                 : }
         146                 :
         147                 : inline bool
         148               0 : ConsumeSubstring(const char*& aStart, const char* aEnd, const char* aSubstring)
         149                 : {
         150               0 :   size_t substrLen = PL_strlen(aSubstring);
         151                 :
         152               0 :   if (static_cast<size_t>(aEnd - aStart) < substrLen)
         153               0 :     return false;
         154                 :
         155               0 :   bool result = false;
         156                 :
         157               0 :   if (PL_strstr(aStart, aSubstring) == aStart) {
         158               0 :     aStart += substrLen;
         159               0 :     result = true;
         160                 :   }
         161                 :
         162               0 :   return result;
         163                 : }
         164                 :
         165                 : bool
         166               0 : ParseClockComponent(const char*& aStart,
         167                 :                     const char* aEnd,
         168                 :                     double& aResult,
         169                 :                     bool& aIsReal,
         170                 :                     bool& aCouldBeMin,
         171                 :                     bool& aCouldBeSec)
         172                 : {
         173                 :   nsresult rv;
         174               0 :   const char* begin = aStart;
         175               0 :   double value = GetFloat(aStart, aEnd, &rv);
         176                 :
         177                 :   // Check a number was found
         178               0 :   if (NS_FAILED(rv))
         179               0 :     return false;
         180                 :
         181                 :   // Check that it's not expressed in exponential form
         182               0 :   size_t len = aStart - begin;
         183               0 :   bool isExp = (PL_strnpbrk(begin, "eE", len) != nsnull);
         184               0 :   if (isExp)
         185               0 :     return false;
         186                 :
         187                 :   // Don't allow real numbers of the form "23."
         188               0 :   if (*(aStart - 1) == '.')
         189               0 :     return false;
         190                 :
         191                 :   // Number looks good
         192               0 :   aResult = value;
         193                 :
         194                 :   // Set some flags so we can check this number is valid once we know
         195                 :   // whether it's an hour, minute string etc.
         196               0 :   aIsReal = (PL_strnchr(begin, '.', len) != nsnull);
         197               0 :   aCouldBeMin = (value < 60.0 && (len == 2));
         198                 :   aCouldBeSec = (value < 60.0 ||
         199               0 :       (value == 60.0 && begin[0] == '5')); // Take care of rounding error
         200                 :   aCouldBeSec &= (len >= 2 &&
         201               0 :       (begin[2] == '\0' || begin[2] == '.' || IsSpace(begin[2])));
         202                 :
         203               0 :   return true;
         204                 : }
         205                 :
         206                 : bool
         207               0 : ParseMetricMultiplicand(const char*& aStart,
         208                 :                         const char* aEnd,
         209                 :                         PRInt32& multiplicand)
         210                 : {
         211               0 :   bool result = false;
         212                 :
         213               0 :   size_t len = aEnd - aStart;
         214               0 :   const char* cur = aStart;
         215                 :
         216               0 :   if (len) {
         217               0 :     switch (*cur++)
         218                 :     {
         219                 :       case 'h':
         220               0 :         multiplicand = MSEC_PER_HOUR;
         221               0 :         result = true;
         222               0 :         break;
         223                 :       case 'm':
         224               0 :         if (len >= 2) {
         225               0 :           if (*cur == 's') {
         226               0 :             ++cur;
         227               0 :             multiplicand = 1;
         228               0 :             result = true;
         229               0 :           } else if (len >= 3 && *cur++ == 'i' && *cur++ == 'n') {
         230               0 :             multiplicand = MSEC_PER_MIN;
         231               0 :             result = true;
         232                 :           }
         233                 :         }
         234               0 :         break;
         235                 :       case 's':
         236               0 :         multiplicand = MSEC_PER_SEC;
         237               0 :         result = true;
         238               0 :         break;
         239                 :     }
         240                 :   }
         241                 :
         242               0 :   if (result) {
         243               0 :     aStart = cur;
         244                 :   }
         245                 :
         246               0 :   return result;
         247                 : }
         248                 :
         249                 : nsresult
         250               0 : ParseOptionalOffset(const nsAString& aSpec, nsSMILTimeValueSpecParams& aResult)
         251                 : {
         252               0 :   if (aSpec.IsEmpty()) {
         253               0 :     aResult.mOffset.SetMillis(0);
         254               0 :     return NS_OK;
         255                 :   }
         256                 :
         257               0 :   if (aSpec.First() != '+' && aSpec.First() != '-')
         258               0 :     return NS_ERROR_FAILURE;
         259                 :
         260                 :   return nsSMILParserUtils::ParseClockValue(aSpec, &aResult.mOffset,
         261               0 :      nsSMILParserUtils::kClockValueAllowSign);
         262                 : }
         263                 :
         264                 : nsresult
         265               0 : ParseAccessKey(const nsAString& aSpec, nsSMILTimeValueSpecParams& aResult)
         266                 : {
         267               0 :   NS_ABORT_IF_FALSE(StringBeginsWith(aSpec, ACCESSKEY_PREFIX_CC) ||
         268                 :       StringBeginsWith(aSpec, ACCESSKEY_PREFIX_LC),
         269                 :       "Calling ParseAccessKey on non-accesskey-type spec");
         270                 :
         271               0 :   nsSMILTimeValueSpecParams result;
         272               0 :   result.mType = nsSMILTimeValueSpecParams::ACCESSKEY;
         273                 :
         274               0 :   NS_ABORT_IF_FALSE(
         275                 :       ACCESSKEY_PREFIX_LC.Length() == ACCESSKEY_PREFIX_CC.Length(),
         276                 :       "Case variations for accesskey prefix differ in length");
         277               0 :   const PRUnichar* start = aSpec.BeginReading() + ACCESSKEY_PREFIX_LC.Length();
         278               0 :   const PRUnichar* end = aSpec.EndReading();
         279                 :
         280                 :   // Expecting at least <accesskey> + ')'
         281               0 :   if (end - start < 2)
         282               0 :     return NS_ERROR_FAILURE;
         283                 :
         284               0 :   PRUint32 c = *start++;
         285                 :
         286                 :   // Process 32-bit codepoints
         287               0 :   if (NS_IS_HIGH_SURROGATE(c)) {
         288               0 :     if (end - start < 2) // Expecting at least low-surrogate + ')'
         289               0 :       return NS_ERROR_FAILURE;
         290               0 :     PRUint32 lo = *start++;
         291               0 :     if (!NS_IS_LOW_SURROGATE(lo))
         292               0 :       return NS_ERROR_FAILURE;
         293               0 :     c = SURROGATE_TO_UCS4(c, lo);
         294                 :   // XML 1.1 says that 0xFFFE and 0xFFFF are not valid characters
         295               0 :   } else if (NS_IS_LOW_SURROGATE(c) || c == 0xFFFE || c == 0xFFFF) {
         296               0 :     return NS_ERROR_FAILURE;
         297                 :   }
         298                 :
         299               0 :   result.mRepeatIterationOrAccessKey = c;
         300                 :
         301               0 :   if (*start++ != ')')
         302               0 :     return NS_ERROR_FAILURE;
         303                 :
         304               0 :   SkipBeginWsp(start, end);
         305                 :
         306               0 :   nsresult rv = ParseOptionalOffset(Substring(start, end), result);
         307               0 :   if (NS_FAILED(rv))
         308               0 :     return rv;
         309                 :
         310               0 :   aResult = result;
         311                 :
         312               0 :   return NS_OK;
         313                 : }
         314                 :
         315                 : const PRUnichar*
         316               0 : GetTokenEnd(const nsAString& aStr, bool aBreakOnDot)
         317                 : {
         318               0 :   const PRUnichar* tokenEnd = aStr.BeginReading();
         319               0 :   const PRUnichar* const end = aStr.EndReading();
         320               0 :   bool escape = false;
         321               0 :   while (tokenEnd != end) {
         322               0 :     PRUnichar c = *tokenEnd;
         323               0 :     if (IsSpace(c) ||
         324               0 :        (!escape && (c == '+' || c == '-' || (aBreakOnDot && c == '.')))) {
         325               0 :       break;
         326                 :     }
         327               0 :     escape = (!escape && c == '\\');
         328               0 :     ++tokenEnd;
         329                 :   }
         330               0 :   return tokenEnd;
         331                 : }
         332                 :
         333                 : void
         334               0 : Unescape(nsAString& aStr)
         335                 : {
         336               0 :   const PRUnichar* read = aStr.BeginReading();
         337               0 :   const PRUnichar* const end = aStr.EndReading();
         338               0 :   PRUnichar* write = aStr.BeginWriting();
         339               0 :   bool escape = false;
         340                 :
         341               0 :   while (read != end) {
         342               0 :     NS_ABORT_IF_FALSE(write <= read, "Writing past where we've read");
         343               0 :     if (!escape && *read == '\\') {
         344               0 :       escape = true;
         345               0 :       ++read;
         346                 :     } else {
         347               0 :       *write++ = *read++;
         348               0 :       escape = false;
         349                 :     }
         350                 :   }
         351                 :
         352               0 :   aStr.SetLength(write - aStr.BeginReading());
         353               0 : }
         354                 :
         355                 : nsresult
         356               0 : ParseElementBaseTimeValueSpec(const nsAString& aSpec,
         357                 :                               nsSMILTimeValueSpecParams& aResult)
         358                 : {
         359               0 :   nsSMILTimeValueSpecParams result;
         360                 :
         361                 :   //
         362                 :   // The spec will probably look something like one of these
         363                 :   //
         364                 :   // element-name.begin
         365                 :   // element-name.event-name
         366                 :   // event-name
         367                 :   // element-name.repeat(3)
         368                 :   // event\.name
         369                 :   //
         370                 :   // Technically `repeat(3)' is permitted but the behaviour in this case is not
         371                 :   // defined (for SMIL Animation) so we don't support it here.
         372                 :   //
         373                 :
         374               0 :   const PRUnichar* tokenStart = aSpec.BeginReading();
         375               0 :   const PRUnichar* tokenEnd = GetTokenEnd(aSpec, true);
         376               0 :   nsAutoString token(Substring(tokenStart, tokenEnd));
         377               0 :   Unescape(token);
         378                 :
         379               0 :   if (token.IsEmpty())
         380               0 :     return NS_ERROR_FAILURE;
         381                 :
         382                 :   // Whether the token is an id-ref or event-symbol it should be a valid NCName
         383               0 :   if (NS_FAILED(nsContentUtils::CheckQName(token, false)))
         384               0 :     return NS_ERROR_FAILURE;
         385                 :
         386                 :   // Parse the second token if there is one
         387               0 :   if (tokenEnd != aSpec.EndReading() && *tokenEnd == '.') {
         388               0 :     result.mDependentElemID = do_GetAtom(token);
         389                 :
         390               0 :     tokenStart = ++tokenEnd;
         391               0 :     tokenEnd = GetTokenEnd(Substring(tokenStart, aSpec.EndReading()), false);
         392                 :
         393                 :     // Don't unescape the token unless we need to and not until after we've
         394                 :     // tested it
         395               0 :     const nsAString& rawToken2 = Substring(tokenStart, tokenEnd);
         396                 :
         397                 :     // element-name.begin
         398               0 :     if (rawToken2.Equals(NS_LITERAL_STRING("begin"))) {
         399               0 :       result.mType = nsSMILTimeValueSpecParams::SYNCBASE;
         400               0 :       result.mSyncBegin = true;
         401                 :     // element-name.end
         402               0 :     } else if (rawToken2.Equals(NS_LITERAL_STRING("end"))) {
         403               0 :       result.mType = nsSMILTimeValueSpecParams::SYNCBASE;
         404               0 :       result.mSyncBegin = false;
         405                 :     // element-name.repeat(digit+)
         406               0 :     } else if (StringBeginsWith(rawToken2, REPEAT_PREFIX)) {
         407               0 :       result.mType = nsSMILTimeValueSpecParams::REPEAT;
         408               0 :       if (!GetUnsignedIntAndEndParen(
         409               0 :             Substring(tokenStart + REPEAT_PREFIX.Length(), tokenEnd),
         410               0 :             result.mRepeatIterationOrAccessKey))
         411               0 :         return NS_ERROR_FAILURE;
         412                 :     // element-name.event-symbol
         413                 :     } else {
         414               0 :       nsAutoString token2(rawToken2);
         415               0 :       Unescape(token2);
         416               0 :       result.mType = nsSMILTimeValueSpecParams::EVENT;
         417               0 :       if (token2.IsEmpty() ||
         418               0 :           NS_FAILED(nsContentUtils::CheckQName(token2, false)))
         419               0 :         return NS_ERROR_FAILURE;
         420               0 :       result.mEventSymbol = do_GetAtom(token2);
         421                 :     }
         422                 :   } else {
         423                 :     // event-symbol
         424               0 :     result.mType = nsSMILTimeValueSpecParams::EVENT;
         425               0 :     result.mEventSymbol = do_GetAtom(token);
         426                 :   }
         427                 :
         428                 :   // We've reached the end of the token, so we should now be either looking at
         429                 :   // a '+', '-', or the end.
         430               0 :   const PRUnichar* specEnd = aSpec.EndReading();
         431               0 :   SkipBeginWsp(tokenEnd, specEnd);
         432                 :
         433               0 :   nsresult rv = ParseOptionalOffset(Substring(tokenEnd, specEnd), result);
         434               0 :   if (NS_SUCCEEDED(rv)) {
         435               0 :     aResult = result;
         436                 :   }
         437                 :
         438               0 :   return rv;
         439                 : }
         440                 :
         441                 : } // end anonymous namespace block
         442                 :
         443                 : //------------------------------------------------------------------------------
         444                 : // Implementation
         445                 :
         446                 : nsresult
         447               0 : nsSMILParserUtils::ParseKeySplines(const nsAString& aSpec,
         448                 :                                    nsTArray<double>& aSplineArray)
         449                 : {
         450               0 :   nsresult rv = NS_OK;
         451                 :
         452               0 :   NS_ConvertUTF16toUTF8 spec(aSpec);
         453               0 :   const char* start = spec.BeginReading();
         454               0 :   const char* end = spec.EndReading();
         455                 :
         456               0 :   SkipBeginWsp(start, end);
         457                 :
         458               0 :   int i = 0;
         459                 :
         460               0 :   while (start != end)
         461                 :   {
         462               0 :     double value = GetFloat(start, end, &rv);
         463               0 :     if (NS_FAILED(rv))
         464               0 :       break;
         465                 :
         466               0 :     if (value > 1.0 || value < 0.0) {
         467               0 :       rv = NS_ERROR_FAILURE;
         468               0 :       break;
         469                 :     }
         470                 :
         471               0 :     if (!aSplineArray.AppendElement(value)) {
         472               0 :       rv = NS_ERROR_OUT_OF_MEMORY;
         473               0 :       break;
         474                 :     }
         475                 :
         476               0 :     ++i;
         477                 :
         478               0 :     SkipBeginWsp(start, end);
         479               0 :     if (start == end)
         480               0 :       break;
         481                 :
         482               0 :     if (i % 4) {
         483               0 :       if (*start == ',') {
         484               0 :         ++start;
         485                 :       }
         486                 :     } else {
         487               0 :       if (*start != ';') {
         488               0 :         rv = NS_ERROR_FAILURE;
         489               0 :         break;
         490                 :       }
         491               0 :       ++start;
         492                 :     }
         493                 :
         494               0 :     SkipBeginWsp(start, end);
         495                 :   }
         496                 :
         497               0 :   if (i % 4) {
         498               0 :     rv = NS_ERROR_FAILURE; // wrong number of points
         499                 :   }
         500                 :
         501               0 :   return rv;
         502                 : }
         503                 :
         504                 : nsresult
         505               0 : nsSMILParserUtils::ParseSemicolonDelimitedProgressList(const nsAString& aSpec,
         506                 :                                                        bool aNonDecreasing,
         507                 :                                                        nsTArray<double>& aArray)
         508                 : {
         509               0 :   nsresult rv = NS_OK;
         510                 :
         511               0 :   NS_ConvertUTF16toUTF8 spec(aSpec);
         512               0 :   const char* start = spec.BeginReading();
         513               0 :   const char* end = spec.EndReading();
         514                 :
         515               0 :   SkipBeginWsp(start, end);
         516                 :
         517               0 :   double previousValue = -1.0;
         518                 :
         519               0 :   while (start != end) {
         520               0 :     double value = GetFloat(start, end, &rv);
         521               0 :     if (NS_FAILED(rv))
         522               0 :       break;
         523                 :
         524               0 :     if (value > 1.0 || value < 0.0 ||
         525                 :         (aNonDecreasing && value < previousValue)) {
         526               0 :       rv = NS_ERROR_FAILURE;
         527               0 :       break;
         528                 :     }
         529                 :
         530               0 :     if (!aArray.AppendElement(value)) {
         531               0 :       rv = NS_ERROR_OUT_OF_MEMORY;
         532               0 :       break;
         533                 :     }
         534               0 :     previousValue = value;
         535                 :
         536               0 :     SkipBeginWsp(start, end);
         537               0 :     if (start == end)
         538               0 :       break;
         539                 :
         540               0 :     if (*start++ != ';') {
         541               0 :       rv = NS_ERROR_FAILURE;
         542               0 :       break;
         543                 :     }
         544                 :
         545               0 :     SkipBeginWsp(start, end);
         546                 :   }
         547                 :
         548               0 :   return rv;
         549                 : }
         550                 :
         551                 : // Helper class for ParseValues
         552                 : class SMILValueParser : public nsSMILParserUtils::GenericValueParser
         553                 : {
         554                 : public:
         555               0 :   SMILValueParser(const nsISMILAnimationElement* aSrcElement,
         556                 :                   const nsISMILAttr* aSMILAttr,
         557                 :                   nsTArray<nsSMILValue>* aValuesArray,
         558                 :                   bool* aPreventCachingOfSandwich) :
         559                 :     mSrcElement(aSrcElement),
         560                 :     mSMILAttr(aSMILAttr),
         561                 :     mValuesArray(aValuesArray),
         562               0 :     mPreventCachingOfSandwich(aPreventCachingOfSandwich)
         563               0 :   {}
         564                 :
         565               0 :   virtual nsresult Parse(const nsAString& aValueStr) {
         566               0 :     nsSMILValue newValue;
         567               0 :     bool tmpPreventCachingOfSandwich = false;
         568                 :     nsresult rv = mSMILAttr->ValueFromString(aValueStr, mSrcElement, newValue,
         569               0 :                                              tmpPreventCachingOfSandwich);
         570               0 :     if (NS_FAILED(rv))
         571               0 :       return rv;
         572                 :
         573               0 :     if (!mValuesArray->AppendElement(newValue)) {
         574               0 :       return NS_ERROR_OUT_OF_MEMORY;
         575                 :     }
         576               0 :     if (tmpPreventCachingOfSandwich) {
         577               0 :       *mPreventCachingOfSandwich = true;
         578                 :     }
         579               0 :     return NS_OK;
         580                 :   }
         581                 : protected:
         582                 :   const nsISMILAnimationElement* mSrcElement;
         583                 :   const nsISMILAttr* mSMILAttr;
         584                 :   nsTArray<nsSMILValue>* mValuesArray;
         585                 :   bool* mPreventCachingOfSandwich;
         586                 : };
         587                 :
         588                 : nsresult
         589               0 : nsSMILParserUtils::ParseValues(const nsAString& aSpec,
         590                 :                                const nsISMILAnimationElement* aSrcElement,
         591                 :                                const nsISMILAttr& aAttribute,
         592                 :                                nsTArray<nsSMILValue>& aValuesArray,
         593                 :                                bool& aPreventCachingOfSandwich)
         594                 : {
         595                 :   // Assume all results can be cached, until we find one that can't.
         596               0 :   aPreventCachingOfSandwich = false;
         597                 :   SMILValueParser valueParser(aSrcElement, &aAttribute,
         598               0 :                               &aValuesArray, &aPreventCachingOfSandwich);
         599               0 :   return ParseValuesGeneric(aSpec, valueParser);
         600                 : }
         601                 :
         602                 : nsresult
         603               0 : nsSMILParserUtils::ParseValuesGeneric(const nsAString& aSpec,
         604                 :                                       GenericValueParser& aParser)
         605                 : {
         606               0 :   nsCharSeparatedTokenizer tokenizer(aSpec, ';');
         607               0 :   if (!tokenizer.hasMoreTokens()) { // Empty list
         608               0 :     return NS_ERROR_FAILURE;
         609                 :   }
         610                 :
         611               0 :   while (tokenizer.hasMoreTokens()) {
         612               0 :     nsresult rv = aParser.Parse(tokenizer.nextToken());
         613               0 :     if (NS_FAILED(rv)) {
         614               0 :       return NS_ERROR_FAILURE;
         615                 :     }
         616                 :   }
         617                 :
         618               0 :   return NS_OK;
         619                 : }
         620                 :
         621                 : nsresult
         622               0 : nsSMILParserUtils::ParseRepeatCount(const nsAString& aSpec,
         623                 :                                     nsSMILRepeatCount& aResult)
         624                 : {
         625               0 :   nsresult rv = NS_OK;
         626                 :
         627               0 :   NS_ConvertUTF16toUTF8 spec(aSpec);
         628               0 :   const char* start = spec.BeginReading();
         629               0 :   const char* end = spec.EndReading();
         630                 :
         631               0 :   SkipBeginWsp(start, end);
         632                 :
         633               0 :   if (start != end)
         634                 :   {
         635               0 :     if (ConsumeSubstring(start, end, "indefinite")) {
         636               0 :       aResult.SetIndefinite();
         637                 :     } else {
         638               0 :       double value = GetFloat(start, end, &rv);
         639                 :
         640               0 :       if (NS_SUCCEEDED(rv))
         641                 :       {
         642                 :         /* Repeat counts must be > 0 */
         643               0 :         if (value <= 0.0) {
         644               0 :           rv = NS_ERROR_FAILURE;
         645                 :         } else {
         646               0 :           aResult = value;
         647                 :         }
         648                 :       }
         649                 :     }
         650                 :
         651                 :     /* Check for trailing junk */
         652               0 :     SkipBeginWsp(start, end);
         653               0 :     if (start != end) {
         654               0 :       rv = NS_ERROR_FAILURE;
         655                 :     }
         656                 :   } else {
         657                 :     /* Empty spec */
         658               0 :     rv = NS_ERROR_FAILURE;
         659                 :   }
         660                 :
         661               0 :   if (NS_FAILED(rv)) {
         662               0 :     aResult.Unset();
         663                 :   }
         664                 :
         665               0 :   return rv;
         666                 : }
         667                 :
         668                 : nsresult
         669               0 : nsSMILParserUtils::ParseTimeValueSpecParams(const nsAString& aSpec,
         670                 :                                             nsSMILTimeValueSpecParams& aResult)
         671                 : {
         672               0 :   nsresult rv = NS_ERROR_FAILURE;
         673                 :
         674               0 :   const PRUnichar* start = aSpec.BeginReading();
         675               0 :   const PRUnichar* end = aSpec.EndReading();
         676                 :
         677               0 :   SkipBeginEndWsp(start, end);
         678               0 :   if (start == end)
         679               0 :     return rv;
         680                 :
         681               0 :   const nsAString &spec = Substring(start, end);
         682                 :
         683                 :   // offset type
         684               0 :   if (*start == '+' || *start == '-' || NS_IsAsciiDigit(*start)) {
         685                 :     rv = ParseClockValue(spec, &aResult.mOffset,
         686               0 :                          nsSMILParserUtils::kClockValueAllowSign);
         687               0 :     if (NS_SUCCEEDED(rv)) {
         688               0 :       aResult.mType = nsSMILTimeValueSpecParams::OFFSET;
         689                 :     }
         690                 :   }
         691                 :
         692                 :   // indefinite
         693               0 :   else if (spec.Equals(NS_LITERAL_STRING("indefinite"))) {
         694               0 :     aResult.mType = nsSMILTimeValueSpecParams::INDEFINITE;
         695               0 :     rv = NS_OK;
         696                 :   }
         697                 :
         698                 :   // wallclock type
         699               0 :   else if (StringBeginsWith(spec, WALLCLOCK_PREFIX)) {
         700               0 :     rv = NS_ERROR_NOT_IMPLEMENTED;
         701                 :   }
         702                 :
         703                 :   // accesskey type
         704               0 :   else if (StringBeginsWith(spec, ACCESSKEY_PREFIX_LC) ||
         705               0 :            StringBeginsWith(spec, ACCESSKEY_PREFIX_CC)) {
         706               0 :     rv = ParseAccessKey(spec, aResult);
         707                 :   }
         708                 :
         709                 :   // event, syncbase, or repeat
         710                 :   else {
         711               0 :     rv = ParseElementBaseTimeValueSpec(spec, aResult);
         712                 :   }
         713                 :
         714               0 :   return rv;
         715                 : }
         716                 :
         717                 : nsresult
         718               0 : nsSMILParserUtils::ParseClockValue(const nsAString& aSpec,
         719                 :                                    nsSMILTimeValue* aResult,
         720                 :                                    PRUint32 aFlags,   // = 0
         721                 :                                    bool* aIsMedia)  // = nsnull
         722                 : {
         723               0 :   nsSMILTime offset = 0L;
         724               0 :   double component = 0.0;
         725                 :
         726               0 :   PRInt8 sign = 0;
         727               0 :   PRUint8 colonCount = 0;
         728                 :
         729                 :   // Indicates we have started parsing a clock-value (not including the optional
         730                 :   // +/- that precedes the clock-value) or keyword ("media", "indefinite")
         731               0 :   bool started = false;
         732                 :
         733               0 :   PRInt32 metricMultiplicand = MSEC_PER_SEC;
         734                 :
         735               0 :   bool numIsReal = false;
         736               0 :   bool prevNumCouldBeMin = false;
         737               0 :   bool numCouldBeMin = false;
         738               0 :   bool numCouldBeSec = false;
         739               0 :   bool isIndefinite = false;
         740                 :
         741               0 :   if (aIsMedia) {
         742               0 :     *aIsMedia = false;
         743                 :   }
         744                 :
         745               0 :   NS_ConvertUTF16toUTF8 spec(aSpec);
         746               0 :   const char* start = spec.BeginReading();
         747               0 :   const char* end = spec.EndReading();
         748                 :
         749               0 :   while (start != end) {
         750               0 :     if (IsSpace(*start)) {
         751               0 :       ++start;
         752               0 :       if (started) {
         753               0 :         break;
         754                 :       }
         755               0 :     } else if (!started && (aFlags & kClockValueAllowSign) &&
         756                 :                (*start == '+' || *start == '-')) {
         757                 :       // check sign has not already been set (e.g. ++10s)
         758               0 :       if (sign != 0) {
         759               0 :         return NS_ERROR_FAILURE;
         760                 :       }
         761                 :
         762               0 :       sign = (*start == '+') ? 1 : -1;
         763               0 :       ++start;
         764                 :     // The NS_IS_DIGIT etc. macros are not locale-specific
         765               0 :     } else if (NS_IS_DIGIT(*start)) {
         766               0 :       prevNumCouldBeMin = numCouldBeMin;
         767                 :
         768               0 :       if (!ParseClockComponent(start, end, component, numIsReal, numCouldBeMin,
         769               0 :                                numCouldBeSec)) {
         770               0 :         return NS_ERROR_FAILURE;
         771                 :       }
         772               0 :       started = true;
         773               0 :     } else if (started && *start == ':') {
         774               0 :       ++colonCount;
         775                 :
         776                 :       // Neither minutes nor hours can be reals
         777               0 :       if (numIsReal) {
         778               0 :         return NS_ERROR_FAILURE;
         779                 :       }
         780                 :
         781                 :       // Can't have more than two colons
         782               0 :       if (colonCount > 2) {
         783               0 :         return NS_ERROR_FAILURE;
         784                 :       }
         785                 :
         786                 :       // Multiply the offset by 60 and add the last accumulated component
         787               0 :       offset = offset * 60 + nsSMILTime(component);
         788                 :
         789               0 :       component = 0.0;
         790               0 :       ++start;
         791               0 :     } else if (NS_IS_ALPHA(*start)) {
         792               0 :       if (colonCount > 0) {
         793               0 :         return NS_ERROR_FAILURE;
         794                 :       }
         795                 :
         796               0 :       if (!started && (aFlags & kClockValueAllowIndefinite) &&
         797               0 :           ConsumeSubstring(start, end, "indefinite")) {
         798                 :         // We set a separate flag because we don't know what the state of the
         799                 :         // passed in time value is and we shouldn't change it in the case of a
         800                 :         // bad input string (so we can't initialise it to 0ms for example).
         801               0 :         isIndefinite = true;
         802               0 :         if (aResult) {
         803               0 :           aResult->SetIndefinite();
         804                 :         }
         805               0 :         started = true;
         806               0 :       } else if (!started && aIsMedia &&
         807               0 :                  ConsumeSubstring(start, end, "media")) {
         808               0 :         *aIsMedia = true;
         809               0 :         started = true;
         810               0 :       } else if (!ParseMetricMultiplicand(start, end, metricMultiplicand)) {
         811               0 :         return NS_ERROR_FAILURE;
         812                 :       }
         813                 :
         814                 :       // Nothing must come after the string except whitespace
         815               0 :       break;
         816                 :     } else {
         817               0 :       return NS_ERROR_FAILURE;
         818                 :     }
         819                 :   }
         820                 :
         821                 :   // Whitespace/empty string
         822               0 :   if (!started) {
         823               0 :     return NS_ERROR_FAILURE;
         824                 :   }
         825                 :
         826                 :   // Process remainder of string (if any) to ensure it is only trailing
         827                 :   // whitespace (embedded whitespace is not allowed)
         828               0 :   SkipBeginWsp(start, end);
         829               0 :   if (start != end) {
         830               0 :     return NS_ERROR_FAILURE;
         831                 :   }
         832                 :
         833                 :   // No more processing required if the value was "indefinite" or "media".
         834               0 :   if (isIndefinite || (aIsMedia && *aIsMedia)) {
         835               0 :     return NS_OK;
         836                 :   }
         837                 :
         838                 :   // If there is more than one colon then the previous component must be a
         839                 :   // correctly formatted minute (i.e. two digits between 00 and 59) and the
         840                 :   // latest component must be a correctly formatted second (i.e. two digits
         841                 :   // before the .)
         842               0 :   if (colonCount > 0 && (!prevNumCouldBeMin || !numCouldBeSec)) {
         843               0 :     return NS_ERROR_FAILURE;
         844                 :   }
         845                 :
         846                 :   // Tack on the last component
         847               0 :   if (colonCount > 0) {
         848               0 :     offset *= 60 * 1000;
         849               0 :     component *= 1000;
         850                 :     // rounding
         851               0 :     component = (component >= 0) ? component + 0.5 : component - 0.5;
         852               0 :     offset += nsSMILTime(component);
         853                 :   } else {
         854               0 :     component *= metricMultiplicand;
         855                 :     // rounding
         856               0 :     component = (component >= 0) ? component + 0.5 : component - 0.5;
         857               0 :     offset = nsSMILTime(component);
         858                 :   }
         859                 :
         860                 :   // we haven't applied the sign yet so if the result is negative we must have
         861                 :   // overflowed
         862               0 :   if (offset < 0) {
         863               0 :     return NS_ERROR_FAILURE;
         864                 :   }
         865                 :
         866               0 :   if (aResult) {
         867               0 :     if (sign == -1) {
         868               0 :       offset = -offset;
         869                 :     }
         870               0 :     aResult->SetMillis(offset);
         871                 :   }
         872                 :
         873               0 :   return NS_OK;
         874                 : }
         875                 :
         876                 : PRInt32
         877               0 : nsSMILParserUtils::CheckForNegativeNumber(const nsAString& aStr)
         878                 : {
         879               0 :   PRInt32 absValLocation = -1;
         880                 :
         881               0 :   nsAString::const_iterator start, end;
         882               0 :   aStr.BeginReading(start);
         883               0 :   aStr.EndReading(end);
         884                 :
         885                 :   // Skip initial whitespace
         886               0 :   SkipBeginWsp(start, end);
         887                 :
         888                 :   // Check for dash
         889               0 :   if (start != end && *start == '-') {
         890               0 :     ++start;
         891                 :     // Check for numeric character
         892               0 :     if (start != end && NS_IS_DIGIT(*start)) {
         893               0 :       absValLocation = start.get() - start.start();
         894                 :     }
         895                 :   }
         896               0 :   return absValLocation;
         897                 : }

Generated by: LCOV version 1.7