|
1 /* -*- Mode: C++; indent-tab-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cin: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "mozilla/ArrayUtils.h" |
|
8 #include "mozilla/Attributes.h" |
|
9 |
|
10 #include "nsCache.h" |
|
11 #include "nsDiskCache.h" |
|
12 #include "nsDiskCacheDeviceSQL.h" |
|
13 #include "nsCacheService.h" |
|
14 #include "nsApplicationCache.h" |
|
15 |
|
16 #include "nsNetCID.h" |
|
17 #include "nsNetUtil.h" |
|
18 #include "nsAutoPtr.h" |
|
19 #include "nsEscape.h" |
|
20 #include "nsIPrefBranch.h" |
|
21 #include "nsIPrefService.h" |
|
22 #include "nsString.h" |
|
23 #include "nsPrintfCString.h" |
|
24 #include "nsCRT.h" |
|
25 #include "nsArrayUtils.h" |
|
26 #include "nsIArray.h" |
|
27 #include "nsIVariant.h" |
|
28 #include "nsILoadContextInfo.h" |
|
29 #include "nsThreadUtils.h" |
|
30 #include "nsISerializable.h" |
|
31 #include "nsSerializationHelper.h" |
|
32 |
|
33 #include "mozIStorageService.h" |
|
34 #include "mozIStorageStatement.h" |
|
35 #include "mozIStorageFunction.h" |
|
36 #include "mozStorageHelper.h" |
|
37 |
|
38 #include "nsICacheVisitor.h" |
|
39 #include "nsISeekableStream.h" |
|
40 |
|
41 #include "mozilla/Telemetry.h" |
|
42 |
|
43 #include "sqlite3.h" |
|
44 #include "mozilla/storage.h" |
|
45 |
|
46 using namespace mozilla; |
|
47 using namespace mozilla::storage; |
|
48 |
|
49 static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" }; |
|
50 |
|
51 #define LOG(args) CACHE_LOG_DEBUG(args) |
|
52 |
|
53 static uint32_t gNextTemporaryClientID = 0; |
|
54 |
|
55 /***************************************************************************** |
|
56 * helpers |
|
57 */ |
|
58 |
|
59 static nsresult |
|
60 EnsureDir(nsIFile *dir) |
|
61 { |
|
62 bool exists; |
|
63 nsresult rv = dir->Exists(&exists); |
|
64 if (NS_SUCCEEDED(rv) && !exists) |
|
65 rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700); |
|
66 return rv; |
|
67 } |
|
68 |
|
69 static bool |
|
70 DecomposeCacheEntryKey(const nsCString *fullKey, |
|
71 const char **cid, |
|
72 const char **key, |
|
73 nsCString &buf) |
|
74 { |
|
75 buf = *fullKey; |
|
76 |
|
77 int32_t colon = buf.FindChar(':'); |
|
78 if (colon == kNotFound) |
|
79 { |
|
80 NS_ERROR("Invalid key"); |
|
81 return false; |
|
82 } |
|
83 buf.SetCharAt('\0', colon); |
|
84 |
|
85 *cid = buf.get(); |
|
86 *key = buf.get() + colon + 1; |
|
87 |
|
88 return true; |
|
89 } |
|
90 |
|
91 class AutoResetStatement |
|
92 { |
|
93 public: |
|
94 AutoResetStatement(mozIStorageStatement *s) |
|
95 : mStatement(s) {} |
|
96 ~AutoResetStatement() { mStatement->Reset(); } |
|
97 mozIStorageStatement *operator->() { return mStatement; } |
|
98 private: |
|
99 mozIStorageStatement *mStatement; |
|
100 }; |
|
101 |
|
102 class EvictionObserver |
|
103 { |
|
104 public: |
|
105 EvictionObserver(mozIStorageConnection *db, |
|
106 nsOfflineCacheEvictionFunction *evictionFunction) |
|
107 : mDB(db), mEvictionFunction(evictionFunction) |
|
108 { |
|
109 mDB->ExecuteSimpleSQL( |
|
110 NS_LITERAL_CSTRING("CREATE TEMP TRIGGER cache_on_delete BEFORE DELETE" |
|
111 " ON moz_cache FOR EACH ROW BEGIN SELECT" |
|
112 " cache_eviction_observer(" |
|
113 " OLD.ClientID, OLD.key, OLD.generation);" |
|
114 " END;")); |
|
115 mEvictionFunction->Reset(); |
|
116 } |
|
117 |
|
118 ~EvictionObserver() |
|
119 { |
|
120 mDB->ExecuteSimpleSQL( |
|
121 NS_LITERAL_CSTRING("DROP TRIGGER cache_on_delete;")); |
|
122 mEvictionFunction->Reset(); |
|
123 } |
|
124 |
|
125 void Apply() { return mEvictionFunction->Apply(); } |
|
126 |
|
127 private: |
|
128 mozIStorageConnection *mDB; |
|
129 nsRefPtr<nsOfflineCacheEvictionFunction> mEvictionFunction; |
|
130 }; |
|
131 |
|
132 #define DCACHE_HASH_MAX INT64_MAX |
|
133 #define DCACHE_HASH_BITS 64 |
|
134 |
|
135 /** |
|
136 * nsOfflineCache::Hash(const char * key) |
|
137 * |
|
138 * This algorithm of this method implies nsOfflineCacheRecords will be stored |
|
139 * in a certain order on disk. If the algorithm changes, existing cache |
|
140 * map files may become invalid, and therefore the kCurrentVersion needs |
|
141 * to be revised. |
|
142 */ |
|
143 static uint64_t |
|
144 DCacheHash(const char * key) |
|
145 { |
|
146 // initval 0x7416f295 was chosen randomly |
|
147 return (uint64_t(nsDiskCache::Hash(key, 0)) << 32) | nsDiskCache::Hash(key, 0x7416f295); |
|
148 } |
|
149 |
|
150 /****************************************************************************** |
|
151 * nsOfflineCacheEvictionFunction |
|
152 */ |
|
153 |
|
154 NS_IMPL_ISUPPORTS(nsOfflineCacheEvictionFunction, mozIStorageFunction) |
|
155 |
|
156 // helper function for directly exposing the same data file binding |
|
157 // path algorithm used in nsOfflineCacheBinding::Create |
|
158 static nsresult |
|
159 GetCacheDataFile(nsIFile *cacheDir, const char *key, |
|
160 int generation, nsCOMPtr<nsIFile> &file) |
|
161 { |
|
162 cacheDir->Clone(getter_AddRefs(file)); |
|
163 if (!file) |
|
164 return NS_ERROR_OUT_OF_MEMORY; |
|
165 |
|
166 uint64_t hash = DCacheHash(key); |
|
167 |
|
168 uint32_t dir1 = (uint32_t) (hash & 0x0F); |
|
169 uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4); |
|
170 |
|
171 hash >>= 8; |
|
172 |
|
173 file->AppendNative(nsPrintfCString("%X", dir1)); |
|
174 file->AppendNative(nsPrintfCString("%X", dir2)); |
|
175 |
|
176 char leaf[64]; |
|
177 PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation); |
|
178 return file->AppendNative(nsDependentCString(leaf)); |
|
179 } |
|
180 |
|
181 NS_IMETHODIMP |
|
182 nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray *values, nsIVariant **_retval) |
|
183 { |
|
184 LOG(("nsOfflineCacheEvictionFunction::OnFunctionCall\n")); |
|
185 |
|
186 *_retval = nullptr; |
|
187 |
|
188 uint32_t numEntries; |
|
189 nsresult rv = values->GetNumEntries(&numEntries); |
|
190 NS_ENSURE_SUCCESS(rv, rv); |
|
191 NS_ASSERTION(numEntries == 3, "unexpected number of arguments"); |
|
192 |
|
193 uint32_t valueLen; |
|
194 const char *clientID = values->AsSharedUTF8String(0, &valueLen); |
|
195 const char *key = values->AsSharedUTF8String(1, &valueLen); |
|
196 nsAutoCString fullKey(clientID); |
|
197 fullKey.AppendLiteral(":"); |
|
198 fullKey.Append(key); |
|
199 int generation = values->AsInt32(2); |
|
200 |
|
201 // If the key is currently locked, refuse to delete this row. |
|
202 if (mDevice->IsLocked(fullKey)) { |
|
203 NS_ADDREF(*_retval = new IntegerVariant(SQLITE_IGNORE)); |
|
204 return NS_OK; |
|
205 } |
|
206 |
|
207 nsCOMPtr<nsIFile> file; |
|
208 rv = GetCacheDataFile(mDevice->CacheDirectory(), key, |
|
209 generation, file); |
|
210 if (NS_FAILED(rv)) |
|
211 { |
|
212 LOG(("GetCacheDataFile [key=%s generation=%d] failed [rv=%x]!\n", |
|
213 key, generation, rv)); |
|
214 return rv; |
|
215 } |
|
216 |
|
217 mItems.AppendObject(file); |
|
218 |
|
219 return NS_OK; |
|
220 } |
|
221 |
|
222 void |
|
223 nsOfflineCacheEvictionFunction::Apply() |
|
224 { |
|
225 LOG(("nsOfflineCacheEvictionFunction::Apply\n")); |
|
226 |
|
227 for (int32_t i = 0; i < mItems.Count(); i++) { |
|
228 #if defined(PR_LOGGING) |
|
229 nsAutoCString path; |
|
230 mItems[i]->GetNativePath(path); |
|
231 LOG((" removing %s\n", path.get())); |
|
232 #endif |
|
233 |
|
234 mItems[i]->Remove(false); |
|
235 } |
|
236 |
|
237 Reset(); |
|
238 } |
|
239 |
|
240 class nsOfflineCacheDiscardCache : public nsRunnable |
|
241 { |
|
242 public: |
|
243 nsOfflineCacheDiscardCache(nsOfflineCacheDevice *device, |
|
244 nsCString &group, |
|
245 nsCString &clientID) |
|
246 : mDevice(device) |
|
247 , mGroup(group) |
|
248 , mClientID(clientID) |
|
249 { |
|
250 } |
|
251 |
|
252 NS_IMETHOD Run() |
|
253 { |
|
254 if (mDevice->IsActiveCache(mGroup, mClientID)) |
|
255 { |
|
256 mDevice->DeactivateGroup(mGroup); |
|
257 } |
|
258 |
|
259 return mDevice->EvictEntries(mClientID.get()); |
|
260 } |
|
261 |
|
262 private: |
|
263 nsRefPtr<nsOfflineCacheDevice> mDevice; |
|
264 nsCString mGroup; |
|
265 nsCString mClientID; |
|
266 }; |
|
267 |
|
268 /****************************************************************************** |
|
269 * nsOfflineCacheDeviceInfo |
|
270 */ |
|
271 |
|
272 class nsOfflineCacheDeviceInfo MOZ_FINAL : public nsICacheDeviceInfo |
|
273 { |
|
274 public: |
|
275 NS_DECL_ISUPPORTS |
|
276 NS_DECL_NSICACHEDEVICEINFO |
|
277 |
|
278 nsOfflineCacheDeviceInfo(nsOfflineCacheDevice* device) |
|
279 : mDevice(device) |
|
280 {} |
|
281 |
|
282 private: |
|
283 nsOfflineCacheDevice* mDevice; |
|
284 }; |
|
285 |
|
286 NS_IMPL_ISUPPORTS(nsOfflineCacheDeviceInfo, nsICacheDeviceInfo) |
|
287 |
|
288 NS_IMETHODIMP |
|
289 nsOfflineCacheDeviceInfo::GetDescription(char **aDescription) |
|
290 { |
|
291 *aDescription = NS_strdup("Offline cache device"); |
|
292 return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY; |
|
293 } |
|
294 |
|
295 NS_IMETHODIMP |
|
296 nsOfflineCacheDeviceInfo::GetUsageReport(char ** usageReport) |
|
297 { |
|
298 nsAutoCString buffer; |
|
299 buffer.AssignLiteral(" <tr>\n" |
|
300 " <th>Cache Directory:</th>\n" |
|
301 " <td>"); |
|
302 nsIFile *cacheDir = mDevice->CacheDirectory(); |
|
303 if (!cacheDir) |
|
304 return NS_OK; |
|
305 |
|
306 nsAutoString path; |
|
307 nsresult rv = cacheDir->GetPath(path); |
|
308 if (NS_SUCCEEDED(rv)) |
|
309 AppendUTF16toUTF8(path, buffer); |
|
310 else |
|
311 buffer.AppendLiteral("directory unavailable"); |
|
312 |
|
313 buffer.AppendLiteral("</td>\n" |
|
314 " </tr>\n"); |
|
315 |
|
316 *usageReport = ToNewCString(buffer); |
|
317 if (!*usageReport) |
|
318 return NS_ERROR_OUT_OF_MEMORY; |
|
319 |
|
320 return NS_OK; |
|
321 } |
|
322 |
|
323 NS_IMETHODIMP |
|
324 nsOfflineCacheDeviceInfo::GetEntryCount(uint32_t *aEntryCount) |
|
325 { |
|
326 *aEntryCount = mDevice->EntryCount(); |
|
327 return NS_OK; |
|
328 } |
|
329 |
|
330 NS_IMETHODIMP |
|
331 nsOfflineCacheDeviceInfo::GetTotalSize(uint32_t *aTotalSize) |
|
332 { |
|
333 *aTotalSize = mDevice->CacheSize(); |
|
334 return NS_OK; |
|
335 } |
|
336 |
|
337 NS_IMETHODIMP |
|
338 nsOfflineCacheDeviceInfo::GetMaximumSize(uint32_t *aMaximumSize) |
|
339 { |
|
340 *aMaximumSize = mDevice->CacheCapacity(); |
|
341 return NS_OK; |
|
342 } |
|
343 |
|
344 /****************************************************************************** |
|
345 * nsOfflineCacheBinding |
|
346 */ |
|
347 |
|
348 class nsOfflineCacheBinding MOZ_FINAL : public nsISupports |
|
349 { |
|
350 public: |
|
351 NS_DECL_THREADSAFE_ISUPPORTS |
|
352 |
|
353 static nsOfflineCacheBinding * |
|
354 Create(nsIFile *cacheDir, const nsCString *key, int generation); |
|
355 |
|
356 enum { FLAG_NEW_ENTRY = 1 }; |
|
357 |
|
358 nsCOMPtr<nsIFile> mDataFile; |
|
359 int mGeneration; |
|
360 int mFlags; |
|
361 |
|
362 bool IsNewEntry() { return mFlags & FLAG_NEW_ENTRY; } |
|
363 void MarkNewEntry() { mFlags |= FLAG_NEW_ENTRY; } |
|
364 void ClearNewEntry() { mFlags &= ~FLAG_NEW_ENTRY; } |
|
365 }; |
|
366 |
|
367 NS_IMPL_ISUPPORTS0(nsOfflineCacheBinding) |
|
368 |
|
369 nsOfflineCacheBinding * |
|
370 nsOfflineCacheBinding::Create(nsIFile *cacheDir, |
|
371 const nsCString *fullKey, |
|
372 int generation) |
|
373 { |
|
374 nsCOMPtr<nsIFile> file; |
|
375 cacheDir->Clone(getter_AddRefs(file)); |
|
376 if (!file) |
|
377 return nullptr; |
|
378 |
|
379 nsAutoCString keyBuf; |
|
380 const char *cid, *key; |
|
381 if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf)) |
|
382 return nullptr; |
|
383 |
|
384 uint64_t hash = DCacheHash(key); |
|
385 |
|
386 uint32_t dir1 = (uint32_t) (hash & 0x0F); |
|
387 uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4); |
|
388 |
|
389 hash >>= 8; |
|
390 |
|
391 // XXX we might want to create these directories up-front |
|
392 |
|
393 file->AppendNative(nsPrintfCString("%X", dir1)); |
|
394 file->Create(nsIFile::DIRECTORY_TYPE, 00700); |
|
395 |
|
396 file->AppendNative(nsPrintfCString("%X", dir2)); |
|
397 file->Create(nsIFile::DIRECTORY_TYPE, 00700); |
|
398 |
|
399 nsresult rv; |
|
400 char leaf[64]; |
|
401 |
|
402 if (generation == -1) |
|
403 { |
|
404 file->AppendNative(NS_LITERAL_CSTRING("placeholder")); |
|
405 |
|
406 for (generation = 0; ; ++generation) |
|
407 { |
|
408 PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation); |
|
409 |
|
410 rv = file->SetNativeLeafName(nsDependentCString(leaf)); |
|
411 if (NS_FAILED(rv)) |
|
412 return nullptr; |
|
413 rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 00600); |
|
414 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) |
|
415 return nullptr; |
|
416 if (NS_SUCCEEDED(rv)) |
|
417 break; |
|
418 } |
|
419 } |
|
420 else |
|
421 { |
|
422 PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation); |
|
423 rv = file->AppendNative(nsDependentCString(leaf)); |
|
424 if (NS_FAILED(rv)) |
|
425 return nullptr; |
|
426 } |
|
427 |
|
428 nsOfflineCacheBinding *binding = new nsOfflineCacheBinding; |
|
429 if (!binding) |
|
430 return nullptr; |
|
431 |
|
432 binding->mDataFile.swap(file); |
|
433 binding->mGeneration = generation; |
|
434 binding->mFlags = 0; |
|
435 return binding; |
|
436 } |
|
437 |
|
438 /****************************************************************************** |
|
439 * nsOfflineCacheRecord |
|
440 */ |
|
441 |
|
442 struct nsOfflineCacheRecord |
|
443 { |
|
444 const char *clientID; |
|
445 const char *key; |
|
446 const uint8_t *metaData; |
|
447 uint32_t metaDataLen; |
|
448 int32_t generation; |
|
449 int32_t dataSize; |
|
450 int32_t fetchCount; |
|
451 int64_t lastFetched; |
|
452 int64_t lastModified; |
|
453 int64_t expirationTime; |
|
454 }; |
|
455 |
|
456 static nsCacheEntry * |
|
457 CreateCacheEntry(nsOfflineCacheDevice *device, |
|
458 const nsCString *fullKey, |
|
459 const nsOfflineCacheRecord &rec) |
|
460 { |
|
461 nsCacheEntry *entry; |
|
462 |
|
463 if (device->IsLocked(*fullKey)) { |
|
464 return nullptr; |
|
465 } |
|
466 |
|
467 nsresult rv = nsCacheEntry::Create(fullKey->get(), // XXX enable sharing |
|
468 nsICache::STREAM_BASED, |
|
469 nsICache::STORE_OFFLINE, |
|
470 device, &entry); |
|
471 if (NS_FAILED(rv)) |
|
472 return nullptr; |
|
473 |
|
474 entry->SetFetchCount((uint32_t) rec.fetchCount); |
|
475 entry->SetLastFetched(SecondsFromPRTime(rec.lastFetched)); |
|
476 entry->SetLastModified(SecondsFromPRTime(rec.lastModified)); |
|
477 entry->SetExpirationTime(SecondsFromPRTime(rec.expirationTime)); |
|
478 entry->SetDataSize((uint32_t) rec.dataSize); |
|
479 |
|
480 entry->UnflattenMetaData((const char *) rec.metaData, rec.metaDataLen); |
|
481 |
|
482 // Restore security info, if present |
|
483 const char* info = entry->GetMetaDataElement("security-info"); |
|
484 if (info) { |
|
485 nsCOMPtr<nsISupports> infoObj; |
|
486 rv = NS_DeserializeObject(nsDependentCString(info), |
|
487 getter_AddRefs(infoObj)); |
|
488 if (NS_FAILED(rv)) { |
|
489 delete entry; |
|
490 return nullptr; |
|
491 } |
|
492 entry->SetSecurityInfo(infoObj); |
|
493 } |
|
494 |
|
495 // create a binding object for this entry |
|
496 nsOfflineCacheBinding *binding = |
|
497 nsOfflineCacheBinding::Create(device->CacheDirectory(), |
|
498 fullKey, |
|
499 rec.generation); |
|
500 if (!binding) |
|
501 { |
|
502 delete entry; |
|
503 return nullptr; |
|
504 } |
|
505 entry->SetData(binding); |
|
506 |
|
507 return entry; |
|
508 } |
|
509 |
|
510 |
|
511 /****************************************************************************** |
|
512 * nsOfflineCacheEntryInfo |
|
513 */ |
|
514 |
|
515 class nsOfflineCacheEntryInfo MOZ_FINAL : public nsICacheEntryInfo |
|
516 { |
|
517 public: |
|
518 NS_DECL_ISUPPORTS |
|
519 NS_DECL_NSICACHEENTRYINFO |
|
520 |
|
521 nsOfflineCacheRecord *mRec; |
|
522 }; |
|
523 |
|
524 NS_IMPL_ISUPPORTS(nsOfflineCacheEntryInfo, nsICacheEntryInfo) |
|
525 |
|
526 NS_IMETHODIMP |
|
527 nsOfflineCacheEntryInfo::GetClientID(char **result) |
|
528 { |
|
529 *result = NS_strdup(mRec->clientID); |
|
530 return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY; |
|
531 } |
|
532 |
|
533 NS_IMETHODIMP |
|
534 nsOfflineCacheEntryInfo::GetDeviceID(char ** deviceID) |
|
535 { |
|
536 *deviceID = NS_strdup(OFFLINE_CACHE_DEVICE_ID); |
|
537 return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY; |
|
538 } |
|
539 |
|
540 NS_IMETHODIMP |
|
541 nsOfflineCacheEntryInfo::GetKey(nsACString &clientKey) |
|
542 { |
|
543 clientKey.Assign(mRec->key); |
|
544 return NS_OK; |
|
545 } |
|
546 |
|
547 NS_IMETHODIMP |
|
548 nsOfflineCacheEntryInfo::GetFetchCount(int32_t *aFetchCount) |
|
549 { |
|
550 *aFetchCount = mRec->fetchCount; |
|
551 return NS_OK; |
|
552 } |
|
553 |
|
554 NS_IMETHODIMP |
|
555 nsOfflineCacheEntryInfo::GetLastFetched(uint32_t *aLastFetched) |
|
556 { |
|
557 *aLastFetched = SecondsFromPRTime(mRec->lastFetched); |
|
558 return NS_OK; |
|
559 } |
|
560 |
|
561 NS_IMETHODIMP |
|
562 nsOfflineCacheEntryInfo::GetLastModified(uint32_t *aLastModified) |
|
563 { |
|
564 *aLastModified = SecondsFromPRTime(mRec->lastModified); |
|
565 return NS_OK; |
|
566 } |
|
567 |
|
568 NS_IMETHODIMP |
|
569 nsOfflineCacheEntryInfo::GetExpirationTime(uint32_t *aExpirationTime) |
|
570 { |
|
571 *aExpirationTime = SecondsFromPRTime(mRec->expirationTime); |
|
572 return NS_OK; |
|
573 } |
|
574 |
|
575 NS_IMETHODIMP |
|
576 nsOfflineCacheEntryInfo::IsStreamBased(bool *aStreamBased) |
|
577 { |
|
578 *aStreamBased = true; |
|
579 return NS_OK; |
|
580 } |
|
581 |
|
582 NS_IMETHODIMP |
|
583 nsOfflineCacheEntryInfo::GetDataSize(uint32_t *aDataSize) |
|
584 { |
|
585 *aDataSize = mRec->dataSize; |
|
586 return NS_OK; |
|
587 } |
|
588 |
|
589 |
|
590 /****************************************************************************** |
|
591 * nsApplicationCacheNamespace |
|
592 */ |
|
593 |
|
594 NS_IMPL_ISUPPORTS(nsApplicationCacheNamespace, nsIApplicationCacheNamespace) |
|
595 |
|
596 NS_IMETHODIMP |
|
597 nsApplicationCacheNamespace::Init(uint32_t itemType, |
|
598 const nsACString &namespaceSpec, |
|
599 const nsACString &data) |
|
600 { |
|
601 mItemType = itemType; |
|
602 mNamespaceSpec = namespaceSpec; |
|
603 mData = data; |
|
604 return NS_OK; |
|
605 } |
|
606 |
|
607 NS_IMETHODIMP |
|
608 nsApplicationCacheNamespace::GetItemType(uint32_t *out) |
|
609 { |
|
610 *out = mItemType; |
|
611 return NS_OK; |
|
612 } |
|
613 |
|
614 NS_IMETHODIMP |
|
615 nsApplicationCacheNamespace::GetNamespaceSpec(nsACString &out) |
|
616 { |
|
617 out = mNamespaceSpec; |
|
618 return NS_OK; |
|
619 } |
|
620 |
|
621 NS_IMETHODIMP |
|
622 nsApplicationCacheNamespace::GetData(nsACString &out) |
|
623 { |
|
624 out = mData; |
|
625 return NS_OK; |
|
626 } |
|
627 |
|
628 /****************************************************************************** |
|
629 * nsApplicationCache |
|
630 */ |
|
631 |
|
632 NS_IMPL_ISUPPORTS(nsApplicationCache, |
|
633 nsIApplicationCache, |
|
634 nsISupportsWeakReference) |
|
635 |
|
636 nsApplicationCache::nsApplicationCache() |
|
637 : mDevice(nullptr) |
|
638 , mValid(true) |
|
639 { |
|
640 } |
|
641 |
|
642 nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device, |
|
643 const nsACString &group, |
|
644 const nsACString &clientID) |
|
645 : mDevice(device) |
|
646 , mGroup(group) |
|
647 , mClientID(clientID) |
|
648 , mValid(true) |
|
649 { |
|
650 } |
|
651 |
|
652 nsApplicationCache::~nsApplicationCache() |
|
653 { |
|
654 if (!mDevice) |
|
655 return; |
|
656 |
|
657 { |
|
658 MutexAutoLock lock(mDevice->mLock); |
|
659 mDevice->mCaches.Remove(mClientID); |
|
660 } |
|
661 |
|
662 // If this isn't an active cache anymore, it can be destroyed. |
|
663 if (mValid && !mDevice->IsActiveCache(mGroup, mClientID)) |
|
664 Discard(); |
|
665 } |
|
666 |
|
667 void |
|
668 nsApplicationCache::MarkInvalid() |
|
669 { |
|
670 mValid = false; |
|
671 } |
|
672 |
|
673 NS_IMETHODIMP |
|
674 nsApplicationCache::InitAsHandle(const nsACString &groupId, |
|
675 const nsACString &clientId) |
|
676 { |
|
677 NS_ENSURE_FALSE(mDevice, NS_ERROR_ALREADY_INITIALIZED); |
|
678 NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED); |
|
679 |
|
680 mGroup = groupId; |
|
681 mClientID = clientId; |
|
682 return NS_OK; |
|
683 } |
|
684 |
|
685 NS_IMETHODIMP |
|
686 nsApplicationCache::GetManifestURI(nsIURI **out) |
|
687 { |
|
688 nsCOMPtr<nsIURI> uri; |
|
689 nsresult rv = NS_NewURI(getter_AddRefs(uri), mGroup); |
|
690 NS_ENSURE_SUCCESS(rv, rv); |
|
691 |
|
692 rv = uri->CloneIgnoringRef(out); |
|
693 NS_ENSURE_SUCCESS(rv, rv); |
|
694 |
|
695 return NS_OK; |
|
696 } |
|
697 |
|
698 NS_IMETHODIMP |
|
699 nsApplicationCache::GetGroupID(nsACString &out) |
|
700 { |
|
701 out = mGroup; |
|
702 return NS_OK; |
|
703 } |
|
704 |
|
705 NS_IMETHODIMP |
|
706 nsApplicationCache::GetClientID(nsACString &out) |
|
707 { |
|
708 out = mClientID; |
|
709 return NS_OK; |
|
710 } |
|
711 |
|
712 NS_IMETHODIMP |
|
713 nsApplicationCache::GetProfileDirectory(nsIFile **out) |
|
714 { |
|
715 if (mDevice->BaseDirectory()) |
|
716 NS_ADDREF(*out = mDevice->BaseDirectory()); |
|
717 else |
|
718 *out = nullptr; |
|
719 |
|
720 return NS_OK; |
|
721 } |
|
722 |
|
723 NS_IMETHODIMP |
|
724 nsApplicationCache::GetActive(bool *out) |
|
725 { |
|
726 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
727 |
|
728 *out = mDevice->IsActiveCache(mGroup, mClientID); |
|
729 return NS_OK; |
|
730 } |
|
731 |
|
732 NS_IMETHODIMP |
|
733 nsApplicationCache::Activate() |
|
734 { |
|
735 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
736 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
737 |
|
738 mDevice->ActivateCache(mGroup, mClientID); |
|
739 |
|
740 if (mDevice->AutoShutdown(this)) |
|
741 mDevice = nullptr; |
|
742 |
|
743 return NS_OK; |
|
744 } |
|
745 |
|
746 NS_IMETHODIMP |
|
747 nsApplicationCache::Discard() |
|
748 { |
|
749 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
750 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
751 |
|
752 mValid = false; |
|
753 |
|
754 nsRefPtr<nsIRunnable> ev = |
|
755 new nsOfflineCacheDiscardCache(mDevice, mGroup, mClientID); |
|
756 nsresult rv = nsCacheService::DispatchToCacheIOThread(ev); |
|
757 return rv; |
|
758 } |
|
759 |
|
760 NS_IMETHODIMP |
|
761 nsApplicationCache::MarkEntry(const nsACString &key, |
|
762 uint32_t typeBits) |
|
763 { |
|
764 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
765 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
766 |
|
767 return mDevice->MarkEntry(mClientID, key, typeBits); |
|
768 } |
|
769 |
|
770 |
|
771 NS_IMETHODIMP |
|
772 nsApplicationCache::UnmarkEntry(const nsACString &key, |
|
773 uint32_t typeBits) |
|
774 { |
|
775 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
776 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
777 |
|
778 return mDevice->UnmarkEntry(mClientID, key, typeBits); |
|
779 } |
|
780 |
|
781 NS_IMETHODIMP |
|
782 nsApplicationCache::GetTypes(const nsACString &key, |
|
783 uint32_t *typeBits) |
|
784 { |
|
785 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
786 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
787 |
|
788 return mDevice->GetTypes(mClientID, key, typeBits); |
|
789 } |
|
790 |
|
791 NS_IMETHODIMP |
|
792 nsApplicationCache::GatherEntries(uint32_t typeBits, |
|
793 uint32_t * count, |
|
794 char *** keys) |
|
795 { |
|
796 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
797 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
798 |
|
799 return mDevice->GatherEntries(mClientID, typeBits, count, keys); |
|
800 } |
|
801 |
|
802 NS_IMETHODIMP |
|
803 nsApplicationCache::AddNamespaces(nsIArray *namespaces) |
|
804 { |
|
805 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
806 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
807 |
|
808 if (!namespaces) |
|
809 return NS_OK; |
|
810 |
|
811 mozStorageTransaction transaction(mDevice->mDB, false); |
|
812 |
|
813 uint32_t length; |
|
814 nsresult rv = namespaces->GetLength(&length); |
|
815 NS_ENSURE_SUCCESS(rv, rv); |
|
816 |
|
817 for (uint32_t i = 0; i < length; i++) { |
|
818 nsCOMPtr<nsIApplicationCacheNamespace> ns = |
|
819 do_QueryElementAt(namespaces, i); |
|
820 if (ns) { |
|
821 rv = mDevice->AddNamespace(mClientID, ns); |
|
822 NS_ENSURE_SUCCESS(rv, rv); |
|
823 } |
|
824 } |
|
825 |
|
826 rv = transaction.Commit(); |
|
827 NS_ENSURE_SUCCESS(rv, rv); |
|
828 |
|
829 return NS_OK; |
|
830 } |
|
831 |
|
832 NS_IMETHODIMP |
|
833 nsApplicationCache::GetMatchingNamespace(const nsACString &key, |
|
834 nsIApplicationCacheNamespace **out) |
|
835 |
|
836 { |
|
837 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
838 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
839 |
|
840 return mDevice->GetMatchingNamespace(mClientID, key, out); |
|
841 } |
|
842 |
|
843 NS_IMETHODIMP |
|
844 nsApplicationCache::GetUsage(uint32_t *usage) |
|
845 { |
|
846 NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); |
|
847 NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); |
|
848 |
|
849 return mDevice->GetUsage(mClientID, usage); |
|
850 } |
|
851 |
|
852 /****************************************************************************** |
|
853 * nsCloseDBEvent |
|
854 *****************************************************************************/ |
|
855 |
|
856 class nsCloseDBEvent : public nsRunnable { |
|
857 public: |
|
858 nsCloseDBEvent(mozIStorageConnection *aDB) |
|
859 { |
|
860 mDB = aDB; |
|
861 } |
|
862 |
|
863 NS_IMETHOD Run() |
|
864 { |
|
865 mDB->Close(); |
|
866 return NS_OK; |
|
867 } |
|
868 |
|
869 protected: |
|
870 virtual ~nsCloseDBEvent() {} |
|
871 |
|
872 private: |
|
873 nsCOMPtr<mozIStorageConnection> mDB; |
|
874 }; |
|
875 |
|
876 |
|
877 |
|
878 /****************************************************************************** |
|
879 * nsOfflineCacheDevice |
|
880 */ |
|
881 |
|
882 NS_IMPL_ISUPPORTS0(nsOfflineCacheDevice) |
|
883 |
|
884 nsOfflineCacheDevice::nsOfflineCacheDevice() |
|
885 : mDB(nullptr) |
|
886 , mCacheCapacity(0) |
|
887 , mDeltaCounter(0) |
|
888 , mAutoShutdown(false) |
|
889 , mLock("nsOfflineCacheDevice.lock") |
|
890 , mActiveCaches(5) |
|
891 , mLockedEntries(64) |
|
892 { |
|
893 } |
|
894 |
|
895 /* static */ |
|
896 bool |
|
897 nsOfflineCacheDevice::GetStrictFileOriginPolicy() |
|
898 { |
|
899 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
900 |
|
901 bool retval; |
|
902 if (prefs && NS_SUCCEEDED(prefs->GetBoolPref("security.fileuri.strict_origin_policy", &retval))) |
|
903 return retval; |
|
904 |
|
905 // As default value use true (be more strict) |
|
906 return true; |
|
907 } |
|
908 |
|
909 uint32_t |
|
910 nsOfflineCacheDevice::CacheSize() |
|
911 { |
|
912 AutoResetStatement statement(mStatement_CacheSize); |
|
913 |
|
914 bool hasRows; |
|
915 nsresult rv = statement->ExecuteStep(&hasRows); |
|
916 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0); |
|
917 |
|
918 return (uint32_t) statement->AsInt32(0); |
|
919 } |
|
920 |
|
921 uint32_t |
|
922 nsOfflineCacheDevice::EntryCount() |
|
923 { |
|
924 AutoResetStatement statement(mStatement_EntryCount); |
|
925 |
|
926 bool hasRows; |
|
927 nsresult rv = statement->ExecuteStep(&hasRows); |
|
928 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0); |
|
929 |
|
930 return (uint32_t) statement->AsInt32(0); |
|
931 } |
|
932 |
|
933 nsresult |
|
934 nsOfflineCacheDevice::UpdateEntry(nsCacheEntry *entry) |
|
935 { |
|
936 // Decompose the key into "ClientID" and "Key" |
|
937 nsAutoCString keyBuf; |
|
938 const char *cid, *key; |
|
939 |
|
940 if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf)) |
|
941 return NS_ERROR_UNEXPECTED; |
|
942 |
|
943 // Store security info, if it is serializable |
|
944 nsCOMPtr<nsISupports> infoObj = entry->SecurityInfo(); |
|
945 nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj); |
|
946 if (infoObj && !serializable) |
|
947 return NS_ERROR_UNEXPECTED; |
|
948 |
|
949 if (serializable) { |
|
950 nsCString info; |
|
951 nsresult rv = NS_SerializeToString(serializable, info); |
|
952 NS_ENSURE_SUCCESS(rv, rv); |
|
953 |
|
954 rv = entry->SetMetaDataElement("security-info", info.get()); |
|
955 NS_ENSURE_SUCCESS(rv, rv); |
|
956 } |
|
957 |
|
958 nsCString metaDataBuf; |
|
959 uint32_t mdSize = entry->MetaDataSize(); |
|
960 if (!metaDataBuf.SetLength(mdSize, fallible_t())) |
|
961 return NS_ERROR_OUT_OF_MEMORY; |
|
962 char *md = metaDataBuf.BeginWriting(); |
|
963 entry->FlattenMetaData(md, mdSize); |
|
964 |
|
965 nsOfflineCacheRecord rec; |
|
966 rec.metaData = (const uint8_t *) md; |
|
967 rec.metaDataLen = mdSize; |
|
968 rec.dataSize = entry->DataSize(); |
|
969 rec.fetchCount = entry->FetchCount(); |
|
970 rec.lastFetched = PRTimeFromSeconds(entry->LastFetched()); |
|
971 rec.lastModified = PRTimeFromSeconds(entry->LastModified()); |
|
972 rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime()); |
|
973 |
|
974 AutoResetStatement statement(mStatement_UpdateEntry); |
|
975 |
|
976 nsresult rv; |
|
977 rv = statement->BindBlobByIndex(0, rec.metaData, rec.metaDataLen); |
|
978 nsresult tmp = statement->BindInt32ByIndex(1, rec.dataSize); |
|
979 if (NS_FAILED(tmp)) { |
|
980 rv = tmp; |
|
981 } |
|
982 tmp = statement->BindInt32ByIndex(2, rec.fetchCount); |
|
983 if (NS_FAILED(tmp)) { |
|
984 rv = tmp; |
|
985 } |
|
986 tmp = statement->BindInt64ByIndex(3, rec.lastFetched); |
|
987 if (NS_FAILED(tmp)) { |
|
988 rv = tmp; |
|
989 } |
|
990 tmp = statement->BindInt64ByIndex(4, rec.lastModified); |
|
991 if (NS_FAILED(tmp)) { |
|
992 rv = tmp; |
|
993 } |
|
994 tmp = statement->BindInt64ByIndex(5, rec.expirationTime); |
|
995 if (NS_FAILED(tmp)) { |
|
996 rv = tmp; |
|
997 } |
|
998 tmp = statement->BindUTF8StringByIndex(6, nsDependentCString(cid)); |
|
999 if (NS_FAILED(tmp)) { |
|
1000 rv = tmp; |
|
1001 } |
|
1002 tmp = statement->BindUTF8StringByIndex(7, nsDependentCString(key)); |
|
1003 if (NS_FAILED(tmp)) { |
|
1004 rv = tmp; |
|
1005 } |
|
1006 NS_ENSURE_SUCCESS(rv, rv); |
|
1007 |
|
1008 bool hasRows; |
|
1009 rv = statement->ExecuteStep(&hasRows); |
|
1010 NS_ENSURE_SUCCESS(rv, rv); |
|
1011 |
|
1012 NS_ASSERTION(!hasRows, "UPDATE should not result in output"); |
|
1013 return rv; |
|
1014 } |
|
1015 |
|
1016 nsresult |
|
1017 nsOfflineCacheDevice::UpdateEntrySize(nsCacheEntry *entry, uint32_t newSize) |
|
1018 { |
|
1019 // Decompose the key into "ClientID" and "Key" |
|
1020 nsAutoCString keyBuf; |
|
1021 const char *cid, *key; |
|
1022 if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf)) |
|
1023 return NS_ERROR_UNEXPECTED; |
|
1024 |
|
1025 AutoResetStatement statement(mStatement_UpdateEntrySize); |
|
1026 |
|
1027 nsresult rv = statement->BindInt32ByIndex(0, newSize); |
|
1028 nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(cid)); |
|
1029 if (NS_FAILED(tmp)) { |
|
1030 rv = tmp; |
|
1031 } |
|
1032 tmp = statement->BindUTF8StringByIndex(2, nsDependentCString(key)); |
|
1033 if (NS_FAILED(tmp)) { |
|
1034 rv = tmp; |
|
1035 } |
|
1036 NS_ENSURE_SUCCESS(rv, rv); |
|
1037 |
|
1038 bool hasRows; |
|
1039 rv = statement->ExecuteStep(&hasRows); |
|
1040 NS_ENSURE_SUCCESS(rv, rv); |
|
1041 |
|
1042 NS_ASSERTION(!hasRows, "UPDATE should not result in output"); |
|
1043 return rv; |
|
1044 } |
|
1045 |
|
1046 nsresult |
|
1047 nsOfflineCacheDevice::DeleteEntry(nsCacheEntry *entry, bool deleteData) |
|
1048 { |
|
1049 if (deleteData) |
|
1050 { |
|
1051 nsresult rv = DeleteData(entry); |
|
1052 if (NS_FAILED(rv)) |
|
1053 return rv; |
|
1054 } |
|
1055 |
|
1056 // Decompose the key into "ClientID" and "Key" |
|
1057 nsAutoCString keyBuf; |
|
1058 const char *cid, *key; |
|
1059 if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf)) |
|
1060 return NS_ERROR_UNEXPECTED; |
|
1061 |
|
1062 AutoResetStatement statement(mStatement_DeleteEntry); |
|
1063 |
|
1064 nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid)); |
|
1065 nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key)); |
|
1066 NS_ENSURE_SUCCESS(rv, rv); |
|
1067 NS_ENSURE_SUCCESS(rv2, rv2); |
|
1068 |
|
1069 bool hasRows; |
|
1070 rv = statement->ExecuteStep(&hasRows); |
|
1071 NS_ENSURE_SUCCESS(rv, rv); |
|
1072 |
|
1073 NS_ASSERTION(!hasRows, "DELETE should not result in output"); |
|
1074 return rv; |
|
1075 } |
|
1076 |
|
1077 nsresult |
|
1078 nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry) |
|
1079 { |
|
1080 nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data(); |
|
1081 NS_ENSURE_STATE(binding); |
|
1082 |
|
1083 return binding->mDataFile->Remove(false); |
|
1084 } |
|
1085 |
|
1086 /** |
|
1087 * nsCacheDevice implementation |
|
1088 */ |
|
1089 |
|
1090 // This struct is local to nsOfflineCacheDevice::Init, but ISO C++98 doesn't |
|
1091 // allow a template (mozilla::ArrayLength) to be instantiated based on a local |
|
1092 // type. Boo-urns! |
|
1093 struct StatementSql { |
|
1094 nsCOMPtr<mozIStorageStatement> &statement; |
|
1095 const char *sql; |
|
1096 StatementSql (nsCOMPtr<mozIStorageStatement> &aStatement, const char *aSql): |
|
1097 statement (aStatement), sql (aSql) {} |
|
1098 }; |
|
1099 |
|
1100 nsresult |
|
1101 nsOfflineCacheDevice::Init() |
|
1102 { |
|
1103 MOZ_ASSERT(false, "Need to be initialized with sqlite"); |
|
1104 return NS_ERROR_NOT_IMPLEMENTED; |
|
1105 } |
|
1106 |
|
1107 nsresult |
|
1108 nsOfflineCacheDevice::InitWithSqlite(mozIStorageService * ss) |
|
1109 { |
|
1110 NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED); |
|
1111 |
|
1112 // SetCacheParentDirectory must have been called |
|
1113 NS_ENSURE_TRUE(mCacheDirectory, NS_ERROR_UNEXPECTED); |
|
1114 |
|
1115 // make sure the cache directory exists |
|
1116 nsresult rv = EnsureDir(mCacheDirectory); |
|
1117 NS_ENSURE_SUCCESS(rv, rv); |
|
1118 |
|
1119 // build path to index file |
|
1120 nsCOMPtr<nsIFile> indexFile; |
|
1121 rv = mCacheDirectory->Clone(getter_AddRefs(indexFile)); |
|
1122 NS_ENSURE_SUCCESS(rv, rv); |
|
1123 rv = indexFile->AppendNative(NS_LITERAL_CSTRING("index.sqlite")); |
|
1124 NS_ENSURE_SUCCESS(rv, rv); |
|
1125 |
|
1126 MOZ_ASSERT(ss, "nsOfflineCacheDevice::InitWithSqlite called before nsCacheService::Init() ?"); |
|
1127 NS_ENSURE_TRUE(ss, NS_ERROR_UNEXPECTED); |
|
1128 |
|
1129 rv = ss->OpenDatabase(indexFile, getter_AddRefs(mDB)); |
|
1130 NS_ENSURE_SUCCESS(rv, rv); |
|
1131 |
|
1132 mInitThread = do_GetCurrentThread(); |
|
1133 |
|
1134 mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;")); |
|
1135 |
|
1136 // XXX ... other initialization steps |
|
1137 |
|
1138 // XXX in the future we may wish to verify the schema for moz_cache |
|
1139 // perhaps using "PRAGMA table_info" ? |
|
1140 |
|
1141 // build the table |
|
1142 // |
|
1143 // "Generation" is the data file generation number. |
|
1144 // |
|
1145 rv = mDB->ExecuteSimpleSQL( |
|
1146 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n" |
|
1147 " ClientID TEXT,\n" |
|
1148 " Key TEXT,\n" |
|
1149 " MetaData BLOB,\n" |
|
1150 " Generation INTEGER,\n" |
|
1151 " DataSize INTEGER,\n" |
|
1152 " FetchCount INTEGER,\n" |
|
1153 " LastFetched INTEGER,\n" |
|
1154 " LastModified INTEGER,\n" |
|
1155 " ExpirationTime INTEGER,\n" |
|
1156 " ItemType INTEGER DEFAULT 0\n" |
|
1157 ");\n")); |
|
1158 NS_ENSURE_SUCCESS(rv, rv); |
|
1159 |
|
1160 // Databases from 1.9.0 don't have the ItemType column. Add the column |
|
1161 // here, but don't worry about failures (the column probably already exists) |
|
1162 mDB->ExecuteSimpleSQL( |
|
1163 NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0")); |
|
1164 |
|
1165 // Create the table for storing cache groups. All actions on |
|
1166 // moz_cache_groups use the GroupID, so use it as the primary key. |
|
1167 rv = mDB->ExecuteSimpleSQL( |
|
1168 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n" |
|
1169 " GroupID TEXT PRIMARY KEY,\n" |
|
1170 " ActiveClientID TEXT\n" |
|
1171 ");\n")); |
|
1172 NS_ENSURE_SUCCESS(rv, rv); |
|
1173 |
|
1174 mDB->ExecuteSimpleSQL( |
|
1175 NS_LITERAL_CSTRING("ALTER TABLE moz_cache_groups " |
|
1176 "ADD ActivateTimeStamp INTEGER DEFAULT 0")); |
|
1177 |
|
1178 // ClientID: clientID joining moz_cache and moz_cache_namespaces |
|
1179 // tables. |
|
1180 // Data: Data associated with this namespace (e.g. a fallback URI |
|
1181 // for fallback entries). |
|
1182 // ItemType: the type of namespace. |
|
1183 rv = mDB->ExecuteSimpleSQL( |
|
1184 NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS" |
|
1185 " moz_cache_namespaces (\n" |
|
1186 " ClientID TEXT,\n" |
|
1187 " NameSpace TEXT,\n" |
|
1188 " Data TEXT,\n" |
|
1189 " ItemType INTEGER\n" |
|
1190 ");\n")); |
|
1191 NS_ENSURE_SUCCESS(rv, rv); |
|
1192 |
|
1193 // Databases from 1.9.0 have a moz_cache_index that should be dropped |
|
1194 rv = mDB->ExecuteSimpleSQL( |
|
1195 NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index")); |
|
1196 NS_ENSURE_SUCCESS(rv, rv); |
|
1197 |
|
1198 // Key/ClientID pairs should be unique in the database. All queries |
|
1199 // against moz_cache use the Key (which is also the most unique), so |
|
1200 // use it as the primary key for this index. |
|
1201 rv = mDB->ExecuteSimpleSQL( |
|
1202 NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS " |
|
1203 " moz_cache_key_clientid_index" |
|
1204 " ON moz_cache (Key, ClientID);")); |
|
1205 NS_ENSURE_SUCCESS(rv, rv); |
|
1206 |
|
1207 // Used for ClientID lookups and to keep ClientID/NameSpace pairs unique. |
|
1208 rv = mDB->ExecuteSimpleSQL( |
|
1209 NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS" |
|
1210 " moz_cache_namespaces_clientid_index" |
|
1211 " ON moz_cache_namespaces (ClientID, NameSpace);")); |
|
1212 NS_ENSURE_SUCCESS(rv, rv); |
|
1213 |
|
1214 // Used for namespace lookups. |
|
1215 rv = mDB->ExecuteSimpleSQL( |
|
1216 NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" |
|
1217 " moz_cache_namespaces_namespace_index" |
|
1218 " ON moz_cache_namespaces (NameSpace);")); |
|
1219 NS_ENSURE_SUCCESS(rv, rv); |
|
1220 |
|
1221 |
|
1222 mEvictionFunction = new nsOfflineCacheEvictionFunction(this); |
|
1223 if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY; |
|
1224 |
|
1225 rv = mDB->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 3, mEvictionFunction); |
|
1226 NS_ENSURE_SUCCESS(rv, rv); |
|
1227 |
|
1228 // create all (most) of our statements up front |
|
1229 StatementSql prepared[] = { |
|
1230 StatementSql ( mStatement_CacheSize, "SELECT Sum(DataSize) from moz_cache;" ), |
|
1231 StatementSql ( mStatement_ApplicationCacheSize, "SELECT Sum(DataSize) from moz_cache WHERE ClientID = ?;" ), |
|
1232 StatementSql ( mStatement_EntryCount, "SELECT count(*) from moz_cache;" ), |
|
1233 StatementSql ( mStatement_UpdateEntry, "UPDATE moz_cache SET MetaData = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" ), |
|
1234 StatementSql ( mStatement_UpdateEntrySize, "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" ), |
|
1235 StatementSql ( mStatement_DeleteEntry, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" ), |
|
1236 StatementSql ( mStatement_FindEntry, "SELECT MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime, ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;" ), |
|
1237 StatementSql ( mStatement_BindEntry, "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?);" ), |
|
1238 |
|
1239 StatementSql ( mStatement_MarkEntry, "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ), |
|
1240 StatementSql ( mStatement_UnmarkEntry, "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ), |
|
1241 StatementSql ( mStatement_GetTypes, "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"), |
|
1242 StatementSql ( mStatement_CleanupUnmarked, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ), |
|
1243 StatementSql ( mStatement_GatherEntries, "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ), |
|
1244 |
|
1245 StatementSql ( mStatement_ActivateClient, "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID, ActivateTimeStamp) VALUES (?, ?, ?);" ), |
|
1246 StatementSql ( mStatement_DeactivateGroup, "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ), |
|
1247 StatementSql ( mStatement_FindClient, "SELECT ClientID, ItemType FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;" ), |
|
1248 |
|
1249 // Search for namespaces that match the URI. Use the <= operator |
|
1250 // to ensure that we use the index on moz_cache_namespaces. |
|
1251 StatementSql ( mStatement_FindClientByNamespace, "SELECT ns.ClientID, ns.ItemType FROM" |
|
1252 " moz_cache_namespaces AS ns JOIN moz_cache_groups AS groups" |
|
1253 " ON ns.ClientID = groups.ActiveClientID" |
|
1254 " WHERE ns.NameSpace <= ?1 AND ?1 GLOB ns.NameSpace || '*'" |
|
1255 " ORDER BY ns.NameSpace DESC, groups.ActivateTimeStamp DESC;"), |
|
1256 StatementSql ( mStatement_FindNamespaceEntry, "SELECT NameSpace, Data, ItemType FROM moz_cache_namespaces" |
|
1257 " WHERE ClientID = ?1" |
|
1258 " AND NameSpace <= ?2 AND ?2 GLOB NameSpace || '*'" |
|
1259 " ORDER BY NameSpace DESC;"), |
|
1260 StatementSql ( mStatement_InsertNamespaceEntry, "INSERT INTO moz_cache_namespaces (ClientID, NameSpace, Data, ItemType) VALUES(?, ?, ?, ?);"), |
|
1261 StatementSql ( mStatement_EnumerateApps, "SELECT GroupID, ActiveClientID FROM moz_cache_groups WHERE GroupID LIKE ?1;"), |
|
1262 StatementSql ( mStatement_EnumerateGroups, "SELECT GroupID, ActiveClientID FROM moz_cache_groups;"), |
|
1263 StatementSql ( mStatement_EnumerateGroupsTimeOrder, "SELECT GroupID, ActiveClientID FROM moz_cache_groups ORDER BY ActivateTimeStamp;") |
|
1264 }; |
|
1265 for (uint32_t i = 0; NS_SUCCEEDED(rv) && i < ArrayLength(prepared); ++i) |
|
1266 { |
|
1267 LOG(("Creating statement: %s\n", prepared[i].sql)); |
|
1268 |
|
1269 rv = mDB->CreateStatement(nsDependentCString(prepared[i].sql), |
|
1270 getter_AddRefs(prepared[i].statement)); |
|
1271 NS_ENSURE_SUCCESS(rv, rv); |
|
1272 } |
|
1273 |
|
1274 rv = InitActiveCaches(); |
|
1275 NS_ENSURE_SUCCESS(rv, rv); |
|
1276 |
|
1277 return NS_OK; |
|
1278 } |
|
1279 |
|
1280 namespace { |
|
1281 |
|
1282 nsresult |
|
1283 GetGroupForCache(const nsCSubstring &clientID, nsCString &group) |
|
1284 { |
|
1285 group.Assign(clientID); |
|
1286 group.Truncate(group.FindChar('|')); |
|
1287 NS_UnescapeURL(group); |
|
1288 |
|
1289 return NS_OK; |
|
1290 } |
|
1291 |
|
1292 nsresult |
|
1293 AppendJARIdentifier(nsACString &_result, int32_t appId, bool isInBrowserElement) |
|
1294 { |
|
1295 _result.Append('#'); |
|
1296 _result.AppendInt(appId); |
|
1297 _result.Append('+'); |
|
1298 _result.Append(isInBrowserElement ? 't' : 'f'); |
|
1299 |
|
1300 return NS_OK; |
|
1301 } |
|
1302 |
|
1303 nsresult |
|
1304 GetJARIdentifier(nsIURI *aURI, |
|
1305 uint32_t appId, bool isInBrowserElement, |
|
1306 nsACString &_result) |
|
1307 { |
|
1308 _result.Truncate(); |
|
1309 |
|
1310 // These lines are here for compatibility only. We must not fill the |
|
1311 // JAR identifier when this is no-app context, otherwise web content |
|
1312 // offline application cache loads would not be satisfied (cache would |
|
1313 // not be found). |
|
1314 if (!isInBrowserElement && appId == NECKO_NO_APP_ID) |
|
1315 return NS_OK; |
|
1316 |
|
1317 // This load context has some special attributes, create a jar identifier |
|
1318 return AppendJARIdentifier(_result, appId, isInBrowserElement); |
|
1319 } |
|
1320 |
|
1321 } // anon namespace |
|
1322 |
|
1323 // static |
|
1324 nsresult |
|
1325 nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL, |
|
1326 uint32_t appId, bool isInBrowserElement, |
|
1327 nsACString &_result) |
|
1328 { |
|
1329 nsCOMPtr<nsIURI> newURI; |
|
1330 nsresult rv = aManifestURL->CloneIgnoringRef(getter_AddRefs(newURI)); |
|
1331 NS_ENSURE_SUCCESS(rv, rv); |
|
1332 |
|
1333 nsAutoCString manifestSpec; |
|
1334 rv = newURI->GetAsciiSpec(manifestSpec); |
|
1335 NS_ENSURE_SUCCESS(rv, rv); |
|
1336 |
|
1337 _result.Assign(manifestSpec); |
|
1338 |
|
1339 nsAutoCString jarid; |
|
1340 rv = GetJARIdentifier(aManifestURL, appId, isInBrowserElement, jarid); |
|
1341 NS_ENSURE_SUCCESS(rv, rv); |
|
1342 |
|
1343 // Include JAR ID, i.e. the extended origin if present. |
|
1344 if (!jarid.IsEmpty()) |
|
1345 _result.Append(jarid); |
|
1346 |
|
1347 return NS_OK; |
|
1348 } |
|
1349 |
|
1350 nsresult |
|
1351 nsOfflineCacheDevice::InitActiveCaches() |
|
1352 { |
|
1353 MutexAutoLock lock(mLock); |
|
1354 |
|
1355 AutoResetStatement statement(mStatement_EnumerateGroups); |
|
1356 |
|
1357 bool hasRows; |
|
1358 nsresult rv = statement->ExecuteStep(&hasRows); |
|
1359 NS_ENSURE_SUCCESS(rv, rv); |
|
1360 |
|
1361 while (hasRows) |
|
1362 { |
|
1363 nsAutoCString group; |
|
1364 statement->GetUTF8String(0, group); |
|
1365 nsCString clientID; |
|
1366 statement->GetUTF8String(1, clientID); |
|
1367 |
|
1368 mActiveCaches.PutEntry(clientID); |
|
1369 mActiveCachesByGroup.Put(group, new nsCString(clientID)); |
|
1370 |
|
1371 rv = statement->ExecuteStep(&hasRows); |
|
1372 NS_ENSURE_SUCCESS(rv, rv); |
|
1373 } |
|
1374 |
|
1375 return NS_OK; |
|
1376 } |
|
1377 |
|
1378 /* static */ |
|
1379 PLDHashOperator |
|
1380 nsOfflineCacheDevice::ShutdownApplicationCache(const nsACString &key, |
|
1381 nsIWeakReference *weakRef, |
|
1382 void *ctx) |
|
1383 { |
|
1384 nsCOMPtr<nsIApplicationCache> obj = do_QueryReferent(weakRef); |
|
1385 if (obj) |
|
1386 { |
|
1387 nsApplicationCache *appCache = static_cast<nsApplicationCache*>(obj.get()); |
|
1388 appCache->MarkInvalid(); |
|
1389 } |
|
1390 |
|
1391 return PL_DHASH_NEXT; |
|
1392 } |
|
1393 |
|
1394 nsresult |
|
1395 nsOfflineCacheDevice::Shutdown() |
|
1396 { |
|
1397 NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED); |
|
1398 |
|
1399 { |
|
1400 MutexAutoLock lock(mLock); |
|
1401 mCaches.EnumerateRead(ShutdownApplicationCache, this); |
|
1402 } |
|
1403 |
|
1404 { |
|
1405 EvictionObserver evictionObserver(mDB, mEvictionFunction); |
|
1406 |
|
1407 // Delete all rows whose clientID is not an active clientID. |
|
1408 nsresult rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
1409 "DELETE FROM moz_cache WHERE rowid IN" |
|
1410 " (SELECT moz_cache.rowid FROM" |
|
1411 " moz_cache LEFT OUTER JOIN moz_cache_groups ON" |
|
1412 " (moz_cache.ClientID = moz_cache_groups.ActiveClientID)" |
|
1413 " WHERE moz_cache_groups.GroupID ISNULL)")); |
|
1414 |
|
1415 if (NS_FAILED(rv)) |
|
1416 NS_WARNING("Failed to clean up unused application caches."); |
|
1417 else |
|
1418 evictionObserver.Apply(); |
|
1419 |
|
1420 // Delete all namespaces whose clientID is not an active clientID. |
|
1421 rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
1422 "DELETE FROM moz_cache_namespaces WHERE rowid IN" |
|
1423 " (SELECT moz_cache_namespaces.rowid FROM" |
|
1424 " moz_cache_namespaces LEFT OUTER JOIN moz_cache_groups ON" |
|
1425 " (moz_cache_namespaces.ClientID = moz_cache_groups.ActiveClientID)" |
|
1426 " WHERE moz_cache_groups.GroupID ISNULL)")); |
|
1427 |
|
1428 if (NS_FAILED(rv)) |
|
1429 NS_WARNING("Failed to clean up namespaces."); |
|
1430 |
|
1431 mEvictionFunction = 0; |
|
1432 |
|
1433 mStatement_CacheSize = nullptr; |
|
1434 mStatement_ApplicationCacheSize = nullptr; |
|
1435 mStatement_EntryCount = nullptr; |
|
1436 mStatement_UpdateEntry = nullptr; |
|
1437 mStatement_UpdateEntrySize = nullptr; |
|
1438 mStatement_DeleteEntry = nullptr; |
|
1439 mStatement_FindEntry = nullptr; |
|
1440 mStatement_BindEntry = nullptr; |
|
1441 mStatement_ClearDomain = nullptr; |
|
1442 mStatement_MarkEntry = nullptr; |
|
1443 mStatement_UnmarkEntry = nullptr; |
|
1444 mStatement_GetTypes = nullptr; |
|
1445 mStatement_FindNamespaceEntry = nullptr; |
|
1446 mStatement_InsertNamespaceEntry = nullptr; |
|
1447 mStatement_CleanupUnmarked = nullptr; |
|
1448 mStatement_GatherEntries = nullptr; |
|
1449 mStatement_ActivateClient = nullptr; |
|
1450 mStatement_DeactivateGroup = nullptr; |
|
1451 mStatement_FindClient = nullptr; |
|
1452 mStatement_FindClientByNamespace = nullptr; |
|
1453 mStatement_EnumerateApps = nullptr; |
|
1454 mStatement_EnumerateGroups = nullptr; |
|
1455 mStatement_EnumerateGroupsTimeOrder = nullptr; |
|
1456 } |
|
1457 |
|
1458 // Close Database on the correct thread |
|
1459 bool isOnCurrentThread = true; |
|
1460 if (mInitThread) |
|
1461 mInitThread->IsOnCurrentThread(&isOnCurrentThread); |
|
1462 |
|
1463 if (!isOnCurrentThread) { |
|
1464 nsCOMPtr<nsIRunnable> ev = new nsCloseDBEvent(mDB); |
|
1465 |
|
1466 if (ev) { |
|
1467 mInitThread->Dispatch(ev, NS_DISPATCH_NORMAL); |
|
1468 } |
|
1469 } |
|
1470 else { |
|
1471 mDB->Close(); |
|
1472 } |
|
1473 |
|
1474 mDB = nullptr; |
|
1475 mInitThread = nullptr; |
|
1476 |
|
1477 return NS_OK; |
|
1478 } |
|
1479 |
|
1480 const char * |
|
1481 nsOfflineCacheDevice::GetDeviceID() |
|
1482 { |
|
1483 return OFFLINE_CACHE_DEVICE_ID; |
|
1484 } |
|
1485 |
|
1486 nsCacheEntry * |
|
1487 nsOfflineCacheDevice::FindEntry(nsCString *fullKey, bool *collision) |
|
1488 { |
|
1489 mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_OFFLINE_SEARCH_2> timer; |
|
1490 LOG(("nsOfflineCacheDevice::FindEntry [key=%s]\n", fullKey->get())); |
|
1491 |
|
1492 // SELECT * FROM moz_cache WHERE key = ? |
|
1493 |
|
1494 // Decompose the key into "ClientID" and "Key" |
|
1495 nsAutoCString keyBuf; |
|
1496 const char *cid, *key; |
|
1497 if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf)) |
|
1498 return nullptr; |
|
1499 |
|
1500 AutoResetStatement statement(mStatement_FindEntry); |
|
1501 |
|
1502 nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid)); |
|
1503 nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key)); |
|
1504 NS_ENSURE_SUCCESS(rv, nullptr); |
|
1505 NS_ENSURE_SUCCESS(rv2, nullptr); |
|
1506 |
|
1507 bool hasRows; |
|
1508 rv = statement->ExecuteStep(&hasRows); |
|
1509 if (NS_FAILED(rv) || !hasRows) |
|
1510 return nullptr; // entry not found |
|
1511 |
|
1512 nsOfflineCacheRecord rec; |
|
1513 statement->GetSharedBlob(0, &rec.metaDataLen, |
|
1514 (const uint8_t **) &rec.metaData); |
|
1515 rec.generation = statement->AsInt32(1); |
|
1516 rec.dataSize = statement->AsInt32(2); |
|
1517 rec.fetchCount = statement->AsInt32(3); |
|
1518 rec.lastFetched = statement->AsInt64(4); |
|
1519 rec.lastModified = statement->AsInt64(5); |
|
1520 rec.expirationTime = statement->AsInt64(6); |
|
1521 |
|
1522 LOG(("entry: [%u %d %d %d %lld %lld %lld]\n", |
|
1523 rec.metaDataLen, |
|
1524 rec.generation, |
|
1525 rec.dataSize, |
|
1526 rec.fetchCount, |
|
1527 rec.lastFetched, |
|
1528 rec.lastModified, |
|
1529 rec.expirationTime)); |
|
1530 |
|
1531 nsCacheEntry *entry = CreateCacheEntry(this, fullKey, rec); |
|
1532 |
|
1533 if (entry) |
|
1534 { |
|
1535 // make sure that the data file exists |
|
1536 nsOfflineCacheBinding *binding = (nsOfflineCacheBinding*)entry->Data(); |
|
1537 bool isFile; |
|
1538 rv = binding->mDataFile->IsFile(&isFile); |
|
1539 if (NS_FAILED(rv) || !isFile) |
|
1540 { |
|
1541 DeleteEntry(entry, false); |
|
1542 delete entry; |
|
1543 return nullptr; |
|
1544 } |
|
1545 |
|
1546 // lock the entry |
|
1547 Lock(*fullKey); |
|
1548 } |
|
1549 |
|
1550 return entry; |
|
1551 } |
|
1552 |
|
1553 nsresult |
|
1554 nsOfflineCacheDevice::DeactivateEntry(nsCacheEntry *entry) |
|
1555 { |
|
1556 LOG(("nsOfflineCacheDevice::DeactivateEntry [key=%s]\n", |
|
1557 entry->Key()->get())); |
|
1558 |
|
1559 // This method is called to inform us that the nsCacheEntry object is going |
|
1560 // away. We should persist anything that needs to be persisted, or if the |
|
1561 // entry is doomed, we can go ahead and clear its storage. |
|
1562 |
|
1563 if (entry->IsDoomed()) |
|
1564 { |
|
1565 // remove corresponding row and file if they exist |
|
1566 |
|
1567 // the row should have been removed in DoomEntry... we could assert that |
|
1568 // that happened. otherwise, all we have to do here is delete the file |
|
1569 // on disk. |
|
1570 DeleteData(entry); |
|
1571 } |
|
1572 else if (((nsOfflineCacheBinding *)entry->Data())->IsNewEntry()) |
|
1573 { |
|
1574 // UPDATE the database row |
|
1575 |
|
1576 // Only new entries are updated, since offline cache is updated in |
|
1577 // transactions. New entries are those who is returned from |
|
1578 // BindEntry(). |
|
1579 |
|
1580 LOG(("nsOfflineCacheDevice::DeactivateEntry updating new entry\n")); |
|
1581 UpdateEntry(entry); |
|
1582 } else { |
|
1583 LOG(("nsOfflineCacheDevice::DeactivateEntry " |
|
1584 "skipping update since entry is not dirty\n")); |
|
1585 } |
|
1586 |
|
1587 // Unlock the entry |
|
1588 Unlock(*entry->Key()); |
|
1589 |
|
1590 delete entry; |
|
1591 |
|
1592 return NS_OK; |
|
1593 } |
|
1594 |
|
1595 nsresult |
|
1596 nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry) |
|
1597 { |
|
1598 LOG(("nsOfflineCacheDevice::BindEntry [key=%s]\n", entry->Key()->get())); |
|
1599 |
|
1600 NS_ENSURE_STATE(!entry->Data()); |
|
1601 |
|
1602 // This method is called to inform us that we have a new entry. The entry |
|
1603 // may collide with an existing entry in our DB, but if that happens we can |
|
1604 // assume that the entry is not being used. |
|
1605 |
|
1606 // INSERT the database row |
|
1607 |
|
1608 // XXX Assumption: if the row already exists, then FindEntry would have |
|
1609 // returned it. if that entry was doomed, then DoomEntry would have removed |
|
1610 // it from the table. so, we should always have to insert at this point. |
|
1611 |
|
1612 // Decompose the key into "ClientID" and "Key" |
|
1613 nsAutoCString keyBuf; |
|
1614 const char *cid, *key; |
|
1615 if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf)) |
|
1616 return NS_ERROR_UNEXPECTED; |
|
1617 |
|
1618 // create binding, pick best generation number |
|
1619 nsRefPtr<nsOfflineCacheBinding> binding = |
|
1620 nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key(), -1); |
|
1621 if (!binding) |
|
1622 return NS_ERROR_OUT_OF_MEMORY; |
|
1623 binding->MarkNewEntry(); |
|
1624 |
|
1625 nsOfflineCacheRecord rec; |
|
1626 rec.clientID = cid; |
|
1627 rec.key = key; |
|
1628 rec.metaData = nullptr; // don't write any metadata now. |
|
1629 rec.metaDataLen = 0; |
|
1630 rec.generation = binding->mGeneration; |
|
1631 rec.dataSize = 0; |
|
1632 rec.fetchCount = entry->FetchCount(); |
|
1633 rec.lastFetched = PRTimeFromSeconds(entry->LastFetched()); |
|
1634 rec.lastModified = PRTimeFromSeconds(entry->LastModified()); |
|
1635 rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime()); |
|
1636 |
|
1637 AutoResetStatement statement(mStatement_BindEntry); |
|
1638 |
|
1639 nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(rec.clientID)); |
|
1640 nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(rec.key)); |
|
1641 if (NS_FAILED(tmp)) { |
|
1642 rv = tmp; |
|
1643 } |
|
1644 tmp = statement->BindBlobByIndex(2, rec.metaData, rec.metaDataLen); |
|
1645 if (NS_FAILED(tmp)) { |
|
1646 rv = tmp; |
|
1647 } |
|
1648 tmp = statement->BindInt32ByIndex(3, rec.generation); |
|
1649 if (NS_FAILED(tmp)) { |
|
1650 rv = tmp; |
|
1651 } |
|
1652 tmp = statement->BindInt32ByIndex(4, rec.dataSize); |
|
1653 if (NS_FAILED(tmp)) { |
|
1654 rv = tmp; |
|
1655 } |
|
1656 tmp = statement->BindInt32ByIndex(5, rec.fetchCount); |
|
1657 if (NS_FAILED(tmp)) { |
|
1658 rv = tmp; |
|
1659 } |
|
1660 tmp = statement->BindInt64ByIndex(6, rec.lastFetched); |
|
1661 if (NS_FAILED(tmp)) { |
|
1662 rv = tmp; |
|
1663 } |
|
1664 tmp = statement->BindInt64ByIndex(7, rec.lastModified); |
|
1665 if (NS_FAILED(tmp)) { |
|
1666 rv = tmp; |
|
1667 } |
|
1668 tmp = statement->BindInt64ByIndex(8, rec.expirationTime); |
|
1669 if (NS_FAILED(tmp)) { |
|
1670 rv = tmp; |
|
1671 } |
|
1672 NS_ENSURE_SUCCESS(rv, rv); |
|
1673 |
|
1674 bool hasRows; |
|
1675 rv = statement->ExecuteStep(&hasRows); |
|
1676 NS_ENSURE_SUCCESS(rv, rv); |
|
1677 NS_ASSERTION(!hasRows, "INSERT should not result in output"); |
|
1678 |
|
1679 entry->SetData(binding); |
|
1680 |
|
1681 // lock the entry |
|
1682 Lock(*entry->Key()); |
|
1683 |
|
1684 return NS_OK; |
|
1685 } |
|
1686 |
|
1687 void |
|
1688 nsOfflineCacheDevice::DoomEntry(nsCacheEntry *entry) |
|
1689 { |
|
1690 LOG(("nsOfflineCacheDevice::DoomEntry [key=%s]\n", entry->Key()->get())); |
|
1691 |
|
1692 // This method is called to inform us that we should mark the entry to be |
|
1693 // deleted when it is no longer in use. |
|
1694 |
|
1695 // We can go ahead and delete the corresponding row in our table, |
|
1696 // but we must not delete the file on disk until we are deactivated. |
|
1697 // In another word, the file should be deleted if the entry had been |
|
1698 // deactivated. |
|
1699 |
|
1700 DeleteEntry(entry, !entry->IsActive()); |
|
1701 } |
|
1702 |
|
1703 nsresult |
|
1704 nsOfflineCacheDevice::OpenInputStreamForEntry(nsCacheEntry *entry, |
|
1705 nsCacheAccessMode mode, |
|
1706 uint32_t offset, |
|
1707 nsIInputStream **result) |
|
1708 { |
|
1709 LOG(("nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s]\n", |
|
1710 entry->Key()->get())); |
|
1711 |
|
1712 *result = nullptr; |
|
1713 |
|
1714 NS_ENSURE_TRUE(!offset || (offset < entry->DataSize()), NS_ERROR_INVALID_ARG); |
|
1715 |
|
1716 // return an input stream to the entry's data file. the stream |
|
1717 // may be read on a background thread. |
|
1718 |
|
1719 nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data(); |
|
1720 NS_ENSURE_STATE(binding); |
|
1721 |
|
1722 nsCOMPtr<nsIInputStream> in; |
|
1723 NS_NewLocalFileInputStream(getter_AddRefs(in), binding->mDataFile, PR_RDONLY); |
|
1724 if (!in) |
|
1725 return NS_ERROR_UNEXPECTED; |
|
1726 |
|
1727 // respect |offset| param |
|
1728 if (offset != 0) |
|
1729 { |
|
1730 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(in); |
|
1731 NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED); |
|
1732 |
|
1733 seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); |
|
1734 } |
|
1735 |
|
1736 in.swap(*result); |
|
1737 return NS_OK; |
|
1738 } |
|
1739 |
|
1740 nsresult |
|
1741 nsOfflineCacheDevice::OpenOutputStreamForEntry(nsCacheEntry *entry, |
|
1742 nsCacheAccessMode mode, |
|
1743 uint32_t offset, |
|
1744 nsIOutputStream **result) |
|
1745 { |
|
1746 LOG(("nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s]\n", |
|
1747 entry->Key()->get())); |
|
1748 |
|
1749 *result = nullptr; |
|
1750 |
|
1751 NS_ENSURE_TRUE(offset <= entry->DataSize(), NS_ERROR_INVALID_ARG); |
|
1752 |
|
1753 // return an output stream to the entry's data file. we can assume |
|
1754 // that the output stream will only be used on the main thread. |
|
1755 |
|
1756 nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data(); |
|
1757 NS_ENSURE_STATE(binding); |
|
1758 |
|
1759 nsCOMPtr<nsIOutputStream> out; |
|
1760 NS_NewLocalFileOutputStream(getter_AddRefs(out), binding->mDataFile, |
|
1761 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, |
|
1762 00600); |
|
1763 if (!out) |
|
1764 return NS_ERROR_UNEXPECTED; |
|
1765 |
|
1766 // respect |offset| param |
|
1767 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(out); |
|
1768 NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED); |
|
1769 if (offset != 0) |
|
1770 seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); |
|
1771 |
|
1772 // truncate the file at the given offset |
|
1773 seekable->SetEOF(); |
|
1774 |
|
1775 nsCOMPtr<nsIOutputStream> bufferedOut; |
|
1776 nsresult rv = |
|
1777 NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 16 * 1024); |
|
1778 NS_ENSURE_SUCCESS(rv, rv); |
|
1779 |
|
1780 bufferedOut.swap(*result); |
|
1781 return NS_OK; |
|
1782 } |
|
1783 |
|
1784 nsresult |
|
1785 nsOfflineCacheDevice::GetFileForEntry(nsCacheEntry *entry, nsIFile **result) |
|
1786 { |
|
1787 LOG(("nsOfflineCacheDevice::GetFileForEntry [key=%s]\n", |
|
1788 entry->Key()->get())); |
|
1789 |
|
1790 nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data(); |
|
1791 NS_ENSURE_STATE(binding); |
|
1792 |
|
1793 NS_IF_ADDREF(*result = binding->mDataFile); |
|
1794 return NS_OK; |
|
1795 } |
|
1796 |
|
1797 nsresult |
|
1798 nsOfflineCacheDevice::OnDataSizeChange(nsCacheEntry *entry, int32_t deltaSize) |
|
1799 { |
|
1800 LOG(("nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d]\n", |
|
1801 entry->Key()->get(), deltaSize)); |
|
1802 |
|
1803 const int32_t DELTA_THRESHOLD = 1<<14; // 16k |
|
1804 |
|
1805 // called to notify us of an impending change in the total size of the |
|
1806 // specified entry. |
|
1807 |
|
1808 uint32_t oldSize = entry->DataSize(); |
|
1809 NS_ASSERTION(deltaSize >= 0 || int32_t(oldSize) + deltaSize >= 0, "oops"); |
|
1810 uint32_t newSize = int32_t(oldSize) + deltaSize; |
|
1811 UpdateEntrySize(entry, newSize); |
|
1812 |
|
1813 mDeltaCounter += deltaSize; // this may go negative |
|
1814 |
|
1815 if (mDeltaCounter >= DELTA_THRESHOLD) |
|
1816 { |
|
1817 if (CacheSize() > mCacheCapacity) { |
|
1818 // the entry will overrun the cache capacity, doom the entry |
|
1819 // and abort |
|
1820 #ifdef DEBUG |
|
1821 nsresult rv = |
|
1822 #endif |
|
1823 nsCacheService::DoomEntry(entry); |
|
1824 NS_ASSERTION(NS_SUCCEEDED(rv), "DoomEntry() failed."); |
|
1825 return NS_ERROR_ABORT; |
|
1826 } |
|
1827 |
|
1828 mDeltaCounter = 0; // reset counter |
|
1829 } |
|
1830 |
|
1831 return NS_OK; |
|
1832 } |
|
1833 |
|
1834 nsresult |
|
1835 nsOfflineCacheDevice::Visit(nsICacheVisitor *visitor) |
|
1836 { |
|
1837 NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED); |
|
1838 |
|
1839 // called to enumerate the offline cache. |
|
1840 |
|
1841 nsCOMPtr<nsICacheDeviceInfo> deviceInfo = |
|
1842 new nsOfflineCacheDeviceInfo(this); |
|
1843 |
|
1844 bool keepGoing; |
|
1845 nsresult rv = visitor->VisitDevice(OFFLINE_CACHE_DEVICE_ID, deviceInfo, |
|
1846 &keepGoing); |
|
1847 if (NS_FAILED(rv)) |
|
1848 return rv; |
|
1849 |
|
1850 if (!keepGoing) |
|
1851 return NS_OK; |
|
1852 |
|
1853 // SELECT * from moz_cache; |
|
1854 |
|
1855 nsOfflineCacheRecord rec; |
|
1856 nsRefPtr<nsOfflineCacheEntryInfo> info = new nsOfflineCacheEntryInfo; |
|
1857 if (!info) |
|
1858 return NS_ERROR_OUT_OF_MEMORY; |
|
1859 info->mRec = &rec; |
|
1860 |
|
1861 // XXX may want to list columns explicitly |
|
1862 nsCOMPtr<mozIStorageStatement> statement; |
|
1863 rv = mDB->CreateStatement( |
|
1864 NS_LITERAL_CSTRING("SELECT * FROM moz_cache;"), |
|
1865 getter_AddRefs(statement)); |
|
1866 NS_ENSURE_SUCCESS(rv, rv); |
|
1867 |
|
1868 bool hasRows; |
|
1869 for (;;) |
|
1870 { |
|
1871 rv = statement->ExecuteStep(&hasRows); |
|
1872 if (NS_FAILED(rv) || !hasRows) |
|
1873 break; |
|
1874 |
|
1875 statement->GetSharedUTF8String(0, nullptr, &rec.clientID); |
|
1876 statement->GetSharedUTF8String(1, nullptr, &rec.key); |
|
1877 statement->GetSharedBlob(2, &rec.metaDataLen, |
|
1878 (const uint8_t **) &rec.metaData); |
|
1879 rec.generation = statement->AsInt32(3); |
|
1880 rec.dataSize = statement->AsInt32(4); |
|
1881 rec.fetchCount = statement->AsInt32(5); |
|
1882 rec.lastFetched = statement->AsInt64(6); |
|
1883 rec.lastModified = statement->AsInt64(7); |
|
1884 rec.expirationTime = statement->AsInt64(8); |
|
1885 |
|
1886 bool keepGoing; |
|
1887 rv = visitor->VisitEntry(OFFLINE_CACHE_DEVICE_ID, info, &keepGoing); |
|
1888 if (NS_FAILED(rv) || !keepGoing) |
|
1889 break; |
|
1890 } |
|
1891 |
|
1892 info->mRec = nullptr; |
|
1893 return NS_OK; |
|
1894 } |
|
1895 |
|
1896 nsresult |
|
1897 nsOfflineCacheDevice::EvictEntries(const char *clientID) |
|
1898 { |
|
1899 LOG(("nsOfflineCacheDevice::EvictEntries [cid=%s]\n", |
|
1900 clientID ? clientID : "")); |
|
1901 |
|
1902 // called to evict all entries matching the given clientID. |
|
1903 |
|
1904 // need trigger to fire user defined function after a row is deleted |
|
1905 // so we can delete the corresponding data file. |
|
1906 EvictionObserver evictionObserver(mDB, mEvictionFunction); |
|
1907 |
|
1908 nsCOMPtr<mozIStorageStatement> statement; |
|
1909 nsresult rv; |
|
1910 if (clientID) |
|
1911 { |
|
1912 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=?;"), |
|
1913 getter_AddRefs(statement)); |
|
1914 NS_ENSURE_SUCCESS(rv, rv); |
|
1915 |
|
1916 rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID)); |
|
1917 NS_ENSURE_SUCCESS(rv, rv); |
|
1918 |
|
1919 rv = statement->Execute(); |
|
1920 NS_ENSURE_SUCCESS(rv, rv); |
|
1921 |
|
1922 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups WHERE ActiveClientID=?;"), |
|
1923 getter_AddRefs(statement)); |
|
1924 NS_ENSURE_SUCCESS(rv, rv); |
|
1925 |
|
1926 rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID)); |
|
1927 NS_ENSURE_SUCCESS(rv, rv); |
|
1928 |
|
1929 rv = statement->Execute(); |
|
1930 NS_ENSURE_SUCCESS(rv, rv); |
|
1931 } |
|
1932 else |
|
1933 { |
|
1934 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache;"), |
|
1935 getter_AddRefs(statement)); |
|
1936 NS_ENSURE_SUCCESS(rv, rv); |
|
1937 |
|
1938 rv = statement->Execute(); |
|
1939 NS_ENSURE_SUCCESS(rv, rv); |
|
1940 |
|
1941 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups;"), |
|
1942 getter_AddRefs(statement)); |
|
1943 NS_ENSURE_SUCCESS(rv, rv); |
|
1944 |
|
1945 rv = statement->Execute(); |
|
1946 NS_ENSURE_SUCCESS(rv, rv); |
|
1947 } |
|
1948 |
|
1949 evictionObserver.Apply(); |
|
1950 |
|
1951 statement = nullptr; |
|
1952 // Also evict any namespaces associated with this clientID. |
|
1953 if (clientID) |
|
1954 { |
|
1955 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces WHERE ClientID=?"), |
|
1956 getter_AddRefs(statement)); |
|
1957 NS_ENSURE_SUCCESS(rv, rv); |
|
1958 |
|
1959 rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID)); |
|
1960 NS_ENSURE_SUCCESS(rv, rv); |
|
1961 } |
|
1962 else |
|
1963 { |
|
1964 rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces;"), |
|
1965 getter_AddRefs(statement)); |
|
1966 NS_ENSURE_SUCCESS(rv, rv); |
|
1967 } |
|
1968 |
|
1969 rv = statement->Execute(); |
|
1970 NS_ENSURE_SUCCESS(rv, rv); |
|
1971 |
|
1972 return NS_OK; |
|
1973 } |
|
1974 |
|
1975 nsresult |
|
1976 nsOfflineCacheDevice::MarkEntry(const nsCString &clientID, |
|
1977 const nsACString &key, |
|
1978 uint32_t typeBits) |
|
1979 { |
|
1980 LOG(("nsOfflineCacheDevice::MarkEntry [cid=%s, key=%s, typeBits=%d]\n", |
|
1981 clientID.get(), PromiseFlatCString(key).get(), typeBits)); |
|
1982 |
|
1983 AutoResetStatement statement(mStatement_MarkEntry); |
|
1984 nsresult rv = statement->BindInt32ByIndex(0, typeBits); |
|
1985 NS_ENSURE_SUCCESS(rv, rv); |
|
1986 rv = statement->BindUTF8StringByIndex(1, clientID); |
|
1987 NS_ENSURE_SUCCESS(rv, rv); |
|
1988 rv = statement->BindUTF8StringByIndex(2, key); |
|
1989 NS_ENSURE_SUCCESS(rv, rv); |
|
1990 |
|
1991 rv = statement->Execute(); |
|
1992 NS_ENSURE_SUCCESS(rv, rv); |
|
1993 |
|
1994 return NS_OK; |
|
1995 } |
|
1996 |
|
1997 nsresult |
|
1998 nsOfflineCacheDevice::UnmarkEntry(const nsCString &clientID, |
|
1999 const nsACString &key, |
|
2000 uint32_t typeBits) |
|
2001 { |
|
2002 LOG(("nsOfflineCacheDevice::UnmarkEntry [cid=%s, key=%s, typeBits=%d]\n", |
|
2003 clientID.get(), PromiseFlatCString(key).get(), typeBits)); |
|
2004 |
|
2005 AutoResetStatement statement(mStatement_UnmarkEntry); |
|
2006 nsresult rv = statement->BindInt32ByIndex(0, typeBits); |
|
2007 NS_ENSURE_SUCCESS(rv, rv); |
|
2008 rv = statement->BindUTF8StringByIndex(1, clientID); |
|
2009 NS_ENSURE_SUCCESS(rv, rv); |
|
2010 rv = statement->BindUTF8StringByIndex(2, key); |
|
2011 NS_ENSURE_SUCCESS(rv, rv); |
|
2012 |
|
2013 rv = statement->Execute(); |
|
2014 NS_ENSURE_SUCCESS(rv, rv); |
|
2015 |
|
2016 // Remove the entry if it is now empty. |
|
2017 |
|
2018 EvictionObserver evictionObserver(mDB, mEvictionFunction); |
|
2019 |
|
2020 AutoResetStatement cleanupStatement(mStatement_CleanupUnmarked); |
|
2021 rv = cleanupStatement->BindUTF8StringByIndex(0, clientID); |
|
2022 NS_ENSURE_SUCCESS(rv, rv); |
|
2023 rv = cleanupStatement->BindUTF8StringByIndex(1, key); |
|
2024 NS_ENSURE_SUCCESS(rv, rv); |
|
2025 |
|
2026 rv = cleanupStatement->Execute(); |
|
2027 NS_ENSURE_SUCCESS(rv, rv); |
|
2028 |
|
2029 evictionObserver.Apply(); |
|
2030 |
|
2031 return NS_OK; |
|
2032 } |
|
2033 |
|
2034 nsresult |
|
2035 nsOfflineCacheDevice::GetMatchingNamespace(const nsCString &clientID, |
|
2036 const nsACString &key, |
|
2037 nsIApplicationCacheNamespace **out) |
|
2038 { |
|
2039 LOG(("nsOfflineCacheDevice::GetMatchingNamespace [cid=%s, key=%s]\n", |
|
2040 clientID.get(), PromiseFlatCString(key).get())); |
|
2041 |
|
2042 nsresult rv; |
|
2043 |
|
2044 AutoResetStatement statement(mStatement_FindNamespaceEntry); |
|
2045 |
|
2046 rv = statement->BindUTF8StringByIndex(0, clientID); |
|
2047 NS_ENSURE_SUCCESS(rv, rv); |
|
2048 rv = statement->BindUTF8StringByIndex(1, key); |
|
2049 NS_ENSURE_SUCCESS(rv, rv); |
|
2050 |
|
2051 bool hasRows; |
|
2052 rv = statement->ExecuteStep(&hasRows); |
|
2053 NS_ENSURE_SUCCESS(rv, rv); |
|
2054 |
|
2055 *out = nullptr; |
|
2056 |
|
2057 bool found = false; |
|
2058 nsCString nsSpec; |
|
2059 int32_t nsType = 0; |
|
2060 nsCString nsData; |
|
2061 |
|
2062 while (hasRows) |
|
2063 { |
|
2064 int32_t itemType; |
|
2065 rv = statement->GetInt32(2, &itemType); |
|
2066 NS_ENSURE_SUCCESS(rv, rv); |
|
2067 |
|
2068 if (!found || itemType > nsType) |
|
2069 { |
|
2070 nsType = itemType; |
|
2071 |
|
2072 rv = statement->GetUTF8String(0, nsSpec); |
|
2073 NS_ENSURE_SUCCESS(rv, rv); |
|
2074 |
|
2075 rv = statement->GetUTF8String(1, nsData); |
|
2076 NS_ENSURE_SUCCESS(rv, rv); |
|
2077 |
|
2078 found = true; |
|
2079 } |
|
2080 |
|
2081 rv = statement->ExecuteStep(&hasRows); |
|
2082 NS_ENSURE_SUCCESS(rv, rv); |
|
2083 } |
|
2084 |
|
2085 if (found) { |
|
2086 nsCOMPtr<nsIApplicationCacheNamespace> ns = |
|
2087 new nsApplicationCacheNamespace(); |
|
2088 if (!ns) |
|
2089 return NS_ERROR_OUT_OF_MEMORY; |
|
2090 rv = ns->Init(nsType, nsSpec, nsData); |
|
2091 NS_ENSURE_SUCCESS(rv, rv); |
|
2092 |
|
2093 ns.swap(*out); |
|
2094 } |
|
2095 |
|
2096 return NS_OK; |
|
2097 } |
|
2098 |
|
2099 nsresult |
|
2100 nsOfflineCacheDevice::CacheOpportunistically(const nsCString &clientID, |
|
2101 const nsACString &key) |
|
2102 { |
|
2103 // XXX: We should also be propagating this cache entry to other matching |
|
2104 // caches. See bug 444807. |
|
2105 |
|
2106 return MarkEntry(clientID, key, nsIApplicationCache::ITEM_OPPORTUNISTIC); |
|
2107 } |
|
2108 |
|
2109 nsresult |
|
2110 nsOfflineCacheDevice::GetTypes(const nsCString &clientID, |
|
2111 const nsACString &key, |
|
2112 uint32_t *typeBits) |
|
2113 { |
|
2114 LOG(("nsOfflineCacheDevice::GetTypes [cid=%s, key=%s]\n", |
|
2115 clientID.get(), PromiseFlatCString(key).get())); |
|
2116 |
|
2117 AutoResetStatement statement(mStatement_GetTypes); |
|
2118 nsresult rv = statement->BindUTF8StringByIndex(0, clientID); |
|
2119 NS_ENSURE_SUCCESS(rv, rv); |
|
2120 rv = statement->BindUTF8StringByIndex(1, key); |
|
2121 NS_ENSURE_SUCCESS(rv, rv); |
|
2122 |
|
2123 bool hasRows; |
|
2124 rv = statement->ExecuteStep(&hasRows); |
|
2125 NS_ENSURE_SUCCESS(rv, rv); |
|
2126 |
|
2127 if (!hasRows) |
|
2128 return NS_ERROR_CACHE_KEY_NOT_FOUND; |
|
2129 |
|
2130 *typeBits = statement->AsInt32(0); |
|
2131 |
|
2132 return NS_OK; |
|
2133 } |
|
2134 |
|
2135 nsresult |
|
2136 nsOfflineCacheDevice::GatherEntries(const nsCString &clientID, |
|
2137 uint32_t typeBits, |
|
2138 uint32_t *count, |
|
2139 char ***keys) |
|
2140 { |
|
2141 LOG(("nsOfflineCacheDevice::GatherEntries [cid=%s, typeBits=%X]\n", |
|
2142 clientID.get(), typeBits)); |
|
2143 |
|
2144 AutoResetStatement statement(mStatement_GatherEntries); |
|
2145 nsresult rv = statement->BindUTF8StringByIndex(0, clientID); |
|
2146 NS_ENSURE_SUCCESS(rv, rv); |
|
2147 |
|
2148 rv = statement->BindInt32ByIndex(1, typeBits); |
|
2149 NS_ENSURE_SUCCESS(rv, rv); |
|
2150 |
|
2151 return RunSimpleQuery(mStatement_GatherEntries, 0, count, keys); |
|
2152 } |
|
2153 |
|
2154 nsresult |
|
2155 nsOfflineCacheDevice::AddNamespace(const nsCString &clientID, |
|
2156 nsIApplicationCacheNamespace *ns) |
|
2157 { |
|
2158 nsCString namespaceSpec; |
|
2159 nsresult rv = ns->GetNamespaceSpec(namespaceSpec); |
|
2160 NS_ENSURE_SUCCESS(rv, rv); |
|
2161 |
|
2162 nsCString data; |
|
2163 rv = ns->GetData(data); |
|
2164 NS_ENSURE_SUCCESS(rv, rv); |
|
2165 |
|
2166 uint32_t itemType; |
|
2167 rv = ns->GetItemType(&itemType); |
|
2168 NS_ENSURE_SUCCESS(rv, rv); |
|
2169 |
|
2170 LOG(("nsOfflineCacheDevice::AddNamespace [cid=%s, ns=%s, data=%s, type=%d]", |
|
2171 clientID.get(), namespaceSpec.get(), data.get(), itemType)); |
|
2172 |
|
2173 AutoResetStatement statement(mStatement_InsertNamespaceEntry); |
|
2174 |
|
2175 rv = statement->BindUTF8StringByIndex(0, clientID); |
|
2176 NS_ENSURE_SUCCESS(rv, rv); |
|
2177 |
|
2178 rv = statement->BindUTF8StringByIndex(1, namespaceSpec); |
|
2179 NS_ENSURE_SUCCESS(rv, rv); |
|
2180 |
|
2181 rv = statement->BindUTF8StringByIndex(2, data); |
|
2182 NS_ENSURE_SUCCESS(rv, rv); |
|
2183 |
|
2184 rv = statement->BindInt32ByIndex(3, itemType); |
|
2185 NS_ENSURE_SUCCESS(rv, rv); |
|
2186 |
|
2187 rv = statement->Execute(); |
|
2188 NS_ENSURE_SUCCESS(rv, rv); |
|
2189 |
|
2190 return NS_OK; |
|
2191 } |
|
2192 |
|
2193 nsresult |
|
2194 nsOfflineCacheDevice::GetUsage(const nsACString &clientID, |
|
2195 uint32_t *usage) |
|
2196 { |
|
2197 LOG(("nsOfflineCacheDevice::GetUsage [cid=%s]\n", |
|
2198 PromiseFlatCString(clientID).get())); |
|
2199 |
|
2200 *usage = 0; |
|
2201 |
|
2202 AutoResetStatement statement(mStatement_ApplicationCacheSize); |
|
2203 |
|
2204 nsresult rv = statement->BindUTF8StringByIndex(0, clientID); |
|
2205 NS_ENSURE_SUCCESS(rv, rv); |
|
2206 |
|
2207 bool hasRows; |
|
2208 rv = statement->ExecuteStep(&hasRows); |
|
2209 NS_ENSURE_SUCCESS(rv, rv); |
|
2210 |
|
2211 if (!hasRows) |
|
2212 return NS_OK; |
|
2213 |
|
2214 *usage = static_cast<uint32_t>(statement->AsInt32(0)); |
|
2215 |
|
2216 return NS_OK; |
|
2217 } |
|
2218 |
|
2219 nsresult |
|
2220 nsOfflineCacheDevice::GetGroups(uint32_t *count, |
|
2221 char ***keys) |
|
2222 { |
|
2223 LOG(("nsOfflineCacheDevice::GetGroups")); |
|
2224 |
|
2225 return RunSimpleQuery(mStatement_EnumerateGroups, 0, count, keys); |
|
2226 } |
|
2227 |
|
2228 nsresult |
|
2229 nsOfflineCacheDevice::GetGroupsTimeOrdered(uint32_t *count, |
|
2230 char ***keys) |
|
2231 { |
|
2232 LOG(("nsOfflineCacheDevice::GetGroupsTimeOrder")); |
|
2233 |
|
2234 return RunSimpleQuery(mStatement_EnumerateGroupsTimeOrder, 0, count, keys); |
|
2235 } |
|
2236 |
|
2237 bool |
|
2238 nsOfflineCacheDevice::IsLocked(const nsACString &key) |
|
2239 { |
|
2240 MutexAutoLock lock(mLock); |
|
2241 return mLockedEntries.GetEntry(key); |
|
2242 } |
|
2243 |
|
2244 void |
|
2245 nsOfflineCacheDevice::Lock(const nsACString &key) |
|
2246 { |
|
2247 MutexAutoLock lock(mLock); |
|
2248 mLockedEntries.PutEntry(key); |
|
2249 } |
|
2250 |
|
2251 void |
|
2252 nsOfflineCacheDevice::Unlock(const nsACString &key) |
|
2253 { |
|
2254 MutexAutoLock lock(mLock); |
|
2255 mLockedEntries.RemoveEntry(key); |
|
2256 } |
|
2257 |
|
2258 nsresult |
|
2259 nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement, |
|
2260 uint32_t resultIndex, |
|
2261 uint32_t * count, |
|
2262 char *** values) |
|
2263 { |
|
2264 bool hasRows; |
|
2265 nsresult rv = statement->ExecuteStep(&hasRows); |
|
2266 NS_ENSURE_SUCCESS(rv, rv); |
|
2267 |
|
2268 nsTArray<nsCString> valArray; |
|
2269 while (hasRows) |
|
2270 { |
|
2271 uint32_t length; |
|
2272 valArray.AppendElement( |
|
2273 nsDependentCString(statement->AsSharedUTF8String(resultIndex, &length))); |
|
2274 |
|
2275 rv = statement->ExecuteStep(&hasRows); |
|
2276 NS_ENSURE_SUCCESS(rv, rv); |
|
2277 } |
|
2278 |
|
2279 *count = valArray.Length(); |
|
2280 char **ret = static_cast<char **>(NS_Alloc(*count * sizeof(char*))); |
|
2281 if (!ret) return NS_ERROR_OUT_OF_MEMORY; |
|
2282 |
|
2283 for (uint32_t i = 0; i < *count; i++) { |
|
2284 ret[i] = NS_strdup(valArray[i].get()); |
|
2285 if (!ret[i]) { |
|
2286 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret); |
|
2287 return NS_ERROR_OUT_OF_MEMORY; |
|
2288 } |
|
2289 } |
|
2290 |
|
2291 *values = ret; |
|
2292 |
|
2293 return NS_OK; |
|
2294 } |
|
2295 |
|
2296 nsresult |
|
2297 nsOfflineCacheDevice::CreateApplicationCache(const nsACString &group, |
|
2298 nsIApplicationCache **out) |
|
2299 { |
|
2300 *out = nullptr; |
|
2301 |
|
2302 nsCString clientID; |
|
2303 // Some characters are special in the clientID. Escape the groupID |
|
2304 // before putting it in to the client key. |
|
2305 if (!NS_Escape(nsCString(group), clientID, url_Path)) { |
|
2306 return NS_ERROR_OUT_OF_MEMORY; |
|
2307 } |
|
2308 |
|
2309 PRTime now = PR_Now(); |
|
2310 |
|
2311 // Include the timestamp to guarantee uniqueness across runs, and |
|
2312 // the gNextTemporaryClientID for uniqueness within a second. |
|
2313 clientID.Append(nsPrintfCString("|%016lld|%d", |
|
2314 now / PR_USEC_PER_SEC, |
|
2315 gNextTemporaryClientID++)); |
|
2316 |
|
2317 nsCOMPtr<nsIApplicationCache> cache = new nsApplicationCache(this, |
|
2318 group, |
|
2319 clientID); |
|
2320 if (!cache) |
|
2321 return NS_ERROR_OUT_OF_MEMORY; |
|
2322 |
|
2323 nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(cache); |
|
2324 if (!weak) |
|
2325 return NS_ERROR_OUT_OF_MEMORY; |
|
2326 |
|
2327 MutexAutoLock lock(mLock); |
|
2328 mCaches.Put(clientID, weak); |
|
2329 |
|
2330 cache.swap(*out); |
|
2331 |
|
2332 return NS_OK; |
|
2333 } |
|
2334 |
|
2335 nsresult |
|
2336 nsOfflineCacheDevice::GetApplicationCache(const nsACString &clientID, |
|
2337 nsIApplicationCache **out) |
|
2338 { |
|
2339 MutexAutoLock lock(mLock); |
|
2340 return GetApplicationCache_Unlocked(clientID, out); |
|
2341 } |
|
2342 |
|
2343 nsresult |
|
2344 nsOfflineCacheDevice::GetApplicationCache_Unlocked(const nsACString &clientID, |
|
2345 nsIApplicationCache **out) |
|
2346 { |
|
2347 *out = nullptr; |
|
2348 |
|
2349 nsCOMPtr<nsIApplicationCache> cache; |
|
2350 |
|
2351 nsWeakPtr weak; |
|
2352 if (mCaches.Get(clientID, getter_AddRefs(weak))) |
|
2353 cache = do_QueryReferent(weak); |
|
2354 |
|
2355 if (!cache) |
|
2356 { |
|
2357 nsCString group; |
|
2358 nsresult rv = GetGroupForCache(clientID, group); |
|
2359 NS_ENSURE_SUCCESS(rv, rv); |
|
2360 |
|
2361 if (group.IsEmpty()) { |
|
2362 return NS_OK; |
|
2363 } |
|
2364 |
|
2365 cache = new nsApplicationCache(this, group, clientID); |
|
2366 weak = do_GetWeakReference(cache); |
|
2367 if (!weak) |
|
2368 return NS_ERROR_OUT_OF_MEMORY; |
|
2369 |
|
2370 mCaches.Put(clientID, weak); |
|
2371 } |
|
2372 |
|
2373 cache.swap(*out); |
|
2374 |
|
2375 return NS_OK; |
|
2376 } |
|
2377 |
|
2378 nsresult |
|
2379 nsOfflineCacheDevice::GetActiveCache(const nsACString &group, |
|
2380 nsIApplicationCache **out) |
|
2381 { |
|
2382 *out = nullptr; |
|
2383 |
|
2384 MutexAutoLock lock(mLock); |
|
2385 |
|
2386 nsCString *clientID; |
|
2387 if (mActiveCachesByGroup.Get(group, &clientID)) |
|
2388 return GetApplicationCache_Unlocked(*clientID, out); |
|
2389 |
|
2390 return NS_OK; |
|
2391 } |
|
2392 |
|
2393 nsresult |
|
2394 nsOfflineCacheDevice::DeactivateGroup(const nsACString &group) |
|
2395 { |
|
2396 nsCString *active = nullptr; |
|
2397 |
|
2398 AutoResetStatement statement(mStatement_DeactivateGroup); |
|
2399 nsresult rv = statement->BindUTF8StringByIndex(0, group); |
|
2400 NS_ENSURE_SUCCESS(rv, rv); |
|
2401 |
|
2402 rv = statement->Execute(); |
|
2403 NS_ENSURE_SUCCESS(rv, rv); |
|
2404 |
|
2405 MutexAutoLock lock(mLock); |
|
2406 |
|
2407 if (mActiveCachesByGroup.Get(group, &active)) |
|
2408 { |
|
2409 mActiveCaches.RemoveEntry(*active); |
|
2410 mActiveCachesByGroup.Remove(group); |
|
2411 active = nullptr; |
|
2412 } |
|
2413 |
|
2414 return NS_OK; |
|
2415 } |
|
2416 |
|
2417 nsresult |
|
2418 nsOfflineCacheDevice::DiscardByAppId(int32_t appID, bool browserEntriesOnly) |
|
2419 { |
|
2420 nsresult rv; |
|
2421 |
|
2422 nsAutoCString jaridsuffix; |
|
2423 jaridsuffix.Append('%'); |
|
2424 rv = AppendJARIdentifier(jaridsuffix, appID, browserEntriesOnly); |
|
2425 NS_ENSURE_SUCCESS(rv, rv); |
|
2426 |
|
2427 { |
|
2428 AutoResetStatement statement(mStatement_EnumerateApps); |
|
2429 rv = statement->BindUTF8StringByIndex(0, jaridsuffix); |
|
2430 NS_ENSURE_SUCCESS(rv, rv); |
|
2431 |
|
2432 bool hasRows; |
|
2433 rv = statement->ExecuteStep(&hasRows); |
|
2434 NS_ENSURE_SUCCESS(rv, rv); |
|
2435 |
|
2436 while (hasRows) { |
|
2437 nsAutoCString group; |
|
2438 rv = statement->GetUTF8String(0, group); |
|
2439 NS_ENSURE_SUCCESS(rv, rv); |
|
2440 |
|
2441 nsCString clientID; |
|
2442 rv = statement->GetUTF8String(1, clientID); |
|
2443 NS_ENSURE_SUCCESS(rv, rv); |
|
2444 |
|
2445 nsCOMPtr<nsIRunnable> ev = |
|
2446 new nsOfflineCacheDiscardCache(this, group, clientID); |
|
2447 |
|
2448 rv = nsCacheService::DispatchToCacheIOThread(ev); |
|
2449 NS_ENSURE_SUCCESS(rv, rv); |
|
2450 |
|
2451 rv = statement->ExecuteStep(&hasRows); |
|
2452 NS_ENSURE_SUCCESS(rv, rv); |
|
2453 } |
|
2454 } |
|
2455 |
|
2456 if (!browserEntriesOnly) { |
|
2457 // If deleting app, delete any 'inBrowserElement' entries too |
|
2458 rv = DiscardByAppId(appID, true); |
|
2459 NS_ENSURE_SUCCESS(rv, rv); |
|
2460 } |
|
2461 |
|
2462 return NS_OK; |
|
2463 } |
|
2464 |
|
2465 bool |
|
2466 nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI, |
|
2467 const nsACString &clientID, |
|
2468 nsILoadContextInfo *loadContextInfo) |
|
2469 { |
|
2470 { |
|
2471 MutexAutoLock lock(mLock); |
|
2472 if (!mActiveCaches.Contains(clientID)) |
|
2473 return false; |
|
2474 } |
|
2475 |
|
2476 nsAutoCString groupID; |
|
2477 nsresult rv = GetGroupForCache(clientID, groupID); |
|
2478 NS_ENSURE_SUCCESS(rv, false); |
|
2479 |
|
2480 nsCOMPtr<nsIURI> groupURI; |
|
2481 rv = NS_NewURI(getter_AddRefs(groupURI), groupID); |
|
2482 if (NS_FAILED(rv)) |
|
2483 return false; |
|
2484 |
|
2485 // When we are choosing an initial cache to load the top |
|
2486 // level document from, the URL of that document must have |
|
2487 // the same origin as the manifest, according to the spec. |
|
2488 // The following check is here because explicit, fallback |
|
2489 // and dynamic entries might have origin different from the |
|
2490 // manifest origin. |
|
2491 if (!NS_SecurityCompareURIs(keyURI, groupURI, |
|
2492 GetStrictFileOriginPolicy())) |
|
2493 return false; |
|
2494 |
|
2495 // Get extended origin attributes |
|
2496 uint32_t appId = NECKO_NO_APP_ID; |
|
2497 bool isInBrowserElement = false; |
|
2498 |
|
2499 if (loadContextInfo) { |
|
2500 appId = loadContextInfo->AppId(); |
|
2501 isInBrowserElement = loadContextInfo->IsInBrowserElement(); |
|
2502 } |
|
2503 |
|
2504 // Check the groupID we found is equal to groupID based |
|
2505 // on the load context demanding load from app cache. |
|
2506 // This is check of extended origin. |
|
2507 nsAutoCString demandedGroupID; |
|
2508 rv = BuildApplicationCacheGroupID(groupURI, appId, isInBrowserElement, |
|
2509 demandedGroupID); |
|
2510 NS_ENSURE_SUCCESS(rv, false); |
|
2511 |
|
2512 if (groupID != demandedGroupID) |
|
2513 return false; |
|
2514 |
|
2515 return true; |
|
2516 } |
|
2517 |
|
2518 |
|
2519 nsresult |
|
2520 nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key, |
|
2521 nsILoadContextInfo *loadContextInfo, |
|
2522 nsIApplicationCache **out) |
|
2523 { |
|
2524 *out = nullptr; |
|
2525 |
|
2526 nsCOMPtr<nsIURI> keyURI; |
|
2527 nsresult rv = NS_NewURI(getter_AddRefs(keyURI), key); |
|
2528 NS_ENSURE_SUCCESS(rv, rv); |
|
2529 |
|
2530 // First try to find a matching cache entry. |
|
2531 AutoResetStatement statement(mStatement_FindClient); |
|
2532 rv = statement->BindUTF8StringByIndex(0, key); |
|
2533 NS_ENSURE_SUCCESS(rv, rv); |
|
2534 |
|
2535 bool hasRows; |
|
2536 rv = statement->ExecuteStep(&hasRows); |
|
2537 NS_ENSURE_SUCCESS(rv, rv); |
|
2538 |
|
2539 while (hasRows) { |
|
2540 int32_t itemType; |
|
2541 rv = statement->GetInt32(1, &itemType); |
|
2542 NS_ENSURE_SUCCESS(rv, rv); |
|
2543 |
|
2544 if (!(itemType & nsIApplicationCache::ITEM_FOREIGN)) { |
|
2545 nsAutoCString clientID; |
|
2546 rv = statement->GetUTF8String(0, clientID); |
|
2547 NS_ENSURE_SUCCESS(rv, rv); |
|
2548 |
|
2549 if (CanUseCache(keyURI, clientID, loadContextInfo)) { |
|
2550 return GetApplicationCache(clientID, out); |
|
2551 } |
|
2552 } |
|
2553 |
|
2554 rv = statement->ExecuteStep(&hasRows); |
|
2555 NS_ENSURE_SUCCESS(rv, rv); |
|
2556 } |
|
2557 |
|
2558 // OK, we didn't find an exact match. Search for a client with a |
|
2559 // matching namespace. |
|
2560 |
|
2561 AutoResetStatement nsstatement(mStatement_FindClientByNamespace); |
|
2562 |
|
2563 rv = nsstatement->BindUTF8StringByIndex(0, key); |
|
2564 NS_ENSURE_SUCCESS(rv, rv); |
|
2565 |
|
2566 rv = nsstatement->ExecuteStep(&hasRows); |
|
2567 NS_ENSURE_SUCCESS(rv, rv); |
|
2568 |
|
2569 while (hasRows) |
|
2570 { |
|
2571 int32_t itemType; |
|
2572 rv = nsstatement->GetInt32(1, &itemType); |
|
2573 NS_ENSURE_SUCCESS(rv, rv); |
|
2574 |
|
2575 // Don't associate with a cache based solely on a whitelist entry |
|
2576 if (!(itemType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) { |
|
2577 nsAutoCString clientID; |
|
2578 rv = nsstatement->GetUTF8String(0, clientID); |
|
2579 NS_ENSURE_SUCCESS(rv, rv); |
|
2580 |
|
2581 if (CanUseCache(keyURI, clientID, loadContextInfo)) { |
|
2582 return GetApplicationCache(clientID, out); |
|
2583 } |
|
2584 } |
|
2585 |
|
2586 rv = nsstatement->ExecuteStep(&hasRows); |
|
2587 NS_ENSURE_SUCCESS(rv, rv); |
|
2588 } |
|
2589 |
|
2590 return NS_OK; |
|
2591 } |
|
2592 |
|
2593 nsresult |
|
2594 nsOfflineCacheDevice::CacheOpportunistically(nsIApplicationCache* cache, |
|
2595 const nsACString &key) |
|
2596 { |
|
2597 NS_ENSURE_ARG_POINTER(cache); |
|
2598 |
|
2599 nsresult rv; |
|
2600 |
|
2601 nsAutoCString clientID; |
|
2602 rv = cache->GetClientID(clientID); |
|
2603 NS_ENSURE_SUCCESS(rv, rv); |
|
2604 |
|
2605 return CacheOpportunistically(clientID, key); |
|
2606 } |
|
2607 |
|
2608 nsresult |
|
2609 nsOfflineCacheDevice::ActivateCache(const nsCSubstring &group, |
|
2610 const nsCSubstring &clientID) |
|
2611 { |
|
2612 AutoResetStatement statement(mStatement_ActivateClient); |
|
2613 nsresult rv = statement->BindUTF8StringByIndex(0, group); |
|
2614 NS_ENSURE_SUCCESS(rv, rv); |
|
2615 rv = statement->BindUTF8StringByIndex(1, clientID); |
|
2616 NS_ENSURE_SUCCESS(rv, rv); |
|
2617 rv = statement->BindInt32ByIndex(2, SecondsFromPRTime(PR_Now())); |
|
2618 NS_ENSURE_SUCCESS(rv, rv); |
|
2619 |
|
2620 rv = statement->Execute(); |
|
2621 NS_ENSURE_SUCCESS(rv, rv); |
|
2622 |
|
2623 MutexAutoLock lock(mLock); |
|
2624 |
|
2625 nsCString *active; |
|
2626 if (mActiveCachesByGroup.Get(group, &active)) |
|
2627 { |
|
2628 mActiveCaches.RemoveEntry(*active); |
|
2629 mActiveCachesByGroup.Remove(group); |
|
2630 active = nullptr; |
|
2631 } |
|
2632 |
|
2633 if (!clientID.IsEmpty()) |
|
2634 { |
|
2635 mActiveCaches.PutEntry(clientID); |
|
2636 mActiveCachesByGroup.Put(group, new nsCString(clientID)); |
|
2637 } |
|
2638 |
|
2639 return NS_OK; |
|
2640 } |
|
2641 |
|
2642 bool |
|
2643 nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group, |
|
2644 const nsCSubstring &clientID) |
|
2645 { |
|
2646 nsCString *active = nullptr; |
|
2647 MutexAutoLock lock(mLock); |
|
2648 return mActiveCachesByGroup.Get(group, &active) && *active == clientID; |
|
2649 } |
|
2650 |
|
2651 /** |
|
2652 * Preference accessors |
|
2653 */ |
|
2654 |
|
2655 void |
|
2656 nsOfflineCacheDevice::SetCacheParentDirectory(nsIFile *parentDir) |
|
2657 { |
|
2658 if (Initialized()) |
|
2659 { |
|
2660 NS_ERROR("cannot switch cache directory once initialized"); |
|
2661 return; |
|
2662 } |
|
2663 |
|
2664 if (!parentDir) |
|
2665 { |
|
2666 mCacheDirectory = nullptr; |
|
2667 return; |
|
2668 } |
|
2669 |
|
2670 // ensure parent directory exists |
|
2671 nsresult rv = EnsureDir(parentDir); |
|
2672 if (NS_FAILED(rv)) |
|
2673 { |
|
2674 NS_WARNING("unable to create parent directory"); |
|
2675 return; |
|
2676 } |
|
2677 |
|
2678 mBaseDirectory = parentDir; |
|
2679 |
|
2680 // cache dir may not exist, but that's ok |
|
2681 nsCOMPtr<nsIFile> dir; |
|
2682 rv = parentDir->Clone(getter_AddRefs(dir)); |
|
2683 if (NS_FAILED(rv)) |
|
2684 return; |
|
2685 rv = dir->AppendNative(NS_LITERAL_CSTRING("OfflineCache")); |
|
2686 if (NS_FAILED(rv)) |
|
2687 return; |
|
2688 |
|
2689 mCacheDirectory = do_QueryInterface(dir); |
|
2690 } |
|
2691 |
|
2692 void |
|
2693 nsOfflineCacheDevice::SetCapacity(uint32_t capacity) |
|
2694 { |
|
2695 mCacheCapacity = capacity * 1024; |
|
2696 } |
|
2697 |
|
2698 bool |
|
2699 nsOfflineCacheDevice::AutoShutdown(nsIApplicationCache * aAppCache) |
|
2700 { |
|
2701 if (!mAutoShutdown) |
|
2702 return false; |
|
2703 |
|
2704 mAutoShutdown = false; |
|
2705 |
|
2706 Shutdown(); |
|
2707 |
|
2708 nsRefPtr<nsCacheService> cacheService = nsCacheService::GlobalInstance(); |
|
2709 cacheService->RemoveCustomOfflineDevice(this); |
|
2710 |
|
2711 nsAutoCString clientID; |
|
2712 aAppCache->GetClientID(clientID); |
|
2713 |
|
2714 MutexAutoLock lock(mLock); |
|
2715 mCaches.Remove(clientID); |
|
2716 |
|
2717 return true; |
|
2718 } |