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

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

mercurial