netwerk/cache2/CacheFileUtils.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "CacheLog.h"
     6 #include "CacheFileUtils.h"
     7 #include "LoadContextInfo.h"
     8 #include "nsCOMPtr.h"
     9 #include "nsAutoPtr.h"
    10 #include "nsString.h"
    13 namespace mozilla {
    14 namespace net {
    15 namespace CacheFileUtils {
    17 namespace { // anon
    19 /**
    20  * A simple recursive descent parser for the mapping key.
    21  */
    22 class KeyParser
    23 {
    24 public:
    25   KeyParser(nsACString::const_iterator aCaret, nsACString::const_iterator aEnd)
    26     : caret(aCaret)
    27     , end(aEnd)
    28     // Initialize attributes to their default values
    29     , appId(nsILoadContextInfo::NO_APP_ID)
    30     , isPrivate(false)
    31     , isInBrowser(false)
    32     , isAnonymous(false)
    33     // Initialize the cache key to a zero length by default
    34     , cacheKey(aEnd)
    35     , lastTag(0)
    36   {
    37   }
    39 private:
    40   // Current character being parsed
    41   nsACString::const_iterator caret;
    42   // The end of the buffer
    43   nsACString::const_iterator const end;
    45   // Results
    46   uint32_t appId;
    47   bool isPrivate;
    48   bool isInBrowser;
    49   bool isAnonymous;
    50   nsCString idEnhance;
    51   // Position of the cache key, if present
    52   nsACString::const_iterator cacheKey;
    54   // Keeps the last tag name, used for alphabetical sort checking
    55   char lastTag;
    57   bool ParseTags()
    58   {
    59     // Expects to be at the tag name or at the end
    60     if (caret == end)
    61       return true;
    63     // 'Read' the tag name and move to the next char
    64     char const tag = *caret++;
    65     // Check the alphabetical order, hard-fail on disobedience
    66     if (!(lastTag < tag || tag == ':'))
    67       return false;
    69     lastTag = tag;
    71     switch (tag) {
    72     case ':':
    73       // last possible tag, when present there is the cacheKey following,
    74       // not terminated with ',' and no need to unescape.
    75       cacheKey = caret;
    76       caret = end;
    77       return true;
    78     case 'p':
    79       isPrivate = true;
    80       break;
    81     case 'b':
    82       isInBrowser = true;
    83       break;
    84     case 'a':
    85       isAnonymous = true;
    86       break;
    87     case 'i': {
    88       nsAutoCString appIdString;
    89       if (!ParseValue(&appIdString))
    90         return false;
    92       nsresult rv;
    93       int64_t appId64 = appIdString.ToInteger64(&rv);
    94       if (NS_FAILED(rv))
    95         return false; // appid value is mandatory
    96       if (appId64 < 0 || appId64 > PR_UINT32_MAX)
    97         return false; // not in the range
    98       appId = static_cast<uint32_t>(appId64);
   100       break;
   101     }
   102     case '~':
   103       if (!ParseValue(&idEnhance))
   104         return false;
   105       break;
   106     default:
   107       if (!ParseValue()) // skip any tag values, optional
   108         return false;
   109       break;
   110     }
   112     // Recurse to the next tag
   113     return ParseNextTagOrEnd();
   114   }
   116   bool ParseNextTagOrEnd()
   117   {
   118     // We expect a comma after every tag
   119     if (caret == end || *caret++ != ',')
   120       return false;
   122     // Go to another tag
   123     return ParseTags();
   124   }
   126   bool ParseValue(nsACString * result = nullptr)
   127   {
   128     // If at the end, fail since we expect a comma ; value may be empty tho
   129     if (caret == end)
   130       return false;
   132     // Remeber where the value starts
   133     nsACString::const_iterator val = caret;
   134     nsACString::const_iterator comma = end;
   135     bool escape = false;
   136     while (caret != end) {
   137       nsACString::const_iterator at = caret;
   138       ++caret; // we can safely break/continue the loop now
   140       if (*at == ',') {
   141         if (comma != end) {
   142           // another comma (we have found ",," -> escape)
   143           comma = end;
   144           escape = true;
   145         } else {
   146           comma = at;
   147         }
   148         continue;
   149       }
   151       if (comma != end) {
   152         // after a single comma
   153         break;
   154       }
   155     }
   157     // At this point |comma| points to the last and lone ',' we've hit.
   158     // If a lone comma was not found, |comma| is at the end of the buffer,
   159     // that is not expected and we return failure.
   161     caret = comma;
   162     if (result) {
   163       if (escape) {
   164         // No ReplaceSubstring on nsACString..
   165         nsAutoCString _result(Substring(val, caret));
   166         _result.ReplaceSubstring(NS_LITERAL_CSTRING(",,"), NS_LITERAL_CSTRING(","));
   167         result->Assign(_result);
   168       } else {
   169         result->Assign(Substring(val, caret));
   170       }
   171     }
   173     return caret != end;
   174   }
   176 public:
   177   already_AddRefed<LoadContextInfo> Parse()
   178   {
   179     nsRefPtr<LoadContextInfo> info;
   180     if (ParseTags())
   181       info = GetLoadContextInfo(isPrivate, appId, isInBrowser, isAnonymous);
   183     return info.forget();
   184   }
   186   void URISpec(nsACString &result)
   187   {
   188     // cacheKey is either pointing to end or the position where the cache key is.
   189     result.Assign(Substring(cacheKey, end));
   190   }
   192   void IdEnhance(nsACString &result)
   193   {
   194     result.Assign(idEnhance);
   195   }
   196 };
   198 } // anon
   200 already_AddRefed<nsILoadContextInfo>
   201 ParseKey(const nsCSubstring &aKey,
   202          nsCSubstring *aIdEnhance,
   203          nsCSubstring *aURISpec)
   204 {
   205   nsACString::const_iterator caret, end;
   206   aKey.BeginReading(caret);
   207   aKey.EndReading(end);
   209   KeyParser parser(caret, end);
   210   nsRefPtr<LoadContextInfo> info = parser.Parse();
   212   if (info) {
   213     if (aIdEnhance)
   214       parser.IdEnhance(*aIdEnhance);
   215     if (aURISpec)
   216       parser.URISpec(*aURISpec);
   217   }
   219   return info.forget();
   220 }
   222 void
   223 AppendKeyPrefix(nsILoadContextInfo* aInfo, nsACString &_retval)
   224 {
   225   /**
   226    * This key is used to salt file hashes.  When form of the key is changed
   227    * cache entries will fail to find on disk.
   228    *
   229    * IMPORTANT NOTE:
   230    * Keep the attributes list sorted according their ASCII code.
   231    */
   233   if (aInfo->IsAnonymous()) {
   234     _retval.Append(NS_LITERAL_CSTRING("a,"));
   235   }
   237   if (aInfo->IsInBrowserElement()) {
   238     _retval.Append(NS_LITERAL_CSTRING("b,"));
   239   }
   241   if (aInfo->AppId() != nsILoadContextInfo::NO_APP_ID) {
   242     _retval.Append('i');
   243     _retval.AppendInt(aInfo->AppId());
   244     _retval.Append(',');
   245   }
   247   if (aInfo->IsPrivate()) {
   248     _retval.Append(NS_LITERAL_CSTRING("p,"));
   249   }
   250 }
   252 void
   253 AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue)
   254 {
   255   aTarget.Append(aTag);
   257   // First check the value string to save some memory copying
   258   // for cases we don't need to escape at all (most likely).
   259   if (!aValue.IsEmpty()) {
   260     if (aValue.FindChar(',') == kNotFound) {
   261       // No need to escape
   262       aTarget.Append(aValue);
   263     } else {
   264       nsAutoCString escapedValue(aValue);
   265       escapedValue.ReplaceSubstring(
   266         NS_LITERAL_CSTRING(","), NS_LITERAL_CSTRING(",,"));
   267       aTarget.Append(escapedValue);
   268     }
   269   }
   271   aTarget.Append(',');
   272 }
   274 nsresult
   275 KeyMatchesLoadContextInfo(const nsACString &aKey, nsILoadContextInfo *aInfo,
   276                           bool *_retval)
   277 {
   278   nsCOMPtr<nsILoadContextInfo> info = ParseKey(aKey);
   280   if (!info) {
   281     return NS_ERROR_FAILURE;
   282   }
   284   *_retval = info->Equals(aInfo);
   285   return NS_OK;
   286 }
   288 } // CacheFileUtils
   289 } // net
   290 } // mozilla

mercurial