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