|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "mozilla/ArrayUtils.h" |
|
8 #include "mozilla/Attributes.h" |
|
9 #include "mozilla/Assertions.h" |
|
10 #include "mozilla/DebugOnly.h" |
|
11 |
|
12 #include "necko-config.h" |
|
13 |
|
14 #include "nsCache.h" |
|
15 #include "nsCacheService.h" |
|
16 #include "nsCacheRequest.h" |
|
17 #include "nsCacheEntry.h" |
|
18 #include "nsCacheEntryDescriptor.h" |
|
19 #include "nsCacheDevice.h" |
|
20 #include "nsMemoryCacheDevice.h" |
|
21 #include "nsICacheVisitor.h" |
|
22 #include "nsDiskCacheDevice.h" |
|
23 #include "nsDiskCacheDeviceSQL.h" |
|
24 #include "nsCacheUtils.h" |
|
25 #include "../cache2/CacheObserver.h" |
|
26 |
|
27 #include "nsIObserverService.h" |
|
28 #include "nsIPrefService.h" |
|
29 #include "nsIPrefBranch.h" |
|
30 #include "nsIFile.h" |
|
31 #include "nsIOService.h" |
|
32 #include "nsDirectoryServiceDefs.h" |
|
33 #include "nsAppDirectoryServiceDefs.h" |
|
34 #include "nsThreadUtils.h" |
|
35 #include "nsProxyRelease.h" |
|
36 #include "nsVoidArray.h" |
|
37 #include "nsDeleteDir.h" |
|
38 #include "nsNetCID.h" |
|
39 #include <math.h> // for log() |
|
40 #include "mozilla/Services.h" |
|
41 #include "nsITimer.h" |
|
42 #include "mozIStorageService.h" |
|
43 |
|
44 #include "mozilla/net/NeckoCommon.h" |
|
45 #include "mozilla/VisualEventTracer.h" |
|
46 #include <algorithm> |
|
47 |
|
48 using namespace mozilla; |
|
49 |
|
50 /****************************************************************************** |
|
51 * nsCacheProfilePrefObserver |
|
52 *****************************************************************************/ |
|
53 #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable" |
|
54 #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory" |
|
55 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\ |
|
56 "browser.cache.disk.smart_size.first_run" |
|
57 #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \ |
|
58 "browser.cache.disk.smart_size.enabled" |
|
59 #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value" |
|
60 #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity" |
|
61 #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size" |
|
62 #define DISK_CACHE_CAPACITY 256000 |
|
63 |
|
64 #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \ |
|
65 "browser.cache.disk.smart_size.use_old_max" |
|
66 |
|
67 #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable" |
|
68 #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory" |
|
69 #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity" |
|
70 #define OFFLINE_CACHE_CAPACITY 512000 |
|
71 |
|
72 #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable" |
|
73 #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity" |
|
74 #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size" |
|
75 |
|
76 #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level" |
|
77 #define CACHE_COMPRESSION_LEVEL 1 |
|
78 |
|
79 #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown" |
|
80 #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache" |
|
81 |
|
82 static const char * observerList[] = { |
|
83 "profile-before-change", |
|
84 "profile-do-change", |
|
85 NS_XPCOM_SHUTDOWN_OBSERVER_ID, |
|
86 "last-pb-context-exited", |
|
87 "suspend_process_notification", |
|
88 "resume_process_notification" |
|
89 }; |
|
90 |
|
91 static const char * prefList[] = { |
|
92 DISK_CACHE_ENABLE_PREF, |
|
93 DISK_CACHE_SMART_SIZE_ENABLED_PREF, |
|
94 DISK_CACHE_CAPACITY_PREF, |
|
95 DISK_CACHE_DIR_PREF, |
|
96 DISK_CACHE_MAX_ENTRY_SIZE_PREF, |
|
97 DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, |
|
98 OFFLINE_CACHE_ENABLE_PREF, |
|
99 OFFLINE_CACHE_CAPACITY_PREF, |
|
100 OFFLINE_CACHE_DIR_PREF, |
|
101 MEMORY_CACHE_ENABLE_PREF, |
|
102 MEMORY_CACHE_CAPACITY_PREF, |
|
103 MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, |
|
104 CACHE_COMPRESSION_LEVEL_PREF, |
|
105 SANITIZE_ON_SHUTDOWN_PREF, |
|
106 CLEAR_ON_SHUTDOWN_PREF |
|
107 }; |
|
108 |
|
109 // Cache sizes, in KB |
|
110 const int32_t DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB |
|
111 #ifdef ANDROID |
|
112 const int32_t MAX_CACHE_SIZE = 200 * 1024; // 200 MB |
|
113 const int32_t OLD_MAX_CACHE_SIZE = 200 * 1024; // 200 MB |
|
114 #else |
|
115 const int32_t MAX_CACHE_SIZE = 350 * 1024; // 350 MB |
|
116 const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; // 1 GB |
|
117 #endif |
|
118 // Default cache size was 50 MB for many years until FF 4: |
|
119 const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024; |
|
120 |
|
121 class nsCacheProfilePrefObserver : public nsIObserver |
|
122 { |
|
123 public: |
|
124 NS_DECL_THREADSAFE_ISUPPORTS |
|
125 NS_DECL_NSIOBSERVER |
|
126 |
|
127 nsCacheProfilePrefObserver() |
|
128 : mHaveProfile(false) |
|
129 , mDiskCacheEnabled(false) |
|
130 , mDiskCacheCapacity(0) |
|
131 , mDiskCacheMaxEntrySize(-1) // -1 means "no limit" |
|
132 , mSmartSizeEnabled(false) |
|
133 , mShouldUseOldMaxSmartSize(false) |
|
134 , mOfflineCacheEnabled(false) |
|
135 , mOfflineCacheCapacity(0) |
|
136 , mMemoryCacheEnabled(true) |
|
137 , mMemoryCacheCapacity(-1) |
|
138 , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit" |
|
139 , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL) |
|
140 , mSanitizeOnShutdown(false) |
|
141 , mClearCacheOnShutdown(false) |
|
142 { |
|
143 } |
|
144 |
|
145 virtual ~nsCacheProfilePrefObserver() {} |
|
146 |
|
147 nsresult Install(); |
|
148 void Remove(); |
|
149 nsresult ReadPrefs(nsIPrefBranch* branch); |
|
150 |
|
151 bool DiskCacheEnabled(); |
|
152 int32_t DiskCacheCapacity() { return mDiskCacheCapacity; } |
|
153 void SetDiskCacheCapacity(int32_t); |
|
154 int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; } |
|
155 nsIFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; } |
|
156 bool SmartSizeEnabled() { return mSmartSizeEnabled; } |
|
157 |
|
158 bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize; } |
|
159 void SetUseNewMaxSmartSize(bool useNew) { mShouldUseOldMaxSmartSize = !useNew; } |
|
160 |
|
161 bool OfflineCacheEnabled(); |
|
162 int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity; } |
|
163 nsIFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; } |
|
164 |
|
165 bool MemoryCacheEnabled(); |
|
166 int32_t MemoryCacheCapacity(); |
|
167 int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize; } |
|
168 |
|
169 int32_t CacheCompressionLevel(); |
|
170 |
|
171 bool SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; } |
|
172 |
|
173 static uint32_t GetSmartCacheSize(const nsAString& cachePath, |
|
174 uint32_t currentSize, |
|
175 bool shouldUseOldMaxSmartSize); |
|
176 |
|
177 bool PermittedToSmartSize(nsIPrefBranch*, bool firstRun); |
|
178 |
|
179 private: |
|
180 bool mHaveProfile; |
|
181 |
|
182 bool mDiskCacheEnabled; |
|
183 int32_t mDiskCacheCapacity; // in kilobytes |
|
184 int32_t mDiskCacheMaxEntrySize; // in kilobytes |
|
185 nsCOMPtr<nsIFile> mDiskCacheParentDirectory; |
|
186 bool mSmartSizeEnabled; |
|
187 |
|
188 bool mShouldUseOldMaxSmartSize; |
|
189 |
|
190 bool mOfflineCacheEnabled; |
|
191 int32_t mOfflineCacheCapacity; // in kilobytes |
|
192 nsCOMPtr<nsIFile> mOfflineCacheParentDirectory; |
|
193 |
|
194 bool mMemoryCacheEnabled; |
|
195 int32_t mMemoryCacheCapacity; // in kilobytes |
|
196 int32_t mMemoryCacheMaxEntrySize; // in kilobytes |
|
197 |
|
198 int32_t mCacheCompressionLevel; |
|
199 |
|
200 bool mSanitizeOnShutdown; |
|
201 bool mClearCacheOnShutdown; |
|
202 }; |
|
203 |
|
204 NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver) |
|
205 |
|
206 class nsSetDiskSmartSizeCallback MOZ_FINAL : public nsITimerCallback |
|
207 { |
|
208 public: |
|
209 NS_DECL_THREADSAFE_ISUPPORTS |
|
210 |
|
211 NS_IMETHOD Notify(nsITimer* aTimer) { |
|
212 if (nsCacheService::gService) { |
|
213 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY)); |
|
214 nsCacheService::gService->SetDiskSmartSize_Locked(); |
|
215 nsCacheService::gService->mSmartSizeTimer = nullptr; |
|
216 } |
|
217 return NS_OK; |
|
218 } |
|
219 }; |
|
220 |
|
221 NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback) |
|
222 |
|
223 // Runnable sent to main thread after the cache IO thread calculates available |
|
224 // disk space, so that there is no race in setting mDiskCacheCapacity. |
|
225 class nsSetSmartSizeEvent: public nsRunnable |
|
226 { |
|
227 public: |
|
228 nsSetSmartSizeEvent(int32_t smartSize) |
|
229 : mSmartSize(smartSize) {} |
|
230 |
|
231 NS_IMETHOD Run() |
|
232 { |
|
233 NS_ASSERTION(NS_IsMainThread(), |
|
234 "Setting smart size data off the main thread"); |
|
235 |
|
236 // Main thread may have already called nsCacheService::Shutdown |
|
237 if (!nsCacheService::IsInitialized()) |
|
238 return NS_ERROR_NOT_AVAILABLE; |
|
239 |
|
240 // Ensure smart sizing wasn't switched off while event was pending. |
|
241 // It is safe to access the observer without the lock since we are |
|
242 // on the main thread and the value changes only on the main thread. |
|
243 if (!nsCacheService::gService->mObserver->SmartSizeEnabled()) |
|
244 return NS_OK; |
|
245 |
|
246 nsCacheService::SetDiskCacheCapacity(mSmartSize); |
|
247 |
|
248 nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
249 if (!ps || |
|
250 NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize))) |
|
251 NS_WARNING("Failed to set smart size pref"); |
|
252 |
|
253 return NS_OK; |
|
254 } |
|
255 |
|
256 private: |
|
257 int32_t mSmartSize; |
|
258 }; |
|
259 |
|
260 |
|
261 // Runnable sent from main thread to cacheIO thread |
|
262 class nsGetSmartSizeEvent: public nsRunnable |
|
263 { |
|
264 public: |
|
265 nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize, |
|
266 bool shouldUseOldMaxSmartSize) |
|
267 : mCachePath(cachePath) |
|
268 , mCurrentSize(currentSize) |
|
269 , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize) |
|
270 {} |
|
271 |
|
272 // Calculates user's disk space available on a background thread and |
|
273 // dispatches this value back to the main thread. |
|
274 NS_IMETHOD Run() |
|
275 { |
|
276 uint32_t size; |
|
277 size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath, |
|
278 mCurrentSize, |
|
279 mShouldUseOldMaxSmartSize); |
|
280 NS_DispatchToMainThread(new nsSetSmartSizeEvent(size)); |
|
281 return NS_OK; |
|
282 } |
|
283 |
|
284 private: |
|
285 nsString mCachePath; |
|
286 uint32_t mCurrentSize; |
|
287 bool mShouldUseOldMaxSmartSize; |
|
288 }; |
|
289 |
|
290 class nsBlockOnCacheThreadEvent : public nsRunnable { |
|
291 public: |
|
292 nsBlockOnCacheThreadEvent() |
|
293 { |
|
294 } |
|
295 NS_IMETHOD Run() |
|
296 { |
|
297 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN)); |
|
298 #ifdef PR_LOGGING |
|
299 CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this)); |
|
300 #endif |
|
301 nsCacheService::gService->mCondVar.Notify(); |
|
302 return NS_OK; |
|
303 } |
|
304 }; |
|
305 |
|
306 |
|
307 nsresult |
|
308 nsCacheProfilePrefObserver::Install() |
|
309 { |
|
310 // install profile-change observer |
|
311 nsCOMPtr<nsIObserverService> observerService = |
|
312 mozilla::services::GetObserverService(); |
|
313 if (!observerService) |
|
314 return NS_ERROR_FAILURE; |
|
315 |
|
316 nsresult rv, rv2 = NS_OK; |
|
317 for (unsigned int i=0; i<ArrayLength(observerList); i++) { |
|
318 rv = observerService->AddObserver(this, observerList[i], false); |
|
319 if (NS_FAILED(rv)) |
|
320 rv2 = rv; |
|
321 } |
|
322 |
|
323 // install preferences observer |
|
324 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
325 if (!branch) return NS_ERROR_FAILURE; |
|
326 |
|
327 for (unsigned int i=0; i<ArrayLength(prefList); i++) { |
|
328 rv = branch->AddObserver(prefList[i], this, false); |
|
329 if (NS_FAILED(rv)) |
|
330 rv2 = rv; |
|
331 } |
|
332 |
|
333 // Determine if we have a profile already |
|
334 // Install() is called *after* the profile-after-change notification |
|
335 // when there is only a single profile, or it is specified on the |
|
336 // commandline at startup. |
|
337 // In that case, we detect the presence of a profile by the existence |
|
338 // of the NS_APP_USER_PROFILE_50_DIR directory. |
|
339 |
|
340 nsCOMPtr<nsIFile> directory; |
|
341 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
342 getter_AddRefs(directory)); |
|
343 if (NS_SUCCEEDED(rv)) |
|
344 mHaveProfile = true; |
|
345 |
|
346 rv = ReadPrefs(branch); |
|
347 NS_ENSURE_SUCCESS(rv, rv); |
|
348 |
|
349 return rv2; |
|
350 } |
|
351 |
|
352 |
|
353 void |
|
354 nsCacheProfilePrefObserver::Remove() |
|
355 { |
|
356 // remove Observer Service observers |
|
357 nsCOMPtr<nsIObserverService> obs = |
|
358 mozilla::services::GetObserverService(); |
|
359 if (obs) { |
|
360 for (unsigned int i=0; i<ArrayLength(observerList); i++) { |
|
361 obs->RemoveObserver(this, observerList[i]); |
|
362 } |
|
363 } |
|
364 |
|
365 // remove Pref Service observers |
|
366 nsCOMPtr<nsIPrefBranch> prefs = |
|
367 do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
368 if (!prefs) |
|
369 return; |
|
370 for (unsigned int i=0; i<ArrayLength(prefList); i++) |
|
371 prefs->RemoveObserver(prefList[i], this); // remove cache pref observers |
|
372 } |
|
373 |
|
374 void |
|
375 nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity) |
|
376 { |
|
377 mDiskCacheCapacity = std::max(0, capacity); |
|
378 } |
|
379 |
|
380 |
|
381 NS_IMETHODIMP |
|
382 nsCacheProfilePrefObserver::Observe(nsISupports * subject, |
|
383 const char * topic, |
|
384 const char16_t * data_unicode) |
|
385 { |
|
386 nsresult rv; |
|
387 NS_ConvertUTF16toUTF8 data(data_unicode); |
|
388 CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic, data.get())); |
|
389 |
|
390 if (!nsCacheService::IsInitialized()) { |
|
391 if (!strcmp("resume_process_notification", topic)) { |
|
392 // A suspended process has a closed cache, so re-open it here. |
|
393 nsCacheService::GlobalInstance()->Init(); |
|
394 } |
|
395 return NS_OK; |
|
396 } |
|
397 |
|
398 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) { |
|
399 // xpcom going away, shutdown cache service |
|
400 nsCacheService::GlobalInstance()->Shutdown(); |
|
401 } else if (!strcmp("profile-before-change", topic)) { |
|
402 // profile before change |
|
403 mHaveProfile = false; |
|
404 |
|
405 // XXX shutdown devices |
|
406 nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse", |
|
407 data.get())); |
|
408 |
|
409 } else if (!strcmp("suspend_process_notification", topic)) { |
|
410 // A suspended process may never return, so shutdown the cache to reduce |
|
411 // cache corruption. |
|
412 nsCacheService::GlobalInstance()->Shutdown(); |
|
413 } else if (!strcmp("profile-do-change", topic)) { |
|
414 // profile after change |
|
415 mHaveProfile = true; |
|
416 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
417 ReadPrefs(branch); |
|
418 nsCacheService::OnProfileChanged(); |
|
419 |
|
420 } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) { |
|
421 |
|
422 // ignore pref changes until we're done switch profiles |
|
423 if (!mHaveProfile) |
|
424 return NS_OK; |
|
425 |
|
426 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv); |
|
427 if (NS_FAILED(rv)) |
|
428 return rv; |
|
429 |
|
430 // which preference changed? |
|
431 if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) { |
|
432 |
|
433 rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, |
|
434 &mDiskCacheEnabled); |
|
435 if (NS_FAILED(rv)) |
|
436 return rv; |
|
437 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled()); |
|
438 |
|
439 } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) { |
|
440 |
|
441 int32_t capacity = 0; |
|
442 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity); |
|
443 if (NS_FAILED(rv)) |
|
444 return rv; |
|
445 mDiskCacheCapacity = std::max(0, capacity); |
|
446 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity); |
|
447 |
|
448 // Update the cache capacity when smart sizing is turned on/off |
|
449 } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) { |
|
450 // Is the update because smartsizing was turned on, or off? |
|
451 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, |
|
452 &mSmartSizeEnabled); |
|
453 if (NS_FAILED(rv)) |
|
454 return rv; |
|
455 int32_t newCapacity = 0; |
|
456 if (mSmartSizeEnabled) { |
|
457 nsCacheService::SetDiskSmartSize(); |
|
458 } else { |
|
459 // Smart sizing switched off: use user specified size |
|
460 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity); |
|
461 if (NS_FAILED(rv)) |
|
462 return rv; |
|
463 mDiskCacheCapacity = std::max(0, newCapacity); |
|
464 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity); |
|
465 } |
|
466 } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) { |
|
467 rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, |
|
468 &mShouldUseOldMaxSmartSize); |
|
469 if (NS_FAILED(rv)) |
|
470 return rv; |
|
471 } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) { |
|
472 int32_t newMaxSize; |
|
473 rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF, |
|
474 &newMaxSize); |
|
475 if (NS_FAILED(rv)) |
|
476 return rv; |
|
477 |
|
478 mDiskCacheMaxEntrySize = std::max(-1, newMaxSize); |
|
479 nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize); |
|
480 |
|
481 #if 0 |
|
482 } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) { |
|
483 // XXX We probaby don't want to respond to this pref except after |
|
484 // XXX profile changes. Ideally, there should be somekind of user |
|
485 // XXX notification that the pref change won't take effect until |
|
486 // XXX the next time the profile changes (browser launch) |
|
487 #endif |
|
488 } else |
|
489 |
|
490 // which preference changed? |
|
491 if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) { |
|
492 |
|
493 rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF, |
|
494 &mOfflineCacheEnabled); |
|
495 if (NS_FAILED(rv)) return rv; |
|
496 nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled()); |
|
497 |
|
498 } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) { |
|
499 |
|
500 int32_t capacity = 0; |
|
501 rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity); |
|
502 if (NS_FAILED(rv)) return rv; |
|
503 mOfflineCacheCapacity = std::max(0, capacity); |
|
504 nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity); |
|
505 #if 0 |
|
506 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) { |
|
507 // XXX We probaby don't want to respond to this pref except after |
|
508 // XXX profile changes. Ideally, there should be some kind of user |
|
509 // XXX notification that the pref change won't take effect until |
|
510 // XXX the next time the profile changes (browser launch) |
|
511 #endif |
|
512 } else |
|
513 |
|
514 if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) { |
|
515 |
|
516 rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, |
|
517 &mMemoryCacheEnabled); |
|
518 if (NS_FAILED(rv)) |
|
519 return rv; |
|
520 nsCacheService::SetMemoryCache(); |
|
521 |
|
522 } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) { |
|
523 |
|
524 mMemoryCacheCapacity = -1; |
|
525 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF, |
|
526 &mMemoryCacheCapacity); |
|
527 nsCacheService::SetMemoryCache(); |
|
528 } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) { |
|
529 int32_t newMaxSize; |
|
530 rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, |
|
531 &newMaxSize); |
|
532 if (NS_FAILED(rv)) |
|
533 return rv; |
|
534 |
|
535 mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize); |
|
536 nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize); |
|
537 } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) { |
|
538 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL; |
|
539 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF, |
|
540 &mCacheCompressionLevel); |
|
541 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel); |
|
542 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel); |
|
543 } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) { |
|
544 rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF, |
|
545 &mSanitizeOnShutdown); |
|
546 if (NS_FAILED(rv)) |
|
547 return rv; |
|
548 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled()); |
|
549 } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) { |
|
550 rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF, |
|
551 &mClearCacheOnShutdown); |
|
552 if (NS_FAILED(rv)) |
|
553 return rv; |
|
554 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled()); |
|
555 } |
|
556 } else if (!strcmp("last-pb-context-exited", topic)) { |
|
557 nsCacheService::LeavePrivateBrowsing(); |
|
558 } |
|
559 |
|
560 return NS_OK; |
|
561 } |
|
562 |
|
563 // Returns default ("smart") size (in KB) of cache, given available disk space |
|
564 // (also in KB) |
|
565 static uint32_t |
|
566 SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize) |
|
567 { |
|
568 uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE; |
|
569 |
|
570 if (availKB > 100 * 1024 * 1024) |
|
571 return maxSize; // skip computing if we're over 100 GB |
|
572 |
|
573 // Grow/shrink in 10 MB units, deliberately, so that in the common case we |
|
574 // don't shrink cache and evict items every time we startup (it's important |
|
575 // that we don't slow down startup benchmarks). |
|
576 uint32_t sz10MBs = 0; |
|
577 uint32_t avail10MBs = availKB / (1024*10); |
|
578 |
|
579 // .5% of space above 25 GB |
|
580 if (avail10MBs > 2500) { |
|
581 sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005); |
|
582 avail10MBs = 2500; |
|
583 } |
|
584 // 1% of space between 7GB -> 25 GB |
|
585 if (avail10MBs > 700) { |
|
586 sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01); |
|
587 avail10MBs = 700; |
|
588 } |
|
589 // 5% of space between 500 MB -> 7 GB |
|
590 if (avail10MBs > 50) { |
|
591 sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05); |
|
592 avail10MBs = 50; |
|
593 } |
|
594 |
|
595 #ifdef ANDROID |
|
596 // On Android, smaller/older devices may have very little storage and |
|
597 // device owners may be sensitive to storage footprint: Use a smaller |
|
598 // percentage of available space and a smaller minimum. |
|
599 |
|
600 // 20% of space up to 500 MB (10 MB min) |
|
601 sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2)); |
|
602 #else |
|
603 // 40% of space up to 500 MB (50 MB min) |
|
604 sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4)); |
|
605 #endif |
|
606 |
|
607 return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024); |
|
608 } |
|
609 |
|
610 /* Computes our best guess for the default size of the user's disk cache, |
|
611 * based on the amount of space they have free on their hard drive. |
|
612 * We use a tiered scheme: the more space available, |
|
613 * the larger the disk cache will be. However, we do not want |
|
614 * to enable the disk cache to grow to an unbounded size, so the larger the |
|
615 * user's available space is, the smaller of a percentage we take. We set a |
|
616 * lower bound of 50MB and an upper bound of 1GB. |
|
617 * |
|
618 *@param: None. |
|
619 *@return: The size that the user's disk cache should default to, in kBytes. |
|
620 */ |
|
621 uint32_t |
|
622 nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath, |
|
623 uint32_t currentSize, |
|
624 bool shouldUseOldMaxSmartSize) |
|
625 { |
|
626 // Check for free space on device where cache directory lives |
|
627 nsresult rv; |
|
628 nsCOMPtr<nsIFile> |
|
629 cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); |
|
630 if (NS_FAILED(rv) || !cacheDirectory) |
|
631 return DEFAULT_CACHE_SIZE; |
|
632 rv = cacheDirectory->InitWithPath(cachePath); |
|
633 if (NS_FAILED(rv)) |
|
634 return DEFAULT_CACHE_SIZE; |
|
635 int64_t bytesAvailable; |
|
636 rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable); |
|
637 if (NS_FAILED(rv)) |
|
638 return DEFAULT_CACHE_SIZE; |
|
639 |
|
640 return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) + |
|
641 currentSize), |
|
642 shouldUseOldMaxSmartSize); |
|
643 } |
|
644 |
|
645 /* Determine if we are permitted to dynamically size the user's disk cache based |
|
646 * on their disk space available. We may do this so long as the pref |
|
647 * smart_size.enabled is true. |
|
648 */ |
|
649 bool |
|
650 nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool |
|
651 firstRun) |
|
652 { |
|
653 nsresult rv; |
|
654 if (firstRun) { |
|
655 // check if user has set cache size in the past |
|
656 bool userSet; |
|
657 rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet); |
|
658 if (NS_FAILED(rv)) userSet = true; |
|
659 if (userSet) { |
|
660 int32_t oldCapacity; |
|
661 // If user explicitly set cache size to be smaller than old default |
|
662 // of 50 MB, then keep user's value. Otherwise use smart sizing. |
|
663 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity); |
|
664 if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) { |
|
665 mSmartSizeEnabled = false; |
|
666 branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, |
|
667 mSmartSizeEnabled); |
|
668 return mSmartSizeEnabled; |
|
669 } |
|
670 } |
|
671 // Set manual setting to MAX cache size as starting val for any |
|
672 // adjustment by user: (bug 559942 comment 65) |
|
673 int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE; |
|
674 branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize); |
|
675 } |
|
676 |
|
677 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF, |
|
678 &mSmartSizeEnabled); |
|
679 if (NS_FAILED(rv)) |
|
680 mSmartSizeEnabled = false; |
|
681 return mSmartSizeEnabled; |
|
682 } |
|
683 |
|
684 |
|
685 nsresult |
|
686 nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch) |
|
687 { |
|
688 nsresult rv = NS_OK; |
|
689 |
|
690 // read disk cache device prefs |
|
691 mDiskCacheEnabled = true; // presume disk cache is enabled |
|
692 (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled); |
|
693 |
|
694 mDiskCacheCapacity = DISK_CACHE_CAPACITY; |
|
695 (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity); |
|
696 mDiskCacheCapacity = std::max(0, mDiskCacheCapacity); |
|
697 |
|
698 (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF, |
|
699 &mDiskCacheMaxEntrySize); |
|
700 mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize); |
|
701 |
|
702 (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF, // ignore error |
|
703 NS_GET_IID(nsIFile), |
|
704 getter_AddRefs(mDiskCacheParentDirectory)); |
|
705 |
|
706 (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, |
|
707 &mShouldUseOldMaxSmartSize); |
|
708 |
|
709 if (!mDiskCacheParentDirectory) { |
|
710 nsCOMPtr<nsIFile> directory; |
|
711 |
|
712 // try to get the disk cache parent directory |
|
713 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR, |
|
714 getter_AddRefs(directory)); |
|
715 if (NS_FAILED(rv)) { |
|
716 // try to get the profile directory (there may not be a profile yet) |
|
717 nsCOMPtr<nsIFile> profDir; |
|
718 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
719 getter_AddRefs(profDir)); |
|
720 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, |
|
721 getter_AddRefs(directory)); |
|
722 if (!directory) |
|
723 directory = profDir; |
|
724 else if (profDir) { |
|
725 nsCacheService::MoveOrRemoveDiskCache(profDir, directory, |
|
726 "Cache"); |
|
727 } |
|
728 } |
|
729 // use file cache in build tree only if asked, to avoid cache dir litter |
|
730 if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) { |
|
731 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, |
|
732 getter_AddRefs(directory)); |
|
733 } |
|
734 if (directory) |
|
735 mDiskCacheParentDirectory = do_QueryInterface(directory, &rv); |
|
736 } |
|
737 if (mDiskCacheParentDirectory) { |
|
738 bool firstSmartSizeRun; |
|
739 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF, |
|
740 &firstSmartSizeRun); |
|
741 if (NS_FAILED(rv)) |
|
742 firstSmartSizeRun = false; |
|
743 if (PermittedToSmartSize(branch, firstSmartSizeRun)) { |
|
744 // Avoid evictions: use previous cache size until smart size event |
|
745 // updates mDiskCacheCapacity |
|
746 rv = branch->GetIntPref(firstSmartSizeRun ? |
|
747 DISK_CACHE_CAPACITY_PREF : |
|
748 DISK_CACHE_SMART_SIZE_PREF, |
|
749 &mDiskCacheCapacity); |
|
750 if (NS_FAILED(rv)) |
|
751 mDiskCacheCapacity = DEFAULT_CACHE_SIZE; |
|
752 } |
|
753 |
|
754 if (firstSmartSizeRun) { |
|
755 // It is no longer our first run |
|
756 rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF, |
|
757 false); |
|
758 if (NS_FAILED(rv)) |
|
759 NS_WARNING("Failed setting first_run pref in ReadPrefs."); |
|
760 } |
|
761 } |
|
762 |
|
763 // read offline cache device prefs |
|
764 mOfflineCacheEnabled = true; // presume offline cache is enabled |
|
765 (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF, |
|
766 &mOfflineCacheEnabled); |
|
767 |
|
768 mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY; |
|
769 (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, |
|
770 &mOfflineCacheCapacity); |
|
771 mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity); |
|
772 |
|
773 (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error |
|
774 NS_GET_IID(nsIFile), |
|
775 getter_AddRefs(mOfflineCacheParentDirectory)); |
|
776 |
|
777 if (!mOfflineCacheParentDirectory) { |
|
778 nsCOMPtr<nsIFile> directory; |
|
779 |
|
780 // try to get the offline cache parent directory |
|
781 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR, |
|
782 getter_AddRefs(directory)); |
|
783 if (NS_FAILED(rv)) { |
|
784 // try to get the profile directory (there may not be a profile yet) |
|
785 nsCOMPtr<nsIFile> profDir; |
|
786 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
787 getter_AddRefs(profDir)); |
|
788 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, |
|
789 getter_AddRefs(directory)); |
|
790 if (!directory) |
|
791 directory = profDir; |
|
792 else if (profDir) { |
|
793 nsCacheService::MoveOrRemoveDiskCache(profDir, directory, |
|
794 "OfflineCache"); |
|
795 } |
|
796 } |
|
797 #if DEBUG |
|
798 if (!directory) { |
|
799 // use current process directory during development |
|
800 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, |
|
801 getter_AddRefs(directory)); |
|
802 } |
|
803 #endif |
|
804 if (directory) |
|
805 mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv); |
|
806 } |
|
807 |
|
808 // read memory cache device prefs |
|
809 (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled); |
|
810 |
|
811 mMemoryCacheCapacity = -1; |
|
812 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF, |
|
813 &mMemoryCacheCapacity); |
|
814 |
|
815 (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, |
|
816 &mMemoryCacheMaxEntrySize); |
|
817 mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize); |
|
818 |
|
819 // read cache compression level pref |
|
820 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL; |
|
821 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF, |
|
822 &mCacheCompressionLevel); |
|
823 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel); |
|
824 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel); |
|
825 |
|
826 // read cache shutdown sanitization prefs |
|
827 (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF, |
|
828 &mSanitizeOnShutdown); |
|
829 (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF, |
|
830 &mClearCacheOnShutdown); |
|
831 |
|
832 return rv; |
|
833 } |
|
834 |
|
835 nsresult |
|
836 nsCacheService::DispatchToCacheIOThread(nsIRunnable* event) |
|
837 { |
|
838 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE; |
|
839 return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL); |
|
840 } |
|
841 |
|
842 nsresult |
|
843 nsCacheService::SyncWithCacheIOThread() |
|
844 { |
|
845 gService->mLock.AssertCurrentThreadOwns(); |
|
846 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE; |
|
847 |
|
848 nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent(); |
|
849 |
|
850 // dispatch event - it will notify the monitor when it's done |
|
851 nsresult rv = |
|
852 gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL); |
|
853 if (NS_FAILED(rv)) { |
|
854 NS_WARNING("Failed dispatching block-event"); |
|
855 return NS_ERROR_UNEXPECTED; |
|
856 } |
|
857 |
|
858 // wait until notified, then return |
|
859 rv = gService->mCondVar.Wait(); |
|
860 |
|
861 return rv; |
|
862 } |
|
863 |
|
864 |
|
865 bool |
|
866 nsCacheProfilePrefObserver::DiskCacheEnabled() |
|
867 { |
|
868 if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory)) return false; |
|
869 return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown); |
|
870 } |
|
871 |
|
872 |
|
873 bool |
|
874 nsCacheProfilePrefObserver::OfflineCacheEnabled() |
|
875 { |
|
876 if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory)) |
|
877 return false; |
|
878 |
|
879 return mOfflineCacheEnabled; |
|
880 } |
|
881 |
|
882 |
|
883 bool |
|
884 nsCacheProfilePrefObserver::MemoryCacheEnabled() |
|
885 { |
|
886 if (mMemoryCacheCapacity == 0) return false; |
|
887 return mMemoryCacheEnabled; |
|
888 } |
|
889 |
|
890 |
|
891 /** |
|
892 * MemoryCacheCapacity |
|
893 * |
|
894 * If the browser.cache.memory.capacity preference is positive, we use that |
|
895 * value for the amount of memory available for the cache. |
|
896 * |
|
897 * If browser.cache.memory.capacity is zero, the memory cache is disabled. |
|
898 * |
|
899 * If browser.cache.memory.capacity is negative or not present, we use a |
|
900 * formula that grows less than linearly with the amount of system memory, |
|
901 * with an upper limit on the cache size. No matter how much physical RAM is |
|
902 * present, the default cache size would not exceed 32 MB. This maximum would |
|
903 * apply only to systems with more than 4 GB of RAM (e.g. terminal servers) |
|
904 * |
|
905 * RAM Cache |
|
906 * --- ----- |
|
907 * 32 Mb 2 Mb |
|
908 * 64 Mb 4 Mb |
|
909 * 128 Mb 6 Mb |
|
910 * 256 Mb 10 Mb |
|
911 * 512 Mb 14 Mb |
|
912 * 1024 Mb 18 Mb |
|
913 * 2048 Mb 24 Mb |
|
914 * 4096 Mb 30 Mb |
|
915 * |
|
916 * The equation for this is (for cache size C and memory size K (kbytes)): |
|
917 * x = log2(K) - 14 |
|
918 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding) |
|
919 * if (C > 32) C = 32 |
|
920 */ |
|
921 |
|
922 int32_t |
|
923 nsCacheProfilePrefObserver::MemoryCacheCapacity() |
|
924 { |
|
925 int32_t capacity = mMemoryCacheCapacity; |
|
926 if (capacity >= 0) { |
|
927 CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity)); |
|
928 return capacity; |
|
929 } |
|
930 |
|
931 static uint64_t bytes = PR_GetPhysicalMemorySize(); |
|
932 CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes)); |
|
933 |
|
934 // If getting the physical memory failed, arbitrarily assume |
|
935 // 32 MB of RAM. We use a low default to have a reasonable |
|
936 // size on all the devices we support. |
|
937 if (bytes == 0) |
|
938 bytes = 32 * 1024 * 1024; |
|
939 |
|
940 // Conversion from unsigned int64_t to double doesn't work on all platforms. |
|
941 // We need to truncate the value at INT64_MAX to make sure we don't |
|
942 // overflow. |
|
943 if (bytes > INT64_MAX) |
|
944 bytes = INT64_MAX; |
|
945 |
|
946 uint64_t kbytes = bytes >> 10; |
|
947 |
|
948 double kBytesD = double(kbytes); |
|
949 |
|
950 double x = log(kBytesD)/log(2.0) - 14; |
|
951 if (x > 0) { |
|
952 capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding |
|
953 if (capacity > 32) |
|
954 capacity = 32; |
|
955 capacity *= 1024; |
|
956 } else { |
|
957 capacity = 0; |
|
958 } |
|
959 |
|
960 return capacity; |
|
961 } |
|
962 |
|
963 int32_t |
|
964 nsCacheProfilePrefObserver::CacheCompressionLevel() |
|
965 { |
|
966 return mCacheCompressionLevel; |
|
967 } |
|
968 |
|
969 /****************************************************************************** |
|
970 * nsProcessRequestEvent |
|
971 *****************************************************************************/ |
|
972 |
|
973 class nsProcessRequestEvent : public nsRunnable { |
|
974 public: |
|
975 nsProcessRequestEvent(nsCacheRequest *aRequest) |
|
976 { |
|
977 MOZ_EVENT_TRACER_NAME_OBJECT(aRequest, aRequest->mKey.get()); |
|
978 MOZ_EVENT_TRACER_WAIT(aRequest, "net::cache::ProcessRequest"); |
|
979 mRequest = aRequest; |
|
980 } |
|
981 |
|
982 NS_IMETHOD Run() |
|
983 { |
|
984 nsresult rv; |
|
985 |
|
986 NS_ASSERTION(mRequest->mListener, |
|
987 "Sync OpenCacheEntry() posted to background thread!"); |
|
988 |
|
989 nsCacheServiceAutoLock lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN)); |
|
990 rv = nsCacheService::gService->ProcessRequest(mRequest, |
|
991 false, |
|
992 nullptr); |
|
993 |
|
994 // Don't delete the request if it was queued |
|
995 if (!(mRequest->IsBlocking() && |
|
996 rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)) |
|
997 delete mRequest; |
|
998 |
|
999 return NS_OK; |
|
1000 } |
|
1001 |
|
1002 protected: |
|
1003 virtual ~nsProcessRequestEvent() {} |
|
1004 |
|
1005 private: |
|
1006 nsCacheRequest *mRequest; |
|
1007 }; |
|
1008 |
|
1009 /****************************************************************************** |
|
1010 * nsDoomEvent |
|
1011 *****************************************************************************/ |
|
1012 |
|
1013 class nsDoomEvent : public nsRunnable { |
|
1014 public: |
|
1015 nsDoomEvent(nsCacheSession *session, |
|
1016 const nsACString &key, |
|
1017 nsICacheListener *listener) |
|
1018 { |
|
1019 mKey = *session->ClientID(); |
|
1020 mKey.Append(':'); |
|
1021 mKey.Append(key); |
|
1022 mStoragePolicy = session->StoragePolicy(); |
|
1023 mListener = listener; |
|
1024 mThread = do_GetCurrentThread(); |
|
1025 // We addref the listener here and release it in nsNotifyDoomListener |
|
1026 // on the callers thread. If posting of nsNotifyDoomListener event fails |
|
1027 // we leak the listener which is better than releasing it on a wrong |
|
1028 // thread. |
|
1029 NS_IF_ADDREF(mListener); |
|
1030 } |
|
1031 |
|
1032 NS_IMETHOD Run() |
|
1033 { |
|
1034 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDOOMEVENT_RUN)); |
|
1035 |
|
1036 bool foundActive = true; |
|
1037 nsresult status = NS_ERROR_NOT_AVAILABLE; |
|
1038 nsCacheEntry *entry; |
|
1039 entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey); |
|
1040 if (!entry) { |
|
1041 bool collision = false; |
|
1042 foundActive = false; |
|
1043 entry = nsCacheService::gService->SearchCacheDevices(&mKey, |
|
1044 mStoragePolicy, |
|
1045 &collision); |
|
1046 } |
|
1047 |
|
1048 if (entry) { |
|
1049 status = NS_OK; |
|
1050 nsCacheService::gService->DoomEntry_Internal(entry, foundActive); |
|
1051 } |
|
1052 |
|
1053 if (mListener) { |
|
1054 mThread->Dispatch(new nsNotifyDoomListener(mListener, status), |
|
1055 NS_DISPATCH_NORMAL); |
|
1056 // posted event will release the reference on the correct thread |
|
1057 mListener = nullptr; |
|
1058 } |
|
1059 |
|
1060 return NS_OK; |
|
1061 } |
|
1062 |
|
1063 private: |
|
1064 nsCString mKey; |
|
1065 nsCacheStoragePolicy mStoragePolicy; |
|
1066 nsICacheListener *mListener; |
|
1067 nsCOMPtr<nsIThread> mThread; |
|
1068 }; |
|
1069 |
|
1070 /****************************************************************************** |
|
1071 * nsCacheService |
|
1072 *****************************************************************************/ |
|
1073 nsCacheService * nsCacheService::gService = nullptr; |
|
1074 |
|
1075 NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal, |
|
1076 nsIMemoryReporter) |
|
1077 |
|
1078 nsCacheService::nsCacheService() |
|
1079 : mObserver(nullptr), |
|
1080 mLock("nsCacheService.mLock"), |
|
1081 mCondVar(mLock, "nsCacheService.mCondVar"), |
|
1082 mTimeStampLock("nsCacheService.mTimeStampLock"), |
|
1083 mInitialized(false), |
|
1084 mClearingEntries(false), |
|
1085 mEnableMemoryDevice(true), |
|
1086 mEnableDiskDevice(true), |
|
1087 mMemoryDevice(nullptr), |
|
1088 mDiskDevice(nullptr), |
|
1089 mOfflineDevice(nullptr), |
|
1090 mTotalEntries(0), |
|
1091 mCacheHits(0), |
|
1092 mCacheMisses(0), |
|
1093 mMaxKeyLength(0), |
|
1094 mMaxDataSize(0), |
|
1095 mMaxMetaSize(0), |
|
1096 mDeactivateFailures(0), |
|
1097 mDeactivatedUnboundEntries(0) |
|
1098 { |
|
1099 NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!"); |
|
1100 gService = this; |
|
1101 |
|
1102 // create list of cache devices |
|
1103 PR_INIT_CLIST(&mDoomedEntries); |
|
1104 } |
|
1105 |
|
1106 nsCacheService::~nsCacheService() |
|
1107 { |
|
1108 if (mInitialized) // Shutdown hasn't been called yet. |
|
1109 (void) Shutdown(); |
|
1110 |
|
1111 if (mObserver) { |
|
1112 mObserver->Remove(); |
|
1113 NS_RELEASE(mObserver); |
|
1114 } |
|
1115 |
|
1116 gService = nullptr; |
|
1117 } |
|
1118 |
|
1119 |
|
1120 nsresult |
|
1121 nsCacheService::Init() |
|
1122 { |
|
1123 // Thie method must be called on the main thread because mCacheIOThread must |
|
1124 // only be modified on the main thread. |
|
1125 if (!NS_IsMainThread()) { |
|
1126 NS_ERROR("nsCacheService::Init called off the main thread"); |
|
1127 return NS_ERROR_NOT_SAME_THREAD; |
|
1128 } |
|
1129 |
|
1130 NS_ASSERTION(!mInitialized, "nsCacheService already initialized."); |
|
1131 if (mInitialized) |
|
1132 return NS_ERROR_ALREADY_INITIALIZED; |
|
1133 |
|
1134 if (mozilla::net::IsNeckoChild()) { |
|
1135 return NS_ERROR_UNEXPECTED; |
|
1136 } |
|
1137 |
|
1138 CACHE_LOG_INIT(); |
|
1139 |
|
1140 nsresult rv; |
|
1141 |
|
1142 mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv); |
|
1143 NS_ENSURE_SUCCESS(rv, rv); |
|
1144 |
|
1145 MOZ_EVENT_TRACER_NAME_OBJECT(nsCacheService::gService, "nsCacheService"); |
|
1146 |
|
1147 rv = NS_NewNamedThread("Cache I/O", |
|
1148 getter_AddRefs(mCacheIOThread)); |
|
1149 if (NS_FAILED(rv)) { |
|
1150 NS_RUNTIMEABORT("Can't create cache IO thread"); |
|
1151 } |
|
1152 |
|
1153 rv = nsDeleteDir::Init(); |
|
1154 if (NS_FAILED(rv)) { |
|
1155 NS_WARNING("Can't initialize nsDeleteDir"); |
|
1156 } |
|
1157 |
|
1158 // initialize hashtable for active cache entries |
|
1159 rv = mActiveEntries.Init(); |
|
1160 if (NS_FAILED(rv)) return rv; |
|
1161 |
|
1162 // create profile/preference observer |
|
1163 if (!mObserver) { |
|
1164 mObserver = new nsCacheProfilePrefObserver(); |
|
1165 NS_ADDREF(mObserver); |
|
1166 mObserver->Install(); |
|
1167 } |
|
1168 |
|
1169 mEnableDiskDevice = mObserver->DiskCacheEnabled(); |
|
1170 mEnableOfflineDevice = mObserver->OfflineCacheEnabled(); |
|
1171 mEnableMemoryDevice = mObserver->MemoryCacheEnabled(); |
|
1172 |
|
1173 RegisterWeakMemoryReporter(this); |
|
1174 |
|
1175 mInitialized = true; |
|
1176 return NS_OK; |
|
1177 } |
|
1178 |
|
1179 // static |
|
1180 PLDHashOperator |
|
1181 nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir, |
|
1182 nsRefPtr<nsOfflineCacheDevice>& aDevice, |
|
1183 void* aUserArg) |
|
1184 { |
|
1185 aDevice->Shutdown(); |
|
1186 return PL_DHASH_REMOVE; |
|
1187 } |
|
1188 |
|
1189 void |
|
1190 nsCacheService::Shutdown() |
|
1191 { |
|
1192 // This method must be called on the main thread because mCacheIOThread must |
|
1193 // only be modified on the main thread. |
|
1194 if (!NS_IsMainThread()) { |
|
1195 NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread"); |
|
1196 } |
|
1197 |
|
1198 nsCOMPtr<nsIThread> cacheIOThread; |
|
1199 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer; |
|
1200 |
|
1201 bool shouldSanitize = false; |
|
1202 nsCOMPtr<nsIFile> parentDir; |
|
1203 |
|
1204 { |
|
1205 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN)); |
|
1206 NS_ASSERTION(mInitialized, |
|
1207 "can't shutdown nsCacheService unless it has been initialized."); |
|
1208 if (!mInitialized) |
|
1209 return; |
|
1210 |
|
1211 mClearingEntries = true; |
|
1212 DoomActiveEntries(nullptr); |
|
1213 } |
|
1214 |
|
1215 CloseAllStreams(); |
|
1216 |
|
1217 UnregisterWeakMemoryReporter(this); |
|
1218 |
|
1219 { |
|
1220 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN)); |
|
1221 NS_ASSERTION(mInitialized, "Bad state"); |
|
1222 |
|
1223 mInitialized = false; |
|
1224 |
|
1225 // Clear entries |
|
1226 ClearDoomList(); |
|
1227 |
|
1228 if (mSmartSizeTimer) { |
|
1229 mSmartSizeTimer->Cancel(); |
|
1230 mSmartSizeTimer = nullptr; |
|
1231 } |
|
1232 |
|
1233 // Make sure to wait for any pending cache-operations before |
|
1234 // proceeding with destructive actions (bug #620660) |
|
1235 (void) SyncWithCacheIOThread(); |
|
1236 |
|
1237 // obtain the disk cache directory in case we need to sanitize it |
|
1238 parentDir = mObserver->DiskCacheParentDirectory(); |
|
1239 shouldSanitize = mObserver->SanitizeAtShutdown(); |
|
1240 |
|
1241 // deallocate memory and disk caches |
|
1242 delete mMemoryDevice; |
|
1243 mMemoryDevice = nullptr; |
|
1244 |
|
1245 delete mDiskDevice; |
|
1246 mDiskDevice = nullptr; |
|
1247 |
|
1248 if (mOfflineDevice) |
|
1249 mOfflineDevice->Shutdown(); |
|
1250 |
|
1251 NS_IF_RELEASE(mOfflineDevice); |
|
1252 |
|
1253 mCustomOfflineDevices.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr); |
|
1254 |
|
1255 #ifdef PR_LOGGING |
|
1256 LogCacheStatistics(); |
|
1257 #endif |
|
1258 |
|
1259 mClearingEntries = false; |
|
1260 mCacheIOThread.swap(cacheIOThread); |
|
1261 } |
|
1262 |
|
1263 if (cacheIOThread) |
|
1264 nsShutdownThread::BlockingShutdown(cacheIOThread); |
|
1265 |
|
1266 if (shouldSanitize) { |
|
1267 nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache")); |
|
1268 if (NS_SUCCEEDED(rv)) { |
|
1269 bool exists; |
|
1270 if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists) |
|
1271 nsDeleteDir::DeleteDir(parentDir, false); |
|
1272 } |
|
1273 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer; |
|
1274 nsDeleteDir::Shutdown(shouldSanitize); |
|
1275 } else { |
|
1276 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer; |
|
1277 nsDeleteDir::Shutdown(shouldSanitize); |
|
1278 } |
|
1279 } |
|
1280 |
|
1281 |
|
1282 nsresult |
|
1283 nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) |
|
1284 { |
|
1285 nsresult rv; |
|
1286 |
|
1287 if (aOuter != nullptr) |
|
1288 return NS_ERROR_NO_AGGREGATION; |
|
1289 |
|
1290 nsCacheService * cacheService = new nsCacheService(); |
|
1291 if (cacheService == nullptr) |
|
1292 return NS_ERROR_OUT_OF_MEMORY; |
|
1293 |
|
1294 NS_ADDREF(cacheService); |
|
1295 rv = cacheService->Init(); |
|
1296 if (NS_SUCCEEDED(rv)) { |
|
1297 rv = cacheService->QueryInterface(aIID, aResult); |
|
1298 } |
|
1299 NS_RELEASE(cacheService); |
|
1300 return rv; |
|
1301 } |
|
1302 |
|
1303 |
|
1304 NS_IMETHODIMP |
|
1305 nsCacheService::CreateSession(const char * clientID, |
|
1306 nsCacheStoragePolicy storagePolicy, |
|
1307 bool streamBased, |
|
1308 nsICacheSession **result) |
|
1309 { |
|
1310 *result = nullptr; |
|
1311 |
|
1312 if (this == nullptr) return NS_ERROR_NOT_AVAILABLE; |
|
1313 |
|
1314 nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased); |
|
1315 if (!session) return NS_ERROR_OUT_OF_MEMORY; |
|
1316 |
|
1317 NS_ADDREF(*result = session); |
|
1318 |
|
1319 return NS_OK; |
|
1320 } |
|
1321 |
|
1322 |
|
1323 nsresult |
|
1324 nsCacheService::EvictEntriesForSession(nsCacheSession * session) |
|
1325 { |
|
1326 NS_ASSERTION(gService, "nsCacheService::gService is null."); |
|
1327 return gService->EvictEntriesForClient(session->ClientID()->get(), |
|
1328 session->StoragePolicy()); |
|
1329 } |
|
1330 |
|
1331 namespace { |
|
1332 |
|
1333 class EvictionNotifierRunnable : public nsRunnable |
|
1334 { |
|
1335 public: |
|
1336 EvictionNotifierRunnable(nsISupports* aSubject) |
|
1337 : mSubject(aSubject) |
|
1338 { } |
|
1339 |
|
1340 NS_DECL_NSIRUNNABLE |
|
1341 |
|
1342 private: |
|
1343 nsCOMPtr<nsISupports> mSubject; |
|
1344 }; |
|
1345 |
|
1346 NS_IMETHODIMP |
|
1347 EvictionNotifierRunnable::Run() |
|
1348 { |
|
1349 nsCOMPtr<nsIObserverService> obsSvc = |
|
1350 mozilla::services::GetObserverService(); |
|
1351 if (obsSvc) { |
|
1352 obsSvc->NotifyObservers(mSubject, |
|
1353 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, |
|
1354 nullptr); |
|
1355 } |
|
1356 return NS_OK; |
|
1357 } |
|
1358 |
|
1359 } // anonymous namespace |
|
1360 |
|
1361 nsresult |
|
1362 nsCacheService::EvictEntriesForClient(const char * clientID, |
|
1363 nsCacheStoragePolicy storagePolicy) |
|
1364 { |
|
1365 nsRefPtr<EvictionNotifierRunnable> r = |
|
1366 new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this)); |
|
1367 NS_DispatchToMainThread(r); |
|
1368 |
|
1369 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT)); |
|
1370 nsresult res = NS_OK; |
|
1371 |
|
1372 if (storagePolicy == nsICache::STORE_ANYWHERE || |
|
1373 storagePolicy == nsICache::STORE_ON_DISK) { |
|
1374 |
|
1375 if (mEnableDiskDevice) { |
|
1376 nsresult rv = NS_OK; |
|
1377 if (!mDiskDevice) |
|
1378 rv = CreateDiskDevice(); |
|
1379 if (mDiskDevice) |
|
1380 rv = mDiskDevice->EvictEntries(clientID); |
|
1381 if (NS_FAILED(rv)) |
|
1382 res = rv; |
|
1383 } |
|
1384 } |
|
1385 |
|
1386 // Only clear the offline cache if it has been specifically asked for. |
|
1387 if (storagePolicy == nsICache::STORE_OFFLINE) { |
|
1388 if (mEnableOfflineDevice) { |
|
1389 nsresult rv = NS_OK; |
|
1390 if (!mOfflineDevice) |
|
1391 rv = CreateOfflineDevice(); |
|
1392 if (mOfflineDevice) |
|
1393 rv = mOfflineDevice->EvictEntries(clientID); |
|
1394 if (NS_FAILED(rv)) |
|
1395 res = rv; |
|
1396 } |
|
1397 } |
|
1398 |
|
1399 if (storagePolicy == nsICache::STORE_ANYWHERE || |
|
1400 storagePolicy == nsICache::STORE_IN_MEMORY) { |
|
1401 // If there is no memory device, there is no need to evict it... |
|
1402 if (mMemoryDevice) { |
|
1403 nsresult rv = mMemoryDevice->EvictEntries(clientID); |
|
1404 if (NS_FAILED(rv)) |
|
1405 res = rv; |
|
1406 } |
|
1407 } |
|
1408 |
|
1409 return res; |
|
1410 } |
|
1411 |
|
1412 |
|
1413 nsresult |
|
1414 nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy, |
|
1415 bool * result) |
|
1416 { |
|
1417 if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE; |
|
1418 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY)); |
|
1419 |
|
1420 *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy); |
|
1421 return NS_OK; |
|
1422 } |
|
1423 |
|
1424 |
|
1425 nsresult |
|
1426 nsCacheService::DoomEntry(nsCacheSession *session, |
|
1427 const nsACString &key, |
|
1428 nsICacheListener *listener) |
|
1429 { |
|
1430 CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n", |
|
1431 session, PromiseFlatCString(key).get())); |
|
1432 NS_ASSERTION(gService, "nsCacheService::gService is null."); |
|
1433 |
|
1434 if (!gService->mInitialized) |
|
1435 return NS_ERROR_NOT_INITIALIZED; |
|
1436 |
|
1437 return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener)); |
|
1438 } |
|
1439 |
|
1440 |
|
1441 bool |
|
1442 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy) |
|
1443 { |
|
1444 if (gService->mEnableMemoryDevice && |
|
1445 (storagePolicy == nsICache::STORE_ANYWHERE || |
|
1446 storagePolicy == nsICache::STORE_IN_MEMORY)) { |
|
1447 return true; |
|
1448 } |
|
1449 if (gService->mEnableDiskDevice && |
|
1450 (storagePolicy == nsICache::STORE_ANYWHERE || |
|
1451 storagePolicy == nsICache::STORE_ON_DISK)) { |
|
1452 return true; |
|
1453 } |
|
1454 if (gService->mEnableOfflineDevice && |
|
1455 storagePolicy == nsICache::STORE_OFFLINE) { |
|
1456 return true; |
|
1457 } |
|
1458 |
|
1459 return false; |
|
1460 } |
|
1461 |
|
1462 NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor) |
|
1463 { |
|
1464 NS_ENSURE_ARG_POINTER(visitor); |
|
1465 |
|
1466 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES)); |
|
1467 |
|
1468 if (!(mEnableDiskDevice || mEnableMemoryDevice)) |
|
1469 return NS_ERROR_NOT_AVAILABLE; |
|
1470 |
|
1471 // XXX record the fact that a visitation is in progress, |
|
1472 // XXX i.e. keep list of visitors in progress. |
|
1473 |
|
1474 nsresult rv = NS_OK; |
|
1475 // If there is no memory device, there are then also no entries to visit... |
|
1476 if (mMemoryDevice) { |
|
1477 rv = mMemoryDevice->Visit(visitor); |
|
1478 if (NS_FAILED(rv)) return rv; |
|
1479 } |
|
1480 |
|
1481 if (mEnableDiskDevice) { |
|
1482 if (!mDiskDevice) { |
|
1483 rv = CreateDiskDevice(); |
|
1484 if (NS_FAILED(rv)) return rv; |
|
1485 } |
|
1486 rv = mDiskDevice->Visit(visitor); |
|
1487 if (NS_FAILED(rv)) return rv; |
|
1488 } |
|
1489 |
|
1490 if (mEnableOfflineDevice) { |
|
1491 if (!mOfflineDevice) { |
|
1492 rv = CreateOfflineDevice(); |
|
1493 if (NS_FAILED(rv)) return rv; |
|
1494 } |
|
1495 rv = mOfflineDevice->Visit(visitor); |
|
1496 if (NS_FAILED(rv)) return rv; |
|
1497 } |
|
1498 |
|
1499 // XXX notify any shutdown process that visitation is complete for THIS visitor. |
|
1500 // XXX keep queue of visitors |
|
1501 |
|
1502 return NS_OK; |
|
1503 } |
|
1504 |
|
1505 void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification() |
|
1506 { |
|
1507 MOZ_ASSERT(NS_IsMainThread()); |
|
1508 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService(); |
|
1509 if (obsvc) { |
|
1510 obsvc->NotifyObservers(nullptr, |
|
1511 "network-clear-cache-stored-anywhere", |
|
1512 nullptr); |
|
1513 } |
|
1514 } |
|
1515 |
|
1516 NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy) |
|
1517 { |
|
1518 if (storagePolicy == nsICache::STORE_ANYWHERE) { |
|
1519 // if not called on main thread, dispatch the notification to the main thread to notify observers |
|
1520 if (!NS_IsMainThread()) { |
|
1521 nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, |
|
1522 &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification); |
|
1523 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
1524 } else { |
|
1525 // else you're already on main thread - notify observers |
|
1526 FireClearNetworkCacheStoredAnywhereNotification(); |
|
1527 } |
|
1528 } |
|
1529 |
|
1530 NS_IMETHODIMP r; |
|
1531 r = EvictEntriesForClient(nullptr, storagePolicy); |
|
1532 |
|
1533 // XXX: Bloody hack until we get this notifier in FF14.0: |
|
1534 // https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsICacheListener#onCacheEntryDoomed%28%29 |
|
1535 if (storagePolicy == nsICache::STORE_ANYWHERE && |
|
1536 NS_IsMainThread() && gService && gService->mInitialized) { |
|
1537 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT)); |
|
1538 gService->mClearingEntries = true; |
|
1539 gService->DoomActiveEntries(nullptr); |
|
1540 gService->ClearDoomList(); |
|
1541 (void) SyncWithCacheIOThread(); |
|
1542 gService->mClearingEntries = false; |
|
1543 } |
|
1544 return r; |
|
1545 } |
|
1546 |
|
1547 NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget) |
|
1548 { |
|
1549 NS_ENSURE_ARG_POINTER(aCacheIOTarget); |
|
1550 |
|
1551 // Because mCacheIOThread can only be changed on the main thread, it can be |
|
1552 // read from the main thread without the lock. This is useful to prevent |
|
1553 // blocking the main thread on other cache operations. |
|
1554 if (!NS_IsMainThread()) { |
|
1555 Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET)); |
|
1556 } |
|
1557 |
|
1558 nsresult rv; |
|
1559 if (mCacheIOThread) { |
|
1560 NS_ADDREF(*aCacheIOTarget = mCacheIOThread); |
|
1561 rv = NS_OK; |
|
1562 } else { |
|
1563 *aCacheIOTarget = nullptr; |
|
1564 rv = NS_ERROR_NOT_AVAILABLE; |
|
1565 } |
|
1566 |
|
1567 if (!NS_IsMainThread()) { |
|
1568 Unlock(); |
|
1569 } |
|
1570 |
|
1571 return rv; |
|
1572 } |
|
1573 |
|
1574 /* nsICacheServiceInternal |
|
1575 * readonly attribute double lockHeldTime; |
|
1576 */ |
|
1577 NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime) |
|
1578 { |
|
1579 MutexAutoLock lock(mTimeStampLock); |
|
1580 |
|
1581 if (mLockAcquiredTimeStamp.IsNull()) { |
|
1582 *aLockHeldTime = 0.0; |
|
1583 } |
|
1584 else { |
|
1585 *aLockHeldTime = |
|
1586 (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds(); |
|
1587 } |
|
1588 |
|
1589 return NS_OK; |
|
1590 } |
|
1591 |
|
1592 /** |
|
1593 * Internal Methods |
|
1594 */ |
|
1595 nsresult |
|
1596 nsCacheService::CreateDiskDevice() |
|
1597 { |
|
1598 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE; |
|
1599 if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE; |
|
1600 if (mDiskDevice) return NS_OK; |
|
1601 |
|
1602 mDiskDevice = new nsDiskCacheDevice; |
|
1603 if (!mDiskDevice) return NS_ERROR_OUT_OF_MEMORY; |
|
1604 |
|
1605 // set the preferences |
|
1606 mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory()); |
|
1607 mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity()); |
|
1608 mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize()); |
|
1609 |
|
1610 nsresult rv = mDiskDevice->Init(); |
|
1611 if (NS_FAILED(rv)) { |
|
1612 #if DEBUG |
|
1613 printf("###\n"); |
|
1614 printf("### mDiskDevice->Init() failed (0x%.8x)\n", |
|
1615 static_cast<uint32_t>(rv)); |
|
1616 printf("### - disabling disk cache for this session.\n"); |
|
1617 printf("###\n"); |
|
1618 #endif |
|
1619 mEnableDiskDevice = false; |
|
1620 delete mDiskDevice; |
|
1621 mDiskDevice = nullptr; |
|
1622 return rv; |
|
1623 } |
|
1624 |
|
1625 Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX, |
|
1626 mObserver->ShouldUseOldMaxSmartSize()); |
|
1627 |
|
1628 NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!"); |
|
1629 |
|
1630 // Disk device is usually created during the startup. Delay smart size |
|
1631 // calculation to avoid possible massive IO caused by eviction of entries |
|
1632 // in case the new smart size is smaller than current cache usage. |
|
1633 mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); |
|
1634 if (NS_SUCCEEDED(rv)) { |
|
1635 rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(), |
|
1636 1000*60*3, |
|
1637 nsITimer::TYPE_ONE_SHOT); |
|
1638 if (NS_FAILED(rv)) { |
|
1639 NS_WARNING("Failed to post smart size timer"); |
|
1640 mSmartSizeTimer = nullptr; |
|
1641 } |
|
1642 } else { |
|
1643 NS_WARNING("Can't create smart size timer"); |
|
1644 } |
|
1645 // Ignore state of the timer and return success since the purpose of the |
|
1646 // method (create the disk-device) has been fulfilled |
|
1647 |
|
1648 return NS_OK; |
|
1649 } |
|
1650 |
|
1651 // Runnable sent from cache thread to main thread |
|
1652 class nsDisableOldMaxSmartSizePrefEvent: public nsRunnable |
|
1653 { |
|
1654 public: |
|
1655 nsDisableOldMaxSmartSizePrefEvent() {} |
|
1656 |
|
1657 NS_IMETHOD Run() |
|
1658 { |
|
1659 // Main thread may have already called nsCacheService::Shutdown |
|
1660 if (!nsCacheService::IsInitialized()) |
|
1661 return NS_ERROR_NOT_AVAILABLE; |
|
1662 |
|
1663 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
1664 if (!branch) { |
|
1665 return NS_ERROR_NOT_AVAILABLE; |
|
1666 } |
|
1667 |
|
1668 nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false); |
|
1669 if (NS_FAILED(rv)) { |
|
1670 NS_WARNING("Failed to disable old max smart size"); |
|
1671 return rv; |
|
1672 } |
|
1673 |
|
1674 // It is safe to call SetDiskSmartSize_Locked() without holding the lock |
|
1675 // when we are on main thread and nsCacheService is initialized. |
|
1676 nsCacheService::gService->SetDiskSmartSize_Locked(); |
|
1677 |
|
1678 if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) { |
|
1679 rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE); |
|
1680 if (NS_FAILED(rv)) { |
|
1681 NS_WARNING("Failed to set cache capacity pref"); |
|
1682 } |
|
1683 } |
|
1684 |
|
1685 return NS_OK; |
|
1686 } |
|
1687 }; |
|
1688 |
|
1689 void |
|
1690 nsCacheService::MarkStartingFresh() |
|
1691 { |
|
1692 if (!gService->mObserver->ShouldUseOldMaxSmartSize()) { |
|
1693 // Already using new max, nothing to do here |
|
1694 return; |
|
1695 } |
|
1696 |
|
1697 gService->mObserver->SetUseNewMaxSmartSize(true); |
|
1698 |
|
1699 // We always dispatch an event here because we don't want to deal with lock |
|
1700 // reentrance issues. |
|
1701 NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent()); |
|
1702 } |
|
1703 |
|
1704 nsresult |
|
1705 nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice) |
|
1706 { |
|
1707 if (!mOfflineDevice) { |
|
1708 nsresult rv = CreateOfflineDevice(); |
|
1709 NS_ENSURE_SUCCESS(rv, rv); |
|
1710 } |
|
1711 |
|
1712 NS_ADDREF(*aDevice = mOfflineDevice); |
|
1713 return NS_OK; |
|
1714 } |
|
1715 |
|
1716 nsresult |
|
1717 nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir, |
|
1718 int32_t aQuota, |
|
1719 nsOfflineCacheDevice **aDevice) |
|
1720 { |
|
1721 nsresult rv; |
|
1722 |
|
1723 nsAutoString profilePath; |
|
1724 rv = aProfileDir->GetPath(profilePath); |
|
1725 NS_ENSURE_SUCCESS(rv, rv); |
|
1726 |
|
1727 if (!mCustomOfflineDevices.Get(profilePath, aDevice)) { |
|
1728 rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice); |
|
1729 NS_ENSURE_SUCCESS(rv, rv); |
|
1730 |
|
1731 (*aDevice)->SetAutoShutdown(); |
|
1732 mCustomOfflineDevices.Put(profilePath, *aDevice); |
|
1733 } |
|
1734 |
|
1735 return NS_OK; |
|
1736 } |
|
1737 |
|
1738 nsresult |
|
1739 nsCacheService::CreateOfflineDevice() |
|
1740 { |
|
1741 CACHE_LOG_ALWAYS(("Creating default offline device")); |
|
1742 |
|
1743 if (mOfflineDevice) return NS_OK; |
|
1744 if (!nsCacheService::IsInitialized()) { |
|
1745 return NS_ERROR_NOT_AVAILABLE; |
|
1746 } |
|
1747 |
|
1748 nsresult rv = CreateCustomOfflineDevice( |
|
1749 mObserver->OfflineCacheParentDirectory(), |
|
1750 mObserver->OfflineCacheCapacity(), |
|
1751 &mOfflineDevice); |
|
1752 NS_ENSURE_SUCCESS(rv, rv); |
|
1753 |
|
1754 return NS_OK; |
|
1755 } |
|
1756 |
|
1757 nsresult |
|
1758 nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir, |
|
1759 int32_t aQuota, |
|
1760 nsOfflineCacheDevice **aDevice) |
|
1761 { |
|
1762 NS_ENSURE_ARG(aProfileDir); |
|
1763 |
|
1764 #if defined(PR_LOGGING) |
|
1765 nsAutoCString profilePath; |
|
1766 aProfileDir->GetNativePath(profilePath); |
|
1767 CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d", |
|
1768 profilePath.BeginReading(), aQuota)); |
|
1769 #endif |
|
1770 |
|
1771 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE; |
|
1772 if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE; |
|
1773 |
|
1774 *aDevice = new nsOfflineCacheDevice; |
|
1775 |
|
1776 NS_ADDREF(*aDevice); |
|
1777 |
|
1778 // set the preferences |
|
1779 (*aDevice)->SetCacheParentDirectory(aProfileDir); |
|
1780 (*aDevice)->SetCapacity(aQuota); |
|
1781 |
|
1782 nsresult rv = (*aDevice)->InitWithSqlite(mStorageService); |
|
1783 if (NS_FAILED(rv)) { |
|
1784 CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv)); |
|
1785 CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n")); |
|
1786 |
|
1787 NS_RELEASE(*aDevice); |
|
1788 } |
|
1789 return rv; |
|
1790 } |
|
1791 |
|
1792 nsresult |
|
1793 nsCacheService::CreateMemoryDevice() |
|
1794 { |
|
1795 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE; |
|
1796 if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE; |
|
1797 if (mMemoryDevice) return NS_OK; |
|
1798 |
|
1799 mMemoryDevice = new nsMemoryCacheDevice; |
|
1800 if (!mMemoryDevice) return NS_ERROR_OUT_OF_MEMORY; |
|
1801 |
|
1802 // set preference |
|
1803 int32_t capacity = mObserver->MemoryCacheCapacity(); |
|
1804 CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity)); |
|
1805 mMemoryDevice->SetCapacity(capacity); |
|
1806 mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize()); |
|
1807 |
|
1808 nsresult rv = mMemoryDevice->Init(); |
|
1809 if (NS_FAILED(rv)) { |
|
1810 NS_WARNING("Initialization of Memory Cache failed."); |
|
1811 delete mMemoryDevice; |
|
1812 mMemoryDevice = nullptr; |
|
1813 } |
|
1814 |
|
1815 return rv; |
|
1816 } |
|
1817 |
|
1818 nsresult |
|
1819 nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice) |
|
1820 { |
|
1821 nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory(); |
|
1822 if (!profileDir) |
|
1823 return NS_ERROR_UNEXPECTED; |
|
1824 |
|
1825 nsAutoString profilePath; |
|
1826 nsresult rv = profileDir->GetPath(profilePath); |
|
1827 NS_ENSURE_SUCCESS(rv, rv); |
|
1828 |
|
1829 mCustomOfflineDevices.Remove(profilePath); |
|
1830 return NS_OK; |
|
1831 } |
|
1832 |
|
1833 nsresult |
|
1834 nsCacheService::CreateRequest(nsCacheSession * session, |
|
1835 const nsACString & clientKey, |
|
1836 nsCacheAccessMode accessRequested, |
|
1837 bool blockingMode, |
|
1838 nsICacheListener * listener, |
|
1839 nsCacheRequest ** request) |
|
1840 { |
|
1841 NS_ASSERTION(request, "CreateRequest: request is null"); |
|
1842 |
|
1843 nsAutoCString key(*session->ClientID()); |
|
1844 key.Append(':'); |
|
1845 key.Append(clientKey); |
|
1846 |
|
1847 if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length(); |
|
1848 |
|
1849 // create request |
|
1850 *request = new nsCacheRequest(key, listener, accessRequested, |
|
1851 blockingMode, session); |
|
1852 |
|
1853 if (!listener) return NS_OK; // we're sync, we're done. |
|
1854 |
|
1855 // get the request's thread |
|
1856 (*request)->mThread = do_GetCurrentThread(); |
|
1857 |
|
1858 return NS_OK; |
|
1859 } |
|
1860 |
|
1861 |
|
1862 class nsCacheListenerEvent : public nsRunnable |
|
1863 { |
|
1864 public: |
|
1865 nsCacheListenerEvent(nsICacheListener *listener, |
|
1866 nsICacheEntryDescriptor *descriptor, |
|
1867 nsCacheAccessMode accessGranted, |
|
1868 nsresult status) |
|
1869 : mListener(listener) // transfers reference |
|
1870 , mDescriptor(descriptor) // transfers reference (may be null) |
|
1871 , mAccessGranted(accessGranted) |
|
1872 , mStatus(status) |
|
1873 {} |
|
1874 |
|
1875 NS_IMETHOD Run() |
|
1876 { |
|
1877 mozilla::eventtracer::AutoEventTracer tracer( |
|
1878 static_cast<nsIRunnable*>(this), |
|
1879 eventtracer::eExec, |
|
1880 eventtracer::eDone, |
|
1881 "net::cache::OnCacheEntryAvailable"); |
|
1882 |
|
1883 mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus); |
|
1884 |
|
1885 NS_RELEASE(mListener); |
|
1886 NS_IF_RELEASE(mDescriptor); |
|
1887 return NS_OK; |
|
1888 } |
|
1889 |
|
1890 private: |
|
1891 // We explicitly leak mListener or mDescriptor if Run is not called |
|
1892 // because otherwise we cannot guarantee that they are destroyed on |
|
1893 // the right thread. |
|
1894 |
|
1895 nsICacheListener *mListener; |
|
1896 nsICacheEntryDescriptor *mDescriptor; |
|
1897 nsCacheAccessMode mAccessGranted; |
|
1898 nsresult mStatus; |
|
1899 }; |
|
1900 |
|
1901 |
|
1902 nsresult |
|
1903 nsCacheService::NotifyListener(nsCacheRequest * request, |
|
1904 nsICacheEntryDescriptor * descriptor, |
|
1905 nsCacheAccessMode accessGranted, |
|
1906 nsresult status) |
|
1907 { |
|
1908 NS_ASSERTION(request->mThread, "no thread set in async request!"); |
|
1909 |
|
1910 // Swap ownership, and release listener on target thread... |
|
1911 nsICacheListener *listener = request->mListener; |
|
1912 request->mListener = nullptr; |
|
1913 |
|
1914 nsCOMPtr<nsIRunnable> ev = |
|
1915 new nsCacheListenerEvent(listener, descriptor, |
|
1916 accessGranted, status); |
|
1917 if (!ev) { |
|
1918 // Better to leak listener and descriptor if we fail because we don't |
|
1919 // want to destroy them inside the cache service lock or on potentially |
|
1920 // the wrong thread. |
|
1921 return NS_ERROR_OUT_OF_MEMORY; |
|
1922 } |
|
1923 |
|
1924 MOZ_EVENT_TRACER_NAME_OBJECT(ev.get(), request->mKey.get()); |
|
1925 MOZ_EVENT_TRACER_WAIT(ev.get(), "net::cache::OnCacheEntryAvailable"); |
|
1926 return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL); |
|
1927 } |
|
1928 |
|
1929 |
|
1930 nsresult |
|
1931 nsCacheService::ProcessRequest(nsCacheRequest * request, |
|
1932 bool calledFromOpenCacheEntry, |
|
1933 nsICacheEntryDescriptor ** result) |
|
1934 { |
|
1935 mozilla::eventtracer::AutoEventTracer tracer( |
|
1936 request, |
|
1937 eventtracer::eExec, |
|
1938 eventtracer::eDone, |
|
1939 "net::cache::ProcessRequest"); |
|
1940 |
|
1941 // !!! must be called with mLock held !!! |
|
1942 nsresult rv; |
|
1943 nsCacheEntry * entry = nullptr; |
|
1944 nsCacheEntry * doomedEntry = nullptr; |
|
1945 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE; |
|
1946 if (result) *result = nullptr; |
|
1947 |
|
1948 while(1) { // Activate entry loop |
|
1949 rv = ActivateEntry(request, &entry, &doomedEntry); // get the entry for this request |
|
1950 if (NS_FAILED(rv)) break; |
|
1951 |
|
1952 while(1) { // Request Access loop |
|
1953 NS_ASSERTION(entry, "no entry in Request Access loop!"); |
|
1954 // entry->RequestAccess queues request on entry |
|
1955 rv = entry->RequestAccess(request, &accessGranted); |
|
1956 if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break; |
|
1957 |
|
1958 if (request->IsBlocking()) { |
|
1959 if (request->mListener) { |
|
1960 // async exits - validate, doom, or close will resume |
|
1961 return rv; |
|
1962 } |
|
1963 |
|
1964 // XXX this is probably wrong... |
|
1965 Unlock(); |
|
1966 rv = request->WaitForValidation(); |
|
1967 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST)); |
|
1968 } |
|
1969 |
|
1970 PR_REMOVE_AND_INIT_LINK(request); |
|
1971 if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error |
|
1972 // okay, we're ready to process this request, request access again |
|
1973 } |
|
1974 if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break; |
|
1975 |
|
1976 if (entry->IsNotInUse()) { |
|
1977 // this request was the last one keeping it around, so get rid of it |
|
1978 DeactivateEntry(entry); |
|
1979 } |
|
1980 // loop back around to look for another entry |
|
1981 } |
|
1982 |
|
1983 if (NS_SUCCEEDED(rv) && request->mProfileDir) { |
|
1984 // Custom cache directory has been demanded. Preset the cache device. |
|
1985 if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) { |
|
1986 // Failsafe check: this is implemented only for offline cache atm. |
|
1987 rv = NS_ERROR_FAILURE; |
|
1988 } else { |
|
1989 nsRefPtr<nsOfflineCacheDevice> customCacheDevice; |
|
1990 rv = GetCustomOfflineDevice(request->mProfileDir, -1, |
|
1991 getter_AddRefs(customCacheDevice)); |
|
1992 if (NS_SUCCEEDED(rv)) |
|
1993 entry->SetCustomCacheDevice(customCacheDevice); |
|
1994 } |
|
1995 } |
|
1996 |
|
1997 nsICacheEntryDescriptor *descriptor = nullptr; |
|
1998 |
|
1999 if (NS_SUCCEEDED(rv)) |
|
2000 rv = entry->CreateDescriptor(request, accessGranted, &descriptor); |
|
2001 |
|
2002 // If doomedEntry is set, ActivatEntry() doomed an existing entry and |
|
2003 // created a new one for that cache-key. However, any pending requests |
|
2004 // on the doomed entry were not processed and we need to do that here. |
|
2005 // This must be done after adding the created entry to list of active |
|
2006 // entries (which is done in ActivateEntry()) otherwise the hashkeys crash |
|
2007 // (see bug ##561313). It is also important to do this after creating a |
|
2008 // descriptor for this request, or some other request may end up being |
|
2009 // executed first for the newly created entry. |
|
2010 // Finally, it is worth to emphasize that if doomedEntry is set, |
|
2011 // ActivateEntry() created a new entry for the request, which will be |
|
2012 // initialized by RequestAccess() and they both should have returned NS_OK. |
|
2013 if (doomedEntry) { |
|
2014 (void) ProcessPendingRequests(doomedEntry); |
|
2015 if (doomedEntry->IsNotInUse()) |
|
2016 DeactivateEntry(doomedEntry); |
|
2017 doomedEntry = nullptr; |
|
2018 } |
|
2019 |
|
2020 if (request->mListener) { // Asynchronous |
|
2021 |
|
2022 if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking()) |
|
2023 return rv; // skip notifying listener, just return rv to caller |
|
2024 |
|
2025 // call listener to report error or descriptor |
|
2026 nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv); |
|
2027 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) { |
|
2028 rv = rv2; // trigger delete request |
|
2029 } |
|
2030 } else { // Synchronous |
|
2031 *result = descriptor; |
|
2032 } |
|
2033 return rv; |
|
2034 } |
|
2035 |
|
2036 |
|
2037 nsresult |
|
2038 nsCacheService::OpenCacheEntry(nsCacheSession * session, |
|
2039 const nsACString & key, |
|
2040 nsCacheAccessMode accessRequested, |
|
2041 bool blockingMode, |
|
2042 nsICacheListener * listener, |
|
2043 nsICacheEntryDescriptor ** result) |
|
2044 { |
|
2045 CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n", |
|
2046 session, PromiseFlatCString(key).get(), accessRequested, |
|
2047 blockingMode)); |
|
2048 NS_ASSERTION(gService, "nsCacheService::gService is null."); |
|
2049 if (result) |
|
2050 *result = nullptr; |
|
2051 |
|
2052 if (!gService->mInitialized) |
|
2053 return NS_ERROR_NOT_INITIALIZED; |
|
2054 |
|
2055 nsCacheRequest * request = nullptr; |
|
2056 |
|
2057 nsresult rv = gService->CreateRequest(session, |
|
2058 key, |
|
2059 accessRequested, |
|
2060 blockingMode, |
|
2061 listener, |
|
2062 &request); |
|
2063 if (NS_FAILED(rv)) return rv; |
|
2064 |
|
2065 CACHE_LOG_DEBUG(("Created request %p\n", request)); |
|
2066 |
|
2067 // Process the request on the background thread if we are on the main thread |
|
2068 // and the the request is asynchronous |
|
2069 if (NS_IsMainThread() && listener && gService->mCacheIOThread) { |
|
2070 nsCOMPtr<nsIRunnable> ev = |
|
2071 new nsProcessRequestEvent(request); |
|
2072 rv = DispatchToCacheIOThread(ev); |
|
2073 |
|
2074 // delete request if we didn't post the event |
|
2075 if (NS_FAILED(rv)) |
|
2076 delete request; |
|
2077 } |
|
2078 else { |
|
2079 |
|
2080 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY)); |
|
2081 rv = gService->ProcessRequest(request, true, result); |
|
2082 |
|
2083 // delete requests that have completed |
|
2084 if (!(listener && blockingMode && |
|
2085 (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) |
|
2086 delete request; |
|
2087 } |
|
2088 |
|
2089 return rv; |
|
2090 } |
|
2091 |
|
2092 |
|
2093 nsresult |
|
2094 nsCacheService::ActivateEntry(nsCacheRequest * request, |
|
2095 nsCacheEntry ** result, |
|
2096 nsCacheEntry ** doomedEntry) |
|
2097 { |
|
2098 CACHE_LOG_DEBUG(("Activate entry for request %p\n", request)); |
|
2099 if (!mInitialized || mClearingEntries) |
|
2100 return NS_ERROR_NOT_AVAILABLE; |
|
2101 |
|
2102 mozilla::eventtracer::AutoEventTracer tracer( |
|
2103 request, |
|
2104 eventtracer::eExec, |
|
2105 eventtracer::eDone, |
|
2106 "net::cache::ActivateEntry"); |
|
2107 |
|
2108 nsresult rv = NS_OK; |
|
2109 |
|
2110 NS_ASSERTION(request != nullptr, "ActivateEntry called with no request"); |
|
2111 if (result) *result = nullptr; |
|
2112 if (doomedEntry) *doomedEntry = nullptr; |
|
2113 if ((!request) || (!result) || (!doomedEntry)) |
|
2114 return NS_ERROR_NULL_POINTER; |
|
2115 |
|
2116 // check if the request can be satisfied |
|
2117 if (!mEnableMemoryDevice && !request->IsStreamBased()) |
|
2118 return NS_ERROR_FAILURE; |
|
2119 if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy())) |
|
2120 return NS_ERROR_FAILURE; |
|
2121 |
|
2122 // search active entries (including those not bound to device) |
|
2123 nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey)); |
|
2124 CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry)); |
|
2125 |
|
2126 if (!entry) { |
|
2127 // search cache devices for entry |
|
2128 bool collision = false; |
|
2129 entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision); |
|
2130 CACHE_LOG_DEBUG(("Device search for request %p returned %p\n", |
|
2131 request, entry)); |
|
2132 // When there is a hashkey collision just refuse to cache it... |
|
2133 if (collision) return NS_ERROR_CACHE_IN_USE; |
|
2134 |
|
2135 if (entry) entry->MarkInitialized(); |
|
2136 } else { |
|
2137 NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!"); |
|
2138 } |
|
2139 |
|
2140 if (entry) { |
|
2141 ++mCacheHits; |
|
2142 entry->Fetched(); |
|
2143 } else { |
|
2144 ++mCacheMisses; |
|
2145 } |
|
2146 |
|
2147 if (entry && |
|
2148 ((request->AccessRequested() == nsICache::ACCESS_WRITE) || |
|
2149 ((request->StoragePolicy() != nsICache::STORE_OFFLINE) && |
|
2150 (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) && |
|
2151 request->WillDoomEntriesIfExpired())))) |
|
2152 |
|
2153 { |
|
2154 // this is FORCE-WRITE request or the entry has expired |
|
2155 // we doom entry without processing pending requests, but store it in |
|
2156 // doomedEntry which causes pending requests to be processed below |
|
2157 rv = DoomEntry_Internal(entry, false); |
|
2158 *doomedEntry = entry; |
|
2159 if (NS_FAILED(rv)) { |
|
2160 // XXX what to do? Increment FailedDooms counter? |
|
2161 } |
|
2162 entry = nullptr; |
|
2163 } |
|
2164 |
|
2165 if (!entry) { |
|
2166 if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) { |
|
2167 // this is a READ-ONLY request |
|
2168 rv = NS_ERROR_CACHE_KEY_NOT_FOUND; |
|
2169 goto error; |
|
2170 } |
|
2171 |
|
2172 entry = new nsCacheEntry(request->mKey, |
|
2173 request->IsStreamBased(), |
|
2174 request->StoragePolicy()); |
|
2175 if (!entry) |
|
2176 return NS_ERROR_OUT_OF_MEMORY; |
|
2177 |
|
2178 if (request->IsPrivate()) |
|
2179 entry->MarkPrivate(); |
|
2180 |
|
2181 entry->Fetched(); |
|
2182 ++mTotalEntries; |
|
2183 |
|
2184 // XXX we could perform an early bind in some cases based on storage policy |
|
2185 } |
|
2186 |
|
2187 if (!entry->IsActive()) { |
|
2188 rv = mActiveEntries.AddEntry(entry); |
|
2189 if (NS_FAILED(rv)) goto error; |
|
2190 CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry)); |
|
2191 entry->MarkActive(); // mark entry active, because it's now in mActiveEntries |
|
2192 } |
|
2193 *result = entry; |
|
2194 return NS_OK; |
|
2195 |
|
2196 error: |
|
2197 *result = nullptr; |
|
2198 delete entry; |
|
2199 return rv; |
|
2200 } |
|
2201 |
|
2202 |
|
2203 nsCacheEntry * |
|
2204 nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision) |
|
2205 { |
|
2206 Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2> timer; |
|
2207 nsCacheEntry * entry = nullptr; |
|
2208 |
|
2209 MOZ_EVENT_TRACER_NAME_OBJECT(key, key->BeginReading()); |
|
2210 eventtracer::AutoEventTracer searchCacheDevices( |
|
2211 key, |
|
2212 eventtracer::eExec, |
|
2213 eventtracer::eDone, |
|
2214 "net::cache::SearchCacheDevices"); |
|
2215 |
|
2216 CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice)); |
|
2217 |
|
2218 *collision = false; |
|
2219 if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) { |
|
2220 // If there is no memory device, then there is nothing to search... |
|
2221 if (mMemoryDevice) { |
|
2222 entry = mMemoryDevice->FindEntry(key, collision); |
|
2223 CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, " |
|
2224 "collision: %d\n", key->get(), entry, collision)); |
|
2225 } |
|
2226 } |
|
2227 |
|
2228 if (!entry && |
|
2229 ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) { |
|
2230 |
|
2231 if (mEnableDiskDevice) { |
|
2232 if (!mDiskDevice) { |
|
2233 nsresult rv = CreateDiskDevice(); |
|
2234 if (NS_FAILED(rv)) |
|
2235 return nullptr; |
|
2236 } |
|
2237 |
|
2238 entry = mDiskDevice->FindEntry(key, collision); |
|
2239 } |
|
2240 } |
|
2241 |
|
2242 if (!entry && (policy == nsICache::STORE_OFFLINE || |
|
2243 (policy == nsICache::STORE_ANYWHERE && |
|
2244 gIOService->IsOffline()))) { |
|
2245 |
|
2246 if (mEnableOfflineDevice) { |
|
2247 if (!mOfflineDevice) { |
|
2248 nsresult rv = CreateOfflineDevice(); |
|
2249 if (NS_FAILED(rv)) |
|
2250 return nullptr; |
|
2251 } |
|
2252 |
|
2253 entry = mOfflineDevice->FindEntry(key, collision); |
|
2254 } |
|
2255 } |
|
2256 |
|
2257 return entry; |
|
2258 } |
|
2259 |
|
2260 |
|
2261 nsCacheDevice * |
|
2262 nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry) |
|
2263 { |
|
2264 nsCacheDevice * device = entry->CacheDevice(); |
|
2265 // return device if found, possibly null if the entry is doomed i.e prevent |
|
2266 // doomed entries to bind to a device (see e.g. bugs #548406 and #596443) |
|
2267 if (device || entry->IsDoomed()) return device; |
|
2268 |
|
2269 int64_t predictedDataSize = entry->PredictedDataSize(); |
|
2270 if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) { |
|
2271 // this is the default |
|
2272 if (!mDiskDevice) { |
|
2273 (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead) |
|
2274 } |
|
2275 |
|
2276 if (mDiskDevice) { |
|
2277 // Bypass the cache if Content-Length says the entry will be too big |
|
2278 if (predictedDataSize != -1 && |
|
2279 mDiskDevice->EntryIsTooBig(predictedDataSize)) { |
|
2280 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry); |
|
2281 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); |
|
2282 return nullptr; |
|
2283 } |
|
2284 |
|
2285 entry->MarkBinding(); // enter state of binding |
|
2286 nsresult rv = mDiskDevice->BindEntry(entry); |
|
2287 entry->ClearBinding(); // exit state of binding |
|
2288 if (NS_SUCCEEDED(rv)) |
|
2289 device = mDiskDevice; |
|
2290 } |
|
2291 } |
|
2292 |
|
2293 // if we can't use mDiskDevice, try mMemoryDevice |
|
2294 if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) { |
|
2295 if (!mMemoryDevice) { |
|
2296 (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead) |
|
2297 } |
|
2298 if (mMemoryDevice) { |
|
2299 // Bypass the cache if Content-Length says entry will be too big |
|
2300 if (predictedDataSize != -1 && |
|
2301 mMemoryDevice->EntryIsTooBig(predictedDataSize)) { |
|
2302 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry); |
|
2303 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); |
|
2304 return nullptr; |
|
2305 } |
|
2306 |
|
2307 entry->MarkBinding(); // enter state of binding |
|
2308 nsresult rv = mMemoryDevice->BindEntry(entry); |
|
2309 entry->ClearBinding(); // exit state of binding |
|
2310 if (NS_SUCCEEDED(rv)) |
|
2311 device = mMemoryDevice; |
|
2312 } |
|
2313 } |
|
2314 |
|
2315 if (!device && entry->IsStreamData() && |
|
2316 entry->IsAllowedOffline() && mEnableOfflineDevice) { |
|
2317 if (!mOfflineDevice) { |
|
2318 (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead) |
|
2319 } |
|
2320 |
|
2321 device = entry->CustomCacheDevice() |
|
2322 ? entry->CustomCacheDevice() |
|
2323 : mOfflineDevice; |
|
2324 |
|
2325 if (device) { |
|
2326 entry->MarkBinding(); |
|
2327 nsresult rv = device->BindEntry(entry); |
|
2328 entry->ClearBinding(); |
|
2329 if (NS_FAILED(rv)) |
|
2330 device = nullptr; |
|
2331 } |
|
2332 } |
|
2333 |
|
2334 if (device) |
|
2335 entry->SetCacheDevice(device); |
|
2336 return device; |
|
2337 } |
|
2338 |
|
2339 nsresult |
|
2340 nsCacheService::DoomEntry(nsCacheEntry * entry) |
|
2341 { |
|
2342 return gService->DoomEntry_Internal(entry, true); |
|
2343 } |
|
2344 |
|
2345 |
|
2346 nsresult |
|
2347 nsCacheService::DoomEntry_Internal(nsCacheEntry * entry, |
|
2348 bool doProcessPendingRequests) |
|
2349 { |
|
2350 if (entry->IsDoomed()) return NS_OK; |
|
2351 |
|
2352 CACHE_LOG_DEBUG(("Dooming entry %p\n", entry)); |
|
2353 nsresult rv = NS_OK; |
|
2354 entry->MarkDoomed(); |
|
2355 |
|
2356 NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device."); |
|
2357 nsCacheDevice * device = entry->CacheDevice(); |
|
2358 if (device) device->DoomEntry(entry); |
|
2359 |
|
2360 if (entry->IsActive()) { |
|
2361 // remove from active entries |
|
2362 mActiveEntries.RemoveEntry(entry); |
|
2363 CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry)); |
|
2364 entry->MarkInactive(); |
|
2365 } |
|
2366 |
|
2367 // put on doom list to wait for descriptors to close |
|
2368 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list"); |
|
2369 PR_APPEND_LINK(entry, &mDoomedEntries); |
|
2370 |
|
2371 // handle pending requests only if we're supposed to |
|
2372 if (doProcessPendingRequests) { |
|
2373 // tell pending requests to get on with their lives... |
|
2374 rv = ProcessPendingRequests(entry); |
|
2375 |
|
2376 // All requests have been removed, but there may still be open descriptors |
|
2377 if (entry->IsNotInUse()) { |
|
2378 DeactivateEntry(entry); // tell device to get rid of it |
|
2379 } |
|
2380 } |
|
2381 return rv; |
|
2382 } |
|
2383 |
|
2384 |
|
2385 void |
|
2386 nsCacheService::OnProfileShutdown(bool cleanse) |
|
2387 { |
|
2388 if (!gService) return; |
|
2389 if (!gService->mInitialized) { |
|
2390 // The cache service has been shut down, but someone is still holding |
|
2391 // a reference to it. Ignore this call. |
|
2392 return; |
|
2393 } |
|
2394 { |
|
2395 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN)); |
|
2396 gService->mClearingEntries = true; |
|
2397 gService->DoomActiveEntries(nullptr); |
|
2398 } |
|
2399 |
|
2400 gService->CloseAllStreams(); |
|
2401 |
|
2402 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN)); |
|
2403 gService->ClearDoomList(); |
|
2404 |
|
2405 // Make sure to wait for any pending cache-operations before |
|
2406 // proceeding with destructive actions (bug #620660) |
|
2407 (void) SyncWithCacheIOThread(); |
|
2408 |
|
2409 if (gService->mDiskDevice && gService->mEnableDiskDevice) { |
|
2410 if (cleanse) |
|
2411 gService->mDiskDevice->EvictEntries(nullptr); |
|
2412 |
|
2413 gService->mDiskDevice->Shutdown(); |
|
2414 } |
|
2415 gService->mEnableDiskDevice = false; |
|
2416 |
|
2417 if (gService->mOfflineDevice && gService->mEnableOfflineDevice) { |
|
2418 if (cleanse) |
|
2419 gService->mOfflineDevice->EvictEntries(nullptr); |
|
2420 |
|
2421 gService->mOfflineDevice->Shutdown(); |
|
2422 } |
|
2423 gService->mCustomOfflineDevices.Enumerate( |
|
2424 &nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr); |
|
2425 |
|
2426 gService->mEnableOfflineDevice = false; |
|
2427 |
|
2428 if (gService->mMemoryDevice) { |
|
2429 // clear memory cache |
|
2430 gService->mMemoryDevice->EvictEntries(nullptr); |
|
2431 } |
|
2432 |
|
2433 gService->mClearingEntries = false; |
|
2434 } |
|
2435 |
|
2436 |
|
2437 void |
|
2438 nsCacheService::OnProfileChanged() |
|
2439 { |
|
2440 if (!gService) return; |
|
2441 |
|
2442 CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged")); |
|
2443 |
|
2444 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED)); |
|
2445 |
|
2446 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled(); |
|
2447 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled(); |
|
2448 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled(); |
|
2449 |
|
2450 if (gService->mDiskDevice) { |
|
2451 gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory()); |
|
2452 gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity()); |
|
2453 |
|
2454 // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false |
|
2455 nsresult rv = gService->mDiskDevice->Init(); |
|
2456 if (NS_FAILED(rv)) { |
|
2457 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed"); |
|
2458 gService->mEnableDiskDevice = false; |
|
2459 // XXX delete mDiskDevice? |
|
2460 } |
|
2461 } |
|
2462 |
|
2463 if (gService->mOfflineDevice) { |
|
2464 gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory()); |
|
2465 gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity()); |
|
2466 |
|
2467 // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false |
|
2468 nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService); |
|
2469 if (NS_FAILED(rv)) { |
|
2470 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed"); |
|
2471 gService->mEnableOfflineDevice = false; |
|
2472 // XXX delete mOfflineDevice? |
|
2473 } |
|
2474 } |
|
2475 |
|
2476 // If memoryDevice exists, reset its size to the new profile |
|
2477 if (gService->mMemoryDevice) { |
|
2478 if (gService->mEnableMemoryDevice) { |
|
2479 // make sure that capacity is reset to the right value |
|
2480 int32_t capacity = gService->mObserver->MemoryCacheCapacity(); |
|
2481 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n", |
|
2482 capacity)); |
|
2483 gService->mMemoryDevice->SetCapacity(capacity); |
|
2484 } else { |
|
2485 // tell memory device to evict everything |
|
2486 CACHE_LOG_DEBUG(("memory device disabled\n")); |
|
2487 gService->mMemoryDevice->SetCapacity(0); |
|
2488 // Don't delete memory device, because some entries may be active still... |
|
2489 } |
|
2490 } |
|
2491 } |
|
2492 |
|
2493 |
|
2494 void |
|
2495 nsCacheService::SetDiskCacheEnabled(bool enabled) |
|
2496 { |
|
2497 if (!gService) return; |
|
2498 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED)); |
|
2499 gService->mEnableDiskDevice = enabled; |
|
2500 } |
|
2501 |
|
2502 |
|
2503 void |
|
2504 nsCacheService::SetDiskCacheCapacity(int32_t capacity) |
|
2505 { |
|
2506 if (!gService) return; |
|
2507 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY)); |
|
2508 |
|
2509 if (gService->mDiskDevice) { |
|
2510 gService->mDiskDevice->SetCapacity(capacity); |
|
2511 } |
|
2512 |
|
2513 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled(); |
|
2514 } |
|
2515 |
|
2516 void |
|
2517 nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize) |
|
2518 { |
|
2519 if (!gService) return; |
|
2520 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE)); |
|
2521 |
|
2522 if (gService->mDiskDevice) { |
|
2523 gService->mDiskDevice->SetMaxEntrySize(maxSize); |
|
2524 } |
|
2525 } |
|
2526 |
|
2527 void |
|
2528 nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize) |
|
2529 { |
|
2530 if (!gService) return; |
|
2531 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE)); |
|
2532 |
|
2533 if (gService->mMemoryDevice) { |
|
2534 gService->mMemoryDevice->SetMaxEntrySize(maxSize); |
|
2535 } |
|
2536 } |
|
2537 |
|
2538 void |
|
2539 nsCacheService::SetOfflineCacheEnabled(bool enabled) |
|
2540 { |
|
2541 if (!gService) return; |
|
2542 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED)); |
|
2543 gService->mEnableOfflineDevice = enabled; |
|
2544 } |
|
2545 |
|
2546 void |
|
2547 nsCacheService::SetOfflineCacheCapacity(int32_t capacity) |
|
2548 { |
|
2549 if (!gService) return; |
|
2550 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY)); |
|
2551 |
|
2552 if (gService->mOfflineDevice) { |
|
2553 gService->mOfflineDevice->SetCapacity(capacity); |
|
2554 } |
|
2555 |
|
2556 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled(); |
|
2557 } |
|
2558 |
|
2559 |
|
2560 void |
|
2561 nsCacheService::SetMemoryCache() |
|
2562 { |
|
2563 if (!gService) return; |
|
2564 |
|
2565 CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache")); |
|
2566 |
|
2567 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE)); |
|
2568 |
|
2569 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled(); |
|
2570 |
|
2571 if (gService->mEnableMemoryDevice) { |
|
2572 if (gService->mMemoryDevice) { |
|
2573 int32_t capacity = gService->mObserver->MemoryCacheCapacity(); |
|
2574 // make sure that capacity is reset to the right value |
|
2575 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n", |
|
2576 capacity)); |
|
2577 gService->mMemoryDevice->SetCapacity(capacity); |
|
2578 } |
|
2579 } else { |
|
2580 if (gService->mMemoryDevice) { |
|
2581 // tell memory device to evict everything |
|
2582 CACHE_LOG_DEBUG(("memory device disabled\n")); |
|
2583 gService->mMemoryDevice->SetCapacity(0); |
|
2584 // Don't delete memory device, because some entries may be active still... |
|
2585 } |
|
2586 } |
|
2587 } |
|
2588 |
|
2589 |
|
2590 /****************************************************************************** |
|
2591 * static methods for nsCacheEntryDescriptor |
|
2592 *****************************************************************************/ |
|
2593 void |
|
2594 nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor) |
|
2595 { |
|
2596 // ask entry to remove descriptor |
|
2597 nsCacheEntry * entry = descriptor->CacheEntry(); |
|
2598 bool doomEntry; |
|
2599 bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry); |
|
2600 |
|
2601 if (!entry->IsValid()) { |
|
2602 gService->ProcessPendingRequests(entry); |
|
2603 } |
|
2604 |
|
2605 if (doomEntry) { |
|
2606 gService->DoomEntry_Internal(entry, true); |
|
2607 return; |
|
2608 } |
|
2609 |
|
2610 if (!stillActive) { |
|
2611 gService->DeactivateEntry(entry); |
|
2612 } |
|
2613 } |
|
2614 |
|
2615 |
|
2616 nsresult |
|
2617 nsCacheService::GetFileForEntry(nsCacheEntry * entry, |
|
2618 nsIFile ** result) |
|
2619 { |
|
2620 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry); |
|
2621 if (!device) return NS_ERROR_UNEXPECTED; |
|
2622 |
|
2623 return device->GetFileForEntry(entry, result); |
|
2624 } |
|
2625 |
|
2626 |
|
2627 nsresult |
|
2628 nsCacheService::OpenInputStreamForEntry(nsCacheEntry * entry, |
|
2629 nsCacheAccessMode mode, |
|
2630 uint32_t offset, |
|
2631 nsIInputStream ** result) |
|
2632 { |
|
2633 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry); |
|
2634 if (!device) return NS_ERROR_UNEXPECTED; |
|
2635 |
|
2636 return device->OpenInputStreamForEntry(entry, mode, offset, result); |
|
2637 } |
|
2638 |
|
2639 nsresult |
|
2640 nsCacheService::OpenOutputStreamForEntry(nsCacheEntry * entry, |
|
2641 nsCacheAccessMode mode, |
|
2642 uint32_t offset, |
|
2643 nsIOutputStream ** result) |
|
2644 { |
|
2645 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry); |
|
2646 if (!device) return NS_ERROR_UNEXPECTED; |
|
2647 |
|
2648 return device->OpenOutputStreamForEntry(entry, mode, offset, result); |
|
2649 } |
|
2650 |
|
2651 |
|
2652 nsresult |
|
2653 nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize) |
|
2654 { |
|
2655 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry); |
|
2656 if (!device) return NS_ERROR_UNEXPECTED; |
|
2657 |
|
2658 return device->OnDataSizeChange(entry, deltaSize); |
|
2659 } |
|
2660 |
|
2661 void |
|
2662 nsCacheService::LockAcquired() |
|
2663 { |
|
2664 MutexAutoLock lock(mTimeStampLock); |
|
2665 mLockAcquiredTimeStamp = TimeStamp::Now(); |
|
2666 } |
|
2667 |
|
2668 void |
|
2669 nsCacheService::LockReleased() |
|
2670 { |
|
2671 MutexAutoLock lock(mTimeStampLock); |
|
2672 mLockAcquiredTimeStamp = TimeStamp(); |
|
2673 } |
|
2674 |
|
2675 void |
|
2676 nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID) |
|
2677 { |
|
2678 mozilla::Telemetry::ID lockerID; |
|
2679 mozilla::Telemetry::ID generalID; |
|
2680 |
|
2681 if (NS_IsMainThread()) { |
|
2682 lockerID = mainThreadLockerID; |
|
2683 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2; |
|
2684 } else { |
|
2685 lockerID = mozilla::Telemetry::HistogramCount; |
|
2686 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2; |
|
2687 } |
|
2688 |
|
2689 TimeStamp start(TimeStamp::Now()); |
|
2690 MOZ_EVENT_TRACER_WAIT(nsCacheService::gService, "net::cache::lock"); |
|
2691 |
|
2692 gService->mLock.Lock(); |
|
2693 gService->LockAcquired(); |
|
2694 |
|
2695 TimeStamp stop(TimeStamp::Now()); |
|
2696 MOZ_EVENT_TRACER_EXEC(nsCacheService::gService, "net::cache::lock"); |
|
2697 |
|
2698 // Telemetry isn't thread safe on its own, but this is OK because we're |
|
2699 // protecting it with the cache lock. |
|
2700 if (lockerID != mozilla::Telemetry::HistogramCount) { |
|
2701 mozilla::Telemetry::AccumulateTimeDelta(lockerID, start, stop); |
|
2702 } |
|
2703 mozilla::Telemetry::AccumulateTimeDelta(generalID, start, stop); |
|
2704 } |
|
2705 |
|
2706 void |
|
2707 nsCacheService::Unlock() |
|
2708 { |
|
2709 gService->mLock.AssertCurrentThreadOwns(); |
|
2710 |
|
2711 nsTArray<nsISupports*> doomed; |
|
2712 doomed.SwapElements(gService->mDoomedObjects); |
|
2713 |
|
2714 gService->LockReleased(); |
|
2715 gService->mLock.Unlock(); |
|
2716 |
|
2717 MOZ_EVENT_TRACER_DONE(nsCacheService::gService, "net::cache::lock"); |
|
2718 |
|
2719 for (uint32_t i = 0; i < doomed.Length(); ++i) |
|
2720 doomed[i]->Release(); |
|
2721 } |
|
2722 |
|
2723 void |
|
2724 nsCacheService::ReleaseObject_Locked(nsISupports * obj, |
|
2725 nsIEventTarget * target) |
|
2726 { |
|
2727 gService->mLock.AssertCurrentThreadOwns(); |
|
2728 |
|
2729 bool isCur; |
|
2730 if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) { |
|
2731 gService->mDoomedObjects.AppendElement(obj); |
|
2732 } else { |
|
2733 NS_ProxyRelease(target, obj); |
|
2734 } |
|
2735 } |
|
2736 |
|
2737 |
|
2738 nsresult |
|
2739 nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element) |
|
2740 { |
|
2741 entry->SetData(element); |
|
2742 entry->TouchData(); |
|
2743 return NS_OK; |
|
2744 } |
|
2745 |
|
2746 |
|
2747 nsresult |
|
2748 nsCacheService::ValidateEntry(nsCacheEntry * entry) |
|
2749 { |
|
2750 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry); |
|
2751 if (!device) return NS_ERROR_UNEXPECTED; |
|
2752 |
|
2753 entry->MarkValid(); |
|
2754 nsresult rv = gService->ProcessPendingRequests(entry); |
|
2755 NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed."); |
|
2756 // XXX what else should be done? |
|
2757 |
|
2758 return rv; |
|
2759 } |
|
2760 |
|
2761 |
|
2762 int32_t |
|
2763 nsCacheService::CacheCompressionLevel() |
|
2764 { |
|
2765 int32_t level = gService->mObserver->CacheCompressionLevel(); |
|
2766 return level; |
|
2767 } |
|
2768 |
|
2769 |
|
2770 void |
|
2771 nsCacheService::DeactivateEntry(nsCacheEntry * entry) |
|
2772 { |
|
2773 CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry)); |
|
2774 nsresult rv = NS_OK; |
|
2775 NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!"); |
|
2776 nsCacheDevice * device = nullptr; |
|
2777 |
|
2778 if (mMaxDataSize < entry->DataSize() ) mMaxDataSize = entry->DataSize(); |
|
2779 if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize(); |
|
2780 |
|
2781 if (entry->IsDoomed()) { |
|
2782 // remove from Doomed list |
|
2783 PR_REMOVE_AND_INIT_LINK(entry); |
|
2784 } else if (entry->IsActive()) { |
|
2785 // remove from active entries |
|
2786 mActiveEntries.RemoveEntry(entry); |
|
2787 CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n", |
|
2788 entry)); |
|
2789 entry->MarkInactive(); |
|
2790 |
|
2791 // bind entry if necessary to store meta-data |
|
2792 device = EnsureEntryHasDevice(entry); |
|
2793 if (!device) { |
|
2794 CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active " |
|
2795 "entry %p\n", |
|
2796 entry)); |
|
2797 NS_WARNING("DeactivateEntry: unable to bind active entry\n"); |
|
2798 return; |
|
2799 } |
|
2800 } else { |
|
2801 // if mInitialized == false, |
|
2802 // then we're shutting down and this state is okay. |
|
2803 NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state."); |
|
2804 } |
|
2805 |
|
2806 device = entry->CacheDevice(); |
|
2807 if (device) { |
|
2808 rv = device->DeactivateEntry(entry); |
|
2809 if (NS_FAILED(rv)) { |
|
2810 // increment deactivate failure count |
|
2811 ++mDeactivateFailures; |
|
2812 } |
|
2813 } else { |
|
2814 // increment deactivating unbound entry statistic |
|
2815 ++mDeactivatedUnboundEntries; |
|
2816 delete entry; // because no one else will |
|
2817 } |
|
2818 } |
|
2819 |
|
2820 |
|
2821 nsresult |
|
2822 nsCacheService::ProcessPendingRequests(nsCacheEntry * entry) |
|
2823 { |
|
2824 mozilla::eventtracer::AutoEventTracer tracer( |
|
2825 entry, |
|
2826 eventtracer::eExec, |
|
2827 eventtracer::eDone, |
|
2828 "net::cache::ProcessPendingRequests"); |
|
2829 |
|
2830 nsresult rv = NS_OK; |
|
2831 nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ); |
|
2832 nsCacheRequest * nextRequest; |
|
2833 bool newWriter = false; |
|
2834 |
|
2835 CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n", |
|
2836 (entry->IsInitialized()?"" : "Un"), |
|
2837 (entry->IsDoomed()?"DOOMED" : ""), |
|
2838 (entry->IsValid()? "V":"Inv"), entry)); |
|
2839 |
|
2840 if (request == &entry->mRequestQ) return NS_OK; // no queued requests |
|
2841 |
|
2842 if (!entry->IsDoomed() && entry->IsInvalid()) { |
|
2843 // 1st descriptor closed w/o MarkValid() |
|
2844 NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors"); |
|
2845 |
|
2846 #if DEBUG |
|
2847 // verify no ACCESS_WRITE requests(shouldn't have any of these) |
|
2848 while (request != &entry->mRequestQ) { |
|
2849 NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE, |
|
2850 "ACCESS_WRITE request should have been given a new entry"); |
|
2851 request = (nsCacheRequest *)PR_NEXT_LINK(request); |
|
2852 } |
|
2853 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ); |
|
2854 #endif |
|
2855 // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer |
|
2856 while (request != &entry->mRequestQ) { |
|
2857 if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) { |
|
2858 newWriter = true; |
|
2859 CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request)); |
|
2860 break; |
|
2861 } |
|
2862 |
|
2863 request = (nsCacheRequest *)PR_NEXT_LINK(request); |
|
2864 } |
|
2865 |
|
2866 if (request == &entry->mRequestQ) // no requests asked for ACCESS_READ_WRITE, back to top |
|
2867 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ); |
|
2868 |
|
2869 // XXX what should we do if there are only READ requests in queue? |
|
2870 // XXX serialize their accesses, give them only read access, but force them to check validate flag? |
|
2871 // XXX or do readers simply presume the entry is valid |
|
2872 // See fix for bug #467392 below |
|
2873 } |
|
2874 |
|
2875 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE; |
|
2876 |
|
2877 while (request != &entry->mRequestQ) { |
|
2878 nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request); |
|
2879 CACHE_LOG_DEBUG((" %sync request %p for %p\n", |
|
2880 (request->mListener?"As":"S"), request, entry)); |
|
2881 |
|
2882 if (request->mListener) { |
|
2883 |
|
2884 // Async request |
|
2885 PR_REMOVE_AND_INIT_LINK(request); |
|
2886 |
|
2887 if (entry->IsDoomed()) { |
|
2888 rv = ProcessRequest(request, false, nullptr); |
|
2889 if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) |
|
2890 rv = NS_OK; |
|
2891 else |
|
2892 delete request; |
|
2893 |
|
2894 if (NS_FAILED(rv)) { |
|
2895 // XXX what to do? |
|
2896 } |
|
2897 } else if (entry->IsValid() || newWriter) { |
|
2898 rv = entry->RequestAccess(request, &accessGranted); |
|
2899 NS_ASSERTION(NS_SUCCEEDED(rv), |
|
2900 "if entry is valid, RequestAccess must succeed."); |
|
2901 // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?"); |
|
2902 |
|
2903 // entry->CreateDescriptor dequeues request, and queues descriptor |
|
2904 nsICacheEntryDescriptor *descriptor = nullptr; |
|
2905 rv = entry->CreateDescriptor(request, |
|
2906 accessGranted, |
|
2907 &descriptor); |
|
2908 |
|
2909 // post call to listener to report error or descriptor |
|
2910 rv = NotifyListener(request, descriptor, accessGranted, rv); |
|
2911 delete request; |
|
2912 if (NS_FAILED(rv)) { |
|
2913 // XXX what to do? |
|
2914 } |
|
2915 |
|
2916 } else { |
|
2917 // read-only request to an invalid entry - need to wait for |
|
2918 // the entry to become valid so we post an event to process |
|
2919 // the request again later (bug #467392) |
|
2920 nsCOMPtr<nsIRunnable> ev = |
|
2921 new nsProcessRequestEvent(request); |
|
2922 rv = DispatchToCacheIOThread(ev); |
|
2923 if (NS_FAILED(rv)) { |
|
2924 delete request; // avoid leak |
|
2925 } |
|
2926 } |
|
2927 } else { |
|
2928 |
|
2929 // Synchronous request |
|
2930 request->WakeUp(); |
|
2931 } |
|
2932 if (newWriter) break; // process remaining requests after validation |
|
2933 request = nextRequest; |
|
2934 } |
|
2935 |
|
2936 return NS_OK; |
|
2937 } |
|
2938 |
|
2939 bool |
|
2940 nsCacheService::IsDoomListEmpty() |
|
2941 { |
|
2942 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries); |
|
2943 return &mDoomedEntries == entry; |
|
2944 } |
|
2945 |
|
2946 void |
|
2947 nsCacheService::ClearDoomList() |
|
2948 { |
|
2949 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries); |
|
2950 |
|
2951 while (entry != &mDoomedEntries) { |
|
2952 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry); |
|
2953 |
|
2954 entry->DetachDescriptors(); |
|
2955 DeactivateEntry(entry); |
|
2956 entry = next; |
|
2957 } |
|
2958 } |
|
2959 |
|
2960 PLDHashOperator |
|
2961 nsCacheService::GetActiveEntries(PLDHashTable * table, |
|
2962 PLDHashEntryHdr * hdr, |
|
2963 uint32_t number, |
|
2964 void * arg) |
|
2965 { |
|
2966 static_cast<nsVoidArray *>(arg)->AppendElement( |
|
2967 ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry); |
|
2968 return PL_DHASH_NEXT; |
|
2969 } |
|
2970 |
|
2971 struct ActiveEntryArgs |
|
2972 { |
|
2973 nsTArray<nsCacheEntry*>* mActiveArray; |
|
2974 nsCacheService::DoomCheckFn mCheckFn; |
|
2975 }; |
|
2976 |
|
2977 void |
|
2978 nsCacheService::DoomActiveEntries(DoomCheckFn check) |
|
2979 { |
|
2980 nsAutoTArray<nsCacheEntry*, 8> array; |
|
2981 ActiveEntryArgs args = { &array, check }; |
|
2982 |
|
2983 mActiveEntries.VisitEntries(RemoveActiveEntry, &args); |
|
2984 |
|
2985 uint32_t count = array.Length(); |
|
2986 for (uint32_t i=0; i < count; ++i) |
|
2987 DoomEntry_Internal(array[i], true); |
|
2988 } |
|
2989 |
|
2990 PLDHashOperator |
|
2991 nsCacheService::RemoveActiveEntry(PLDHashTable * table, |
|
2992 PLDHashEntryHdr * hdr, |
|
2993 uint32_t number, |
|
2994 void * arg) |
|
2995 { |
|
2996 nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry; |
|
2997 NS_ASSERTION(entry, "### active entry = nullptr!"); |
|
2998 |
|
2999 ActiveEntryArgs* args = static_cast<ActiveEntryArgs*>(arg); |
|
3000 if (args->mCheckFn && !args->mCheckFn(entry)) |
|
3001 return PL_DHASH_NEXT; |
|
3002 |
|
3003 NS_ASSERTION(args->mActiveArray, "### array = nullptr!"); |
|
3004 args->mActiveArray->AppendElement(entry); |
|
3005 |
|
3006 // entry is being removed from the active entry list |
|
3007 entry->MarkInactive(); |
|
3008 return PL_DHASH_REMOVE; // and continue enumerating |
|
3009 } |
|
3010 |
|
3011 |
|
3012 void |
|
3013 nsCacheService::CloseAllStreams() |
|
3014 { |
|
3015 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs; |
|
3016 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs; |
|
3017 |
|
3018 { |
|
3019 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS)); |
|
3020 |
|
3021 nsVoidArray entries; |
|
3022 |
|
3023 #if DEBUG |
|
3024 // make sure there is no active entry |
|
3025 mActiveEntries.VisitEntries(GetActiveEntries, &entries); |
|
3026 NS_ASSERTION(entries.Count() == 0, "Bad state"); |
|
3027 #endif |
|
3028 |
|
3029 // Get doomed entries |
|
3030 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries); |
|
3031 while (entry != &mDoomedEntries) { |
|
3032 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry); |
|
3033 entries.AppendElement(entry); |
|
3034 entry = next; |
|
3035 } |
|
3036 |
|
3037 // Iterate through all entries and collect input and output streams |
|
3038 for (int32_t i = 0 ; i < entries.Count() ; i++) { |
|
3039 entry = static_cast<nsCacheEntry *>(entries.ElementAt(i)); |
|
3040 |
|
3041 nsTArray<nsRefPtr<nsCacheEntryDescriptor> > descs; |
|
3042 entry->GetDescriptors(descs); |
|
3043 |
|
3044 for (uint32_t j = 0 ; j < descs.Length() ; j++) { |
|
3045 if (descs[j]->mOutputWrapper) |
|
3046 outputs.AppendElement(descs[j]->mOutputWrapper); |
|
3047 |
|
3048 for (int32_t k = 0 ; k < descs[j]->mInputWrappers.Count() ; k++) |
|
3049 inputs.AppendElement(static_cast< |
|
3050 nsCacheEntryDescriptor::nsInputStreamWrapper *>( |
|
3051 descs[j]->mInputWrappers[k])); |
|
3052 } |
|
3053 } |
|
3054 } |
|
3055 |
|
3056 uint32_t i; |
|
3057 for (i = 0 ; i < inputs.Length() ; i++) |
|
3058 inputs[i]->Close(); |
|
3059 |
|
3060 for (i = 0 ; i < outputs.Length() ; i++) |
|
3061 outputs[i]->Close(); |
|
3062 } |
|
3063 |
|
3064 |
|
3065 bool |
|
3066 nsCacheService::GetClearingEntries() |
|
3067 { |
|
3068 AssertOwnsLock(); |
|
3069 return gService->mClearingEntries; |
|
3070 } |
|
3071 |
|
3072 // static |
|
3073 void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result) |
|
3074 { |
|
3075 *result = nullptr; |
|
3076 if (!gService || !gService->mObserver) |
|
3077 return; |
|
3078 |
|
3079 nsCOMPtr<nsIFile> directory = |
|
3080 gService->mObserver->DiskCacheParentDirectory(); |
|
3081 if (!directory) |
|
3082 return; |
|
3083 |
|
3084 directory->Clone(result); |
|
3085 } |
|
3086 |
|
3087 // static |
|
3088 void nsCacheService::GetDiskCacheDirectory(nsIFile ** result) |
|
3089 { |
|
3090 nsCOMPtr<nsIFile> directory; |
|
3091 GetCacheBaseDirectoty(getter_AddRefs(directory)); |
|
3092 if (!directory) |
|
3093 return; |
|
3094 |
|
3095 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache")); |
|
3096 if (NS_FAILED(rv)) |
|
3097 return; |
|
3098 |
|
3099 directory.forget(result); |
|
3100 } |
|
3101 |
|
3102 // static |
|
3103 void nsCacheService::GetAppCacheDirectory(nsIFile ** result) |
|
3104 { |
|
3105 nsCOMPtr<nsIFile> directory; |
|
3106 GetCacheBaseDirectoty(getter_AddRefs(directory)); |
|
3107 if (!directory) |
|
3108 return; |
|
3109 |
|
3110 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache")); |
|
3111 if (NS_FAILED(rv)) |
|
3112 return; |
|
3113 |
|
3114 directory.forget(result); |
|
3115 } |
|
3116 |
|
3117 |
|
3118 #if defined(PR_LOGGING) |
|
3119 void |
|
3120 nsCacheService::LogCacheStatistics() |
|
3121 { |
|
3122 uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) / |
|
3123 ((double)(mCacheHits + mCacheMisses))) * 100); |
|
3124 CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n")); |
|
3125 CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries)); |
|
3126 CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits)); |
|
3127 CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses)); |
|
3128 CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage)); |
|
3129 CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength)); |
|
3130 CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize)); |
|
3131 CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize)); |
|
3132 CACHE_LOG_ALWAYS(("\n")); |
|
3133 CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n", |
|
3134 mDeactivateFailures)); |
|
3135 CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n", |
|
3136 mDeactivatedUnboundEntries)); |
|
3137 } |
|
3138 #endif |
|
3139 |
|
3140 nsresult |
|
3141 nsCacheService::SetDiskSmartSize() |
|
3142 { |
|
3143 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE)); |
|
3144 |
|
3145 if (!gService) return NS_ERROR_NOT_AVAILABLE; |
|
3146 |
|
3147 return gService->SetDiskSmartSize_Locked(); |
|
3148 } |
|
3149 |
|
3150 nsresult |
|
3151 nsCacheService::SetDiskSmartSize_Locked() |
|
3152 { |
|
3153 nsresult rv; |
|
3154 |
|
3155 if (mozilla::net::CacheObserver::UseNewCache()) { |
|
3156 return NS_ERROR_NOT_AVAILABLE; |
|
3157 } |
|
3158 |
|
3159 if (!mObserver->DiskCacheParentDirectory()) |
|
3160 return NS_ERROR_NOT_AVAILABLE; |
|
3161 |
|
3162 if (!mDiskDevice) |
|
3163 return NS_ERROR_NOT_AVAILABLE; |
|
3164 |
|
3165 if (!mObserver->SmartSizeEnabled()) |
|
3166 return NS_ERROR_NOT_AVAILABLE; |
|
3167 |
|
3168 nsAutoString cachePath; |
|
3169 rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath); |
|
3170 if (NS_SUCCEEDED(rv)) { |
|
3171 nsCOMPtr<nsIRunnable> event = |
|
3172 new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(), |
|
3173 mObserver->ShouldUseOldMaxSmartSize()); |
|
3174 DispatchToCacheIOThread(event); |
|
3175 } else { |
|
3176 return NS_ERROR_FAILURE; |
|
3177 } |
|
3178 |
|
3179 return NS_OK; |
|
3180 } |
|
3181 |
|
3182 void |
|
3183 nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir, |
|
3184 nsIFile *aNewCacheDir, |
|
3185 const char *aCacheSubdir) |
|
3186 { |
|
3187 bool same; |
|
3188 if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same) |
|
3189 return; |
|
3190 |
|
3191 nsCOMPtr<nsIFile> aOldCacheSubdir; |
|
3192 aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir)); |
|
3193 |
|
3194 nsresult rv = aOldCacheSubdir->AppendNative( |
|
3195 nsDependentCString(aCacheSubdir)); |
|
3196 if (NS_FAILED(rv)) |
|
3197 return; |
|
3198 |
|
3199 bool exists; |
|
3200 if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists) |
|
3201 return; |
|
3202 |
|
3203 nsCOMPtr<nsIFile> aNewCacheSubdir; |
|
3204 aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir)); |
|
3205 |
|
3206 rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir)); |
|
3207 if (NS_FAILED(rv)) |
|
3208 return; |
|
3209 |
|
3210 nsAutoCString newPath; |
|
3211 rv = aNewCacheSubdir->GetNativePath(newPath); |
|
3212 if (NS_FAILED(rv)) |
|
3213 return; |
|
3214 |
|
3215 if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) { |
|
3216 // New cache directory does not exist, try to move the old one here |
|
3217 // rename needs an empty target directory |
|
3218 |
|
3219 // Make sure the parent of the target sub-dir exists |
|
3220 rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777); |
|
3221 if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) { |
|
3222 nsAutoCString oldPath; |
|
3223 rv = aOldCacheSubdir->GetNativePath(oldPath); |
|
3224 if (NS_FAILED(rv)) |
|
3225 return; |
|
3226 if (rename(oldPath.get(), newPath.get()) == 0) |
|
3227 return; |
|
3228 } |
|
3229 } |
|
3230 |
|
3231 // Delay delete by 1 minute to avoid IO thrash on startup. |
|
3232 nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000); |
|
3233 } |
|
3234 |
|
3235 static bool |
|
3236 IsEntryPrivate(nsCacheEntry* entry) |
|
3237 { |
|
3238 return entry->IsPrivate(); |
|
3239 } |
|
3240 |
|
3241 void |
|
3242 nsCacheService::LeavePrivateBrowsing() |
|
3243 { |
|
3244 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING)); |
|
3245 |
|
3246 gService->DoomActiveEntries(IsEntryPrivate); |
|
3247 |
|
3248 if (gService->mMemoryDevice) { |
|
3249 // clear memory cache |
|
3250 gService->mMemoryDevice->EvictPrivateEntries(); |
|
3251 } |
|
3252 } |
|
3253 |
|
3254 MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf) |
|
3255 |
|
3256 NS_IMETHODIMP |
|
3257 nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport, |
|
3258 nsISupports* aData) |
|
3259 { |
|
3260 size_t disk = 0; |
|
3261 if (mDiskDevice) { |
|
3262 nsCacheServiceAutoLock |
|
3263 lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE)); |
|
3264 disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf); |
|
3265 } |
|
3266 |
|
3267 size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0; |
|
3268 |
|
3269 #define REPORT(_path, _amount, _desc) \ |
|
3270 do { \ |
|
3271 nsresult rv; \ |
|
3272 rv = aHandleReport->Callback(EmptyCString(), \ |
|
3273 NS_LITERAL_CSTRING(_path), \ |
|
3274 KIND_HEAP, UNITS_BYTES, _amount, \ |
|
3275 NS_LITERAL_CSTRING(_desc), aData); \ |
|
3276 NS_ENSURE_SUCCESS(rv, rv); \ |
|
3277 } while (0) |
|
3278 |
|
3279 REPORT("explicit/network/disk-cache", disk, |
|
3280 "Memory used by the network disk cache."); |
|
3281 |
|
3282 REPORT("explicit/network/memory-cache", memory, |
|
3283 "Memory used by the network memory cache."); |
|
3284 |
|
3285 return NS_OK; |
|
3286 } |