netwerk/cache2/CacheFileUtils.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:586d9f21b2e1
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/. */
4
5 #include "CacheLog.h"
6 #include "CacheFileUtils.h"
7 #include "LoadContextInfo.h"
8 #include "nsCOMPtr.h"
9 #include "nsAutoPtr.h"
10 #include "nsString.h"
11
12
13 namespace mozilla {
14 namespace net {
15 namespace CacheFileUtils {
16
17 namespace { // anon
18
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 }
38
39 private:
40 // Current character being parsed
41 nsACString::const_iterator caret;
42 // The end of the buffer
43 nsACString::const_iterator const end;
44
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;
53
54 // Keeps the last tag name, used for alphabetical sort checking
55 char lastTag;
56
57 bool ParseTags()
58 {
59 // Expects to be at the tag name or at the end
60 if (caret == end)
61 return true;
62
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;
68
69 lastTag = tag;
70
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;
91
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);
99
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 }
111
112 // Recurse to the next tag
113 return ParseNextTagOrEnd();
114 }
115
116 bool ParseNextTagOrEnd()
117 {
118 // We expect a comma after every tag
119 if (caret == end || *caret++ != ',')
120 return false;
121
122 // Go to another tag
123 return ParseTags();
124 }
125
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;
131
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
139
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 }
150
151 if (comma != end) {
152 // after a single comma
153 break;
154 }
155 }
156
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.
160
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 }
172
173 return caret != end;
174 }
175
176 public:
177 already_AddRefed<LoadContextInfo> Parse()
178 {
179 nsRefPtr<LoadContextInfo> info;
180 if (ParseTags())
181 info = GetLoadContextInfo(isPrivate, appId, isInBrowser, isAnonymous);
182
183 return info.forget();
184 }
185
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 }
191
192 void IdEnhance(nsACString &result)
193 {
194 result.Assign(idEnhance);
195 }
196 };
197
198 } // anon
199
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);
208
209 KeyParser parser(caret, end);
210 nsRefPtr<LoadContextInfo> info = parser.Parse();
211
212 if (info) {
213 if (aIdEnhance)
214 parser.IdEnhance(*aIdEnhance);
215 if (aURISpec)
216 parser.URISpec(*aURISpec);
217 }
218
219 return info.forget();
220 }
221
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 */
232
233 if (aInfo->IsAnonymous()) {
234 _retval.Append(NS_LITERAL_CSTRING("a,"));
235 }
236
237 if (aInfo->IsInBrowserElement()) {
238 _retval.Append(NS_LITERAL_CSTRING("b,"));
239 }
240
241 if (aInfo->AppId() != nsILoadContextInfo::NO_APP_ID) {
242 _retval.Append('i');
243 _retval.AppendInt(aInfo->AppId());
244 _retval.Append(',');
245 }
246
247 if (aInfo->IsPrivate()) {
248 _retval.Append(NS_LITERAL_CSTRING("p,"));
249 }
250 }
251
252 void
253 AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue)
254 {
255 aTarget.Append(aTag);
256
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 }
270
271 aTarget.Append(',');
272 }
273
274 nsresult
275 KeyMatchesLoadContextInfo(const nsACString &aKey, nsILoadContextInfo *aInfo,
276 bool *_retval)
277 {
278 nsCOMPtr<nsILoadContextInfo> info = ParseKey(aKey);
279
280 if (!info) {
281 return NS_ERROR_FAILURE;
282 }
283
284 *_retval = info->Equals(aInfo);
285 return NS_OK;
286 }
287
288 } // CacheFileUtils
289 } // net
290 } // mozilla

mercurial