mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
d457251529
- Bug 1120715 - Part 6: Remove the dom.requestcache.enabled pref; r=bkelly (800c996a96) - Bug 1143222 - Put the DOM Cache tests in sequential mode again until we fix the rest of the intermittent failures; a=RyanVM (2ebdd659a1) - Bug 1255636 - Give a better error message when the Request constructor fails because of a cross-origin referrer URL; r=bkelly (d81a21c0bb) - Bug 1265056 - don't needlessly construct nsAutoCString temporaries in dom/; r=baku (3be49ca3fc) - Bug 1243849 - Restore support for accessing the Cache API from app:// URLs and also for storing requests/responses with app:// URLs within it; r=bkelly (eb56fa564c) - Bug 1263235, part 1 - Move PBrowser::AsyncMessage's data argument last. r=smaug (6852b87c22) - Bug 1263235, part 2 - Make PContent::AsyncMessage and PContentBridge::AsyncMessage's data argument last. r=smaug (9e8cd94461) - Bug 1263028 - send HTTP data to the content process in smaller chunks, r=michal (c0da548157) - Bug 1263235, part 3 - Move PHttpChannel::OnTransportAndData's data argument last. r=mayhemer (e1bf4f430f) - Bug 1263235, part 4 - Make PBrowserStream::Write's data argument last. r=jimm (8bcec4d541) - Bug 1260876 - Remove process switch code for signed package code (added by Bug 1186290). r=valentin. (cef270b44c) - Bug 1234575 - Empty fragment is ignored in URI of location header r=mcmanus (db68f102d8) - Bug 1262506 - Unused variable in a runnable in BackgroundParentImpl, r=ehsan (9288f0a111) - bug 1239166 - platform work to support Microsoft Family Safety functionality r=froydnj,mgoodwin,mhowell,rbarnes,vladan (adc357f3b3) - Bug 842818 - Make Crypto::GetRandomValues() work off the main thread r=baku,keeler,mt (533f8942c4) - Bug 1247089 - Add a mode to ReportToConsoleNonLocalized that ignores the calling location. r=bkelly (4be23e0869) - Bug 1258883 - Add a way to replace the entire Push service in tests. r=wchen (06a5f27016) - Bug 1243856 - Remove alarms from the Push H2 backend. r=dragana (60d146dc73) - Bug 1246066 - Clear PushService timeout tasks on uninitialization. r=itcambridge (461276a972) - Bug 1214338 - Implement Android GCM-based PushService protocol. r=rnewman r=kitcambridge (f2bb78994a) - Bug 1257821 - Support the new aesgcm content encoding scheme. r=mt (1da653c14a) - Bug 1243856 - Remove alarms from the Push WebSocket backend. r=dragana (43f74c4999) - Bug 1258145 - Remove waitForPromise from the xpcshell tests. r=wchen (cdd1aff2f6) - Bug 1253831 - Don't check actual intervals in the Push backoff test. r=wchen (859fa0bba3) - Bug 1246341 - Include status codes in "ack" and "unregister" requests. r=dragana (a62d0daf9b) - Bug 1246341 - Add a test for push event error reporting. r=dragana (013bc814e4) - Bug 1247089 - Log Web Push decryption errors. r=bkelly (ffc093dc2f) - Bug 1258221 - patch 2 - Port FileSystem API and DeviceStorage API to PBackground, r=smaug (c1c0e08bc0) - Bug 1258221 - patch 3 - Rename FileSystemTaskBase to FileSystemTaskChildBase, r=smaug (867a0e65fd) - Bug 1251032 - Don't return layersId or textureFactoryIdentifier as outparams in RenderFrameParent constructor. r=kanru (198ddff7fc) - Bug 1251032 - Send RenderFrame info down to child in CreateWindow message. r=kanru (87e9001088) - Bug 1251032 - Make it possible to assign a frameloader to RenderFrameParent after construction. r=kanru (96483d1282) - Bug 1254865 - Send disableglobalhistory state down to TabChild after construction asynchronously. r=smaug (3949285b62) - Bug 1238707 Release the window immediately in TabParent::Destroy() to avoid leaks. r=smaug (fc612485d7) - Bug 1256589 part.1 Move the implementation of StopPropagation() from dom::Event to WidgetEvent r=smaug (181721b64c) - Bug 1256589 part.2 Move the implementation of StopImmediatePropagation() from dom::Event to WidgetEvent r=smaug (554a0dc5b5) - Bug 1203059 part.1 nsXBLWincowKeyHandler mark WidgetEvent::mFlags if it's reserved by chrome before the event is dispatched into the content r=smaug (9162dd68cb) - Bug 1203059 part.2 When an event is reserved by chrome, it should be fired only on chrome r=smaug (35f082ca5f) - Bug 1203059 part.3 Installing and removing keyboard event listeners of nsXBLWindowKeyHandler should be done by the class itself r=smaug (1e06c2d0bc) - Bug 1203059 part.4 Update test_keycodes.xul for the new behavior r=smaug (3014d21c75) - Bug 1256589 part.3 Move the implementation of StopCrossProcessForwarding() from dom::Event to WidgetEvent r=smaug (96db915b51) - Bug 1257180 - patch 1 - Directory clonable to workers, r=smaug (5634acb08d) - Bug 1257180 - patch 2 - Directory can be sent via postMessage(), r=smaug (82ada39ae3) - Bug 1263311: Part 3 - s/nsCancelableRunnable/CancelableRunnable/g. r=froydnj (79d0a6c81f) - Bug 1253198: add WebRtcIce prefix to all ICE unit tests. r=bwc (9c18f5fd56) - Bug 1244926: added TCP socket filter to only allow outgoing STUN. r=jesup (75debfdc84) - Bug 1257405 - Increase auth secret length to 16 octets. r=mt (b1e000c331) - Bug 1257401 - Remove the worker descriptor for PushSubscription. r=khuey (5791fb69ef) - Bug 1257401 - Remove the worker descriptor for PushManager. r=khuey (e2c75903a9) - Bug 1257821 - Remove the authenticated aesgcm128 content coding scheme. r=mt,marco (64a2917910) - Bug 1256488 - Add a Base64 URL-decoder for C++ and chrome JS callers. r=mt,baku (0bbb250298) - Bug 1247685 - Validate and store app server keys in the Push service. r=mt (c3c026ccb1) - Bug 1252650 - Support loading PushService immediately on Android; r=kitcambridge (d59a37fec4) - Bug 1258595 - Shut down the Push service if errors occur at startup. r=wchen (eef1805652) - Bug 1258595 - Wait for the Push service to shut down between tests. r=wchen (c30cf92ce6) - Bug 1262618 - Fix an unchained promise and a couple of non-promise returns in the push service. r=wchen (8eadab5706) - Bug 1263747 - Log error messages when stringifying errors. r=bgrins (edffd0074e) - Bug 1265705 - Silence startup JavaScript strict warning in resource://gre/modules/PushService.jsm. r=kitcambridge (e7e210fb61) - Bug 1264062 - Don't bother checking which accelerated layer types are available if they're all disabled by pref r=milan (0d3208ad59) - Bug 1263346. Remove wrong Ivy Bridge device id. r=Bas (7e39e7f370) - fix misspatch (a67a111b2c) - Bug 1251334 - Create a disposable pref to force-disable e10s in an emergency. r=jimm (ef892d4474) - Bug 1254774 - error: member access into incomplete type 'nsIUUIDGenerator' after bug 1237847. r=aklotz (c1f334609f) - Bug 1257242 - Split the ::BrowserTabsRemoteAutostart() function into two parts, to allow for the blocking policies to be checked independently from the prefs checks. r=jimm (1babda578f) - Bug 1260190 - Disable e10s for accessibility users on OS X. r=jimm (219e5b1f19) - Bug 1237769 - Disable e10s on Windows XP if layers acceleration is requested r=milan (60f2434e9f) - Bug 1232911 - [3.2] Block VPX support in ADM on unsupported devices. r=snorp (6924aa073a) - Bug 1263249 - Bubble up unique failureId in GetFeatureStatus. r=mconley,milan (cd56eeab3c) - Bug 1219296 - Split fields not needed for repaints out from FrameMetrics. r=kats (9003ca634a) - Bug 1219296 - Factor out scroll snap information into a form that's usable by the compositor. r=kats (23d3e619a1) - Bug 1219296 - Make ScrollMetadata::sNullMetadata a StaticAutoPtr so that ScrollMetadata can admit nsTArray members. r=kats (1729ff7d93) - Bug 1257641 - Replace the mUpdateScrollOffset bool with an enum, needed in the next patch. r=botond (f9d546f8e8) - Bug 1257641 - Use empty transactions to carry scroll offset updates to APZ that don't require a repaint. r=mattwoodrow,mstange,botond (ba4a8a8c29) - Bug 1246290 - Add a bit to FrameMetrics to indicate if APZ-scrolling should be disabled on that APZC. r=botond (af2067137f) - Bug 1256589 part.4 Move the implementation of PreventDefault() and add PreventDefaultBeforeDispatch() from dom::Event to WidgetEvent r=smaug (e7828f2d8f) - Bug 1256589 part.5 Add DefaultPrevented() and DefaultPreventedByContent() to WidgetEvent r=smaug (e65cdd9127) - Bug 1249915 - Fix missing MOZ_COUNT_CTOR and some misc cleanup. r=karlt (d2f26cf971) - Bug 1154183 part.1 Move shortcut/access key candidate list creators from nsContentUtils to WidgetKeyboardEvent r=smaug (40b0b11a5a) - Bug 1154183 part.2 eKeyDown event should have charCode value of following keypress event r=smaug (28c1443ba3) - Bug 1154183 part.3 Clean up some variable names in nsXBLWindowKeyHandler::WalkHandlersAndExecute() r=smaug (81e25023d8) - Bug 1154183 part.4 Implement nsXBLWindowKeyHandler::GetElementForHandler() r=smaug (b5605d5c83) - Bug 1154183 part.5 Make nsXBLWindowKeyHandler::GetElementForHandler() use early return style r=smaug (017467204f) - Bug 1154183 part.6 Add nsXBLWindowKeyHandler::IsExecuteableElement() r=smaug (1fda349113) - Bug 1154183 part.7 Don't dispatch preceding keydown events of reserved keypress events on content in the default event group r=smaug (15b9e8c9d2) - Bug 1256589 part.6 Move the implementation of IsTrusted() from dom::Event to WidgetEvent r=smaug (ec79520fd3) - Bug 1253044. Fix fall through of observer topics when other conditions aren't met in PresShell::Observe. r=dholbert (dcc36884aa) - Bug 1157546 - Replace the image visibility API with a more general API that tracks visibility for any kind of frame. r=mstange (d6ea061614) - Bug 1219296 - Factor out the algorithm that computes a scroll snap destination into a reusable form. r=kats (296cbe9e49) - Bug 1254275 - Inspect the event queue to find out whether momentum events are following. r=kats (b2bb8a26b9) - Bug 1219296 - Scroll snap directly in APZ instead of going through the main thread. r=kats (0a30b550f9) - Bug 1219296 - Fix an include-what-you-use error. r=kats (4a128ae98e) - Bug 1260588 - C++ APZ should only allow handoff to ancestor APZC r=botond (9856ab5160) - Bug 1257269 - Panning up in a scrollable element should not hide the toolbar r=kats,jchen (1036ffc9e3) - Bug 1219296 - Ship scroll snap information to the compositor. r=kats (0e920f02a1) - Bug 1219296 - Move the layout.css.scroll-snap.proximity-threshold pref to gfxPrefs, so it can be queried on the compositor thread. r=kats (2e3e1ec16e) - Bug 1219296 - Remove StartSmoothScroll()'s argument, which is no longer used. r=kats (12efcd9c79) - Bug 1219296 - Light refactoring to how a smooth scroll is launched inside APZC. r=kats (ba6a9ed9a2) - Bug 1219296 - Followup to fix stale code comments. r=me and DONTBUILD (fec5f65988) - Bug 1257641 - Remove now-unused code for the lightweight scroll offset update message. r=botond (d449e45d6d) - Bug 1236680 Part 1: Add new WinUtils function to Resolve moved Users folder. r=jimm (51d12f856c) - Bug 1236680 Part 2: Resolve GMP path for moved Users folder. r=cpearce (e568217b78) - Bug 1236680 Part 3: Add #ifs to include to fix bustage. r=me (40c38680ea) - Bug 1240315: Add startup crash report annotation for AppInit_DLLs; r=jimm (aa2040baae) - Bug 1253446 - patch 2 - Return the proper scaling factor when querying the primary screen on Windows. r=emk (9765e4f7ca) - Bug 1251624 - patch 1 - The desktop to device scaling in WinUtils::MonitorFromRect should not depend on custom CSS pixel scaling (devPixelsPerPx setting). r=emk (788b4ad5db) - Bug 1251624 - patch 2 - Check for scaling override (devPixelsPerPx setting) in nsScreenWin::GetDefaultCSSScaleFactor, for proper window positioning when a custom scale factor is used. r=emk (2843a3fe70) - Bug 1222149 - delete unused fields from AsyncEncodeAndWriteIcon; r=roc clang-cl says these are unused, so let's delete them. (62cf7f8f47) - Bug 1204809 - Notify (don't hang) third party windows when adding shortcut icon. r=jmathies (7e4058a0f8) - Bug 1253566 - Deal with char16_t/wchar_t mismatch. r=aklotz (1c6cf160c7) - Bug 1211941 - Let nsICacheStorage.openTruncate impl return an HTTP cache entry write handle, r=michal (6a5796fb93) - Bug 1050613 - Make sure force-valid for HTTP cache entries is removed when entries are removed, r=michal (9efb91eefc) - Bug 1248389 - Cache index causing CPU loops, r=honzab (175b5b27f2) - Bug 1066970 - Show 'calculating' during HTTP cache cleaning process in preferences window, r=michal (db722000d8) - Bug 1248958 - CacheIndex mRWBuf ownership too fragile, read-after-free, r=honzab (66ee3d1d0d) - Bug 1248003 - Purge from HTTP cache memory pool only in reasonable intervals, r=michal (1cd6cb5983) - Bug 1068674 - Don't turn off e10s if hardware acceleration is disabled. r=jimm (dfbef44278)
1815 lines
48 KiB
C++
1815 lines
48 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "CacheLog.h"
|
|
#include "CacheEntry.h"
|
|
#include "CacheStorageService.h"
|
|
#include "CacheObserver.h"
|
|
#include "CacheFileUtils.h"
|
|
#include "CacheIndex.h"
|
|
|
|
#include "nsIInputStream.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsISeekableStream.h"
|
|
#include "nsIURI.h"
|
|
#include "nsICacheEntryOpenCallback.h"
|
|
#include "nsICacheStorage.h"
|
|
#include "nsISerializable.h"
|
|
#include "nsIStreamTransportService.h"
|
|
#include "nsISizeOf.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsString.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsSerializationHelper.h"
|
|
#include "nsThreadUtils.h"
|
|
#include <math.h>
|
|
#include <algorithm>
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
static uint32_t const ENTRY_WANTED =
|
|
nsICacheEntryOpenCallback::ENTRY_WANTED;
|
|
static uint32_t const RECHECK_AFTER_WRITE_FINISHED =
|
|
nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED;
|
|
static uint32_t const ENTRY_NEEDS_REVALIDATION =
|
|
nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION;
|
|
static uint32_t const ENTRY_NOT_WANTED =
|
|
nsICacheEntryOpenCallback::ENTRY_NOT_WANTED;
|
|
|
|
NS_IMPL_ISUPPORTS(CacheEntryHandle, nsICacheEntry)
|
|
|
|
// CacheEntryHandle
|
|
|
|
CacheEntryHandle::CacheEntryHandle(CacheEntry* aEntry)
|
|
: mEntry(aEntry)
|
|
{
|
|
MOZ_COUNT_CTOR(CacheEntryHandle);
|
|
|
|
#ifdef DEBUG
|
|
if (!mEntry->HandlesCount()) {
|
|
// CacheEntry.mHandlesCount must go from zero to one only under
|
|
// the service lock. Can access CacheStorageService::Self() w/o a check
|
|
// since CacheEntry hrefs it.
|
|
CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
|
|
}
|
|
#endif
|
|
|
|
mEntry->AddHandleRef();
|
|
|
|
LOG(("New CacheEntryHandle %p for entry %p", this, aEntry));
|
|
}
|
|
|
|
CacheEntryHandle::~CacheEntryHandle()
|
|
{
|
|
mEntry->ReleaseHandleRef();
|
|
mEntry->OnHandleClosed(this);
|
|
|
|
MOZ_COUNT_DTOR(CacheEntryHandle);
|
|
}
|
|
|
|
// CacheEntry::Callback
|
|
|
|
CacheEntry::Callback::Callback(CacheEntry* aEntry,
|
|
nsICacheEntryOpenCallback *aCallback,
|
|
bool aReadOnly, bool aCheckOnAnyThread,
|
|
bool aSecret)
|
|
: mEntry(aEntry)
|
|
, mCallback(aCallback)
|
|
, mTargetThread(do_GetCurrentThread())
|
|
, mReadOnly(aReadOnly)
|
|
, mRevalidating(false)
|
|
, mCheckOnAnyThread(aCheckOnAnyThread)
|
|
, mRecheckAfterWrite(false)
|
|
, mNotWanted(false)
|
|
, mSecret(aSecret)
|
|
, mDoomWhenFoundPinned(false)
|
|
, mDoomWhenFoundNonPinned(false)
|
|
{
|
|
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
|
|
|
// The counter may go from zero to non-null only under the service lock
|
|
// but here we expect it to be already positive.
|
|
MOZ_ASSERT(mEntry->HandlesCount());
|
|
mEntry->AddHandleRef();
|
|
}
|
|
|
|
CacheEntry::Callback::Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus)
|
|
: mEntry(aEntry)
|
|
, mReadOnly(false)
|
|
, mRevalidating(false)
|
|
, mCheckOnAnyThread(true)
|
|
, mRecheckAfterWrite(false)
|
|
, mNotWanted(false)
|
|
, mSecret(false)
|
|
, mDoomWhenFoundPinned(aDoomWhenFoundInPinStatus == true)
|
|
, mDoomWhenFoundNonPinned(aDoomWhenFoundInPinStatus == false)
|
|
{
|
|
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
|
MOZ_ASSERT(mEntry->HandlesCount());
|
|
mEntry->AddHandleRef();
|
|
}
|
|
|
|
CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
|
|
: mEntry(aThat.mEntry)
|
|
, mCallback(aThat.mCallback)
|
|
, mTargetThread(aThat.mTargetThread)
|
|
, mReadOnly(aThat.mReadOnly)
|
|
, mRevalidating(aThat.mRevalidating)
|
|
, mCheckOnAnyThread(aThat.mCheckOnAnyThread)
|
|
, mRecheckAfterWrite(aThat.mRecheckAfterWrite)
|
|
, mNotWanted(aThat.mNotWanted)
|
|
, mSecret(aThat.mSecret)
|
|
, mDoomWhenFoundPinned(aThat.mDoomWhenFoundPinned)
|
|
, mDoomWhenFoundNonPinned(aThat.mDoomWhenFoundNonPinned)
|
|
{
|
|
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
|
|
|
// The counter may go from zero to non-null only under the service lock
|
|
// but here we expect it to be already positive.
|
|
MOZ_ASSERT(mEntry->HandlesCount());
|
|
mEntry->AddHandleRef();
|
|
}
|
|
|
|
CacheEntry::Callback::~Callback()
|
|
{
|
|
ProxyRelease(mCallback, mTargetThread);
|
|
|
|
mEntry->ReleaseHandleRef();
|
|
MOZ_COUNT_DTOR(CacheEntry::Callback);
|
|
}
|
|
|
|
void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
|
|
{
|
|
if (mEntry == aEntry)
|
|
return;
|
|
|
|
// The counter may go from zero to non-null only under the service lock
|
|
// but here we expect it to be already positive.
|
|
MOZ_ASSERT(aEntry->HandlesCount());
|
|
aEntry->AddHandleRef();
|
|
mEntry->ReleaseHandleRef();
|
|
mEntry = aEntry;
|
|
}
|
|
|
|
bool CacheEntry::Callback::DeferDoom(bool *aDoom) const
|
|
{
|
|
MOZ_ASSERT(mEntry->mPinningKnown);
|
|
|
|
if (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) || MOZ_UNLIKELY(mDoomWhenFoundPinned)) {
|
|
*aDoom = (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && MOZ_LIKELY(!mEntry->mPinned)) ||
|
|
(MOZ_UNLIKELY(mDoomWhenFoundPinned) && MOZ_UNLIKELY(mEntry->mPinned));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
|
|
{
|
|
if (!mCheckOnAnyThread) {
|
|
// Check we are on the target
|
|
return mTargetThread->IsOnCurrentThread(aOnCheckThread);
|
|
}
|
|
|
|
// We can invoke check anywhere
|
|
*aOnCheckThread = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread) const
|
|
{
|
|
return mTargetThread->IsOnCurrentThread(aOnAvailThread);
|
|
}
|
|
|
|
// CacheEntry
|
|
|
|
NS_IMPL_ISUPPORTS(CacheEntry,
|
|
nsICacheEntry,
|
|
nsIRunnable,
|
|
CacheFileListener)
|
|
|
|
CacheEntry::CacheEntry(const nsACString& aStorageID,
|
|
nsIURI* aURI,
|
|
const nsACString& aEnhanceID,
|
|
bool aUseDisk,
|
|
bool aSkipSizeCheck,
|
|
bool aPin)
|
|
: mFrecency(0)
|
|
, mSortingExpirationTime(uint32_t(-1))
|
|
, mLock("CacheEntry")
|
|
, mFileStatus(NS_ERROR_NOT_INITIALIZED)
|
|
, mURI(aURI)
|
|
, mEnhanceID(aEnhanceID)
|
|
, mStorageID(aStorageID)
|
|
, mUseDisk(aUseDisk)
|
|
, mSkipSizeCheck(aSkipSizeCheck)
|
|
, mSecurityInfoLoaded(false)
|
|
, mPreventCallbacks(false)
|
|
, mHasData(false)
|
|
, mPinned(aPin)
|
|
, mPinningKnown(false)
|
|
, mIsDoomed(false)
|
|
, mState(NOTLOADED)
|
|
, mRegistration(NEVERREGISTERED)
|
|
, mWriter(nullptr)
|
|
, mPredictedDataSize(0)
|
|
, mUseCount(0)
|
|
, mReleaseThread(NS_GetCurrentThread())
|
|
{
|
|
MOZ_COUNT_CTOR(CacheEntry);
|
|
|
|
mService = CacheStorageService::Self();
|
|
|
|
CacheStorageService::Self()->RecordMemoryOnlyEntry(
|
|
this, !aUseDisk, true /* overwrite */);
|
|
}
|
|
|
|
CacheEntry::~CacheEntry()
|
|
{
|
|
ProxyRelease(mURI, mReleaseThread);
|
|
|
|
LOG(("CacheEntry::~CacheEntry [this=%p]", this));
|
|
MOZ_COUNT_DTOR(CacheEntry);
|
|
}
|
|
|
|
char const * CacheEntry::StateString(uint32_t aState)
|
|
{
|
|
switch (aState) {
|
|
case NOTLOADED: return "NOTLOADED";
|
|
case LOADING: return "LOADING";
|
|
case EMPTY: return "EMPTY";
|
|
case WRITING: return "WRITING";
|
|
case READY: return "READY";
|
|
case REVALIDATING: return "REVALIDATING";
|
|
}
|
|
|
|
return "?";
|
|
}
|
|
|
|
nsresult CacheEntry::HashingKeyWithStorage(nsACString &aResult) const
|
|
{
|
|
return HashingKey(mStorageID, mEnhanceID, mURI, aResult);
|
|
}
|
|
|
|
nsresult CacheEntry::HashingKey(nsACString &aResult) const
|
|
{
|
|
return HashingKey(EmptyCString(), mEnhanceID, mURI, aResult);
|
|
}
|
|
|
|
// static
|
|
nsresult CacheEntry::HashingKey(nsCSubstring const& aStorageID,
|
|
nsCSubstring const& aEnhanceID,
|
|
nsIURI* aURI,
|
|
nsACString &aResult)
|
|
{
|
|
nsAutoCString spec;
|
|
nsresult rv = aURI->GetAsciiSpec(spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return HashingKey(aStorageID, aEnhanceID, spec, aResult);
|
|
}
|
|
|
|
// static
|
|
nsresult CacheEntry::HashingKey(nsCSubstring const& aStorageID,
|
|
nsCSubstring const& aEnhanceID,
|
|
nsCSubstring const& aURISpec,
|
|
nsACString &aResult)
|
|
{
|
|
/**
|
|
* This key is used to salt hash that is a base for disk file name.
|
|
* Changing it will cause we will not be able to find files on disk.
|
|
*/
|
|
|
|
aResult.Assign(aStorageID);
|
|
|
|
if (!aEnhanceID.IsEmpty()) {
|
|
CacheFileUtils::AppendTagWithValue(aResult, '~', aEnhanceID);
|
|
}
|
|
|
|
// Appending directly
|
|
aResult.Append(':');
|
|
aResult.Append(aURISpec);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
|
|
{
|
|
LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
|
|
this, StateString(mState), aFlags, aCallback));
|
|
|
|
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
|
|
bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
|
|
bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
|
|
bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
|
|
bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
|
|
bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY;
|
|
|
|
MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
|
|
MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
|
|
|
|
Callback callback(this, aCallback, readonly, multithread, secret);
|
|
|
|
if (!Open(callback, truncate, priority, bypassIfBusy)) {
|
|
// We get here when the callback wants to bypass cache when it's busy.
|
|
LOG((" writing or revalidating, callback wants to bypass cache"));
|
|
callback.mNotWanted = true;
|
|
InvokeAvailableCallback(callback);
|
|
}
|
|
}
|
|
|
|
bool CacheEntry::Open(Callback & aCallback, bool aTruncate,
|
|
bool aPriority, bool aBypassIfBusy)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
// Check state under the lock
|
|
if (aBypassIfBusy && (mState == WRITING || mState == REVALIDATING)) {
|
|
return false;
|
|
}
|
|
|
|
RememberCallback(aCallback);
|
|
|
|
// Load() opens the lock
|
|
if (Load(aTruncate, aPriority)) {
|
|
// Loading is in progress...
|
|
return true;
|
|
}
|
|
|
|
InvokeCallbacks();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CacheEntry::Load(bool aTruncate, bool aPriority)
|
|
{
|
|
LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate));
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
if (mState > LOADING) {
|
|
LOG((" already loaded"));
|
|
return false;
|
|
}
|
|
|
|
if (mState == LOADING) {
|
|
LOG((" already loading"));
|
|
return true;
|
|
}
|
|
|
|
mState = LOADING;
|
|
|
|
MOZ_ASSERT(!mFile);
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoCString fileKey;
|
|
rv = HashingKeyWithStorage(fileKey);
|
|
|
|
bool reportMiss = false;
|
|
|
|
// Check the index under two conditions for two states and take appropriate action:
|
|
// 1. When this is a disk entry and not told to truncate, check there is a disk file.
|
|
// If not, set the 'truncate' flag to true so that this entry will open instantly
|
|
// as a new one.
|
|
// 2. When this is a memory-only entry, check there is a disk file.
|
|
// If there is or could be, doom that file.
|
|
if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) {
|
|
// Check the index right now to know we have or have not the entry
|
|
// as soon as possible.
|
|
CacheIndex::EntryStatus status;
|
|
if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
|
|
switch (status) {
|
|
case CacheIndex::DOES_NOT_EXIST:
|
|
// Doesn't apply to memory-only entries, Load() is called only once for them
|
|
// and never again for their session lifetime.
|
|
if (!aTruncate && mUseDisk) {
|
|
LOG((" entry doesn't exist according information from the index, truncating"));
|
|
reportMiss = true;
|
|
aTruncate = true;
|
|
}
|
|
break;
|
|
case CacheIndex::EXISTS:
|
|
case CacheIndex::DO_NOT_KNOW:
|
|
if (!mUseDisk) {
|
|
LOG((" entry open as memory-only, but there is a file, status=%d, dooming it", status));
|
|
CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mFile = new CacheFile();
|
|
|
|
BackgroundOp(Ops::REGISTER);
|
|
|
|
bool directLoad = aTruncate || !mUseDisk;
|
|
if (directLoad) {
|
|
mPinningKnown = true;
|
|
}
|
|
|
|
{
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
|
|
if (reportMiss) {
|
|
/*CacheFileUtils::DetailedCacheHitTelemetry::AddRecord(
|
|
CacheFileUtils::DetailedCacheHitTelemetry::MISS, mLoadStart);*/
|
|
}
|
|
|
|
LOG((" performing load, file=%p", mFile.get()));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = mFile->Init(fileKey,
|
|
aTruncate,
|
|
!mUseDisk,
|
|
mSkipSizeCheck,
|
|
aPriority,
|
|
mPinned,
|
|
directLoad ? nullptr : this);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
mFileStatus = rv;
|
|
AsyncDoom(nullptr);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (directLoad) {
|
|
// Just fake the load has already been done as "new".
|
|
mFileStatus = NS_OK;
|
|
mState = EMPTY;
|
|
}
|
|
|
|
return mState == LOADING;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
|
|
{
|
|
LOG(("CacheEntry::OnFileReady [this=%p, rv=0x%08x, new=%d]",
|
|
this, aResult, aIsNew));
|
|
|
|
// OnFileReady, that is the only code that can transit from LOADING
|
|
// to any follow-on state and can only be invoked ones on an entry.
|
|
// Until this moment there is no consumer that could manipulate
|
|
// the entry state.
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
MOZ_ASSERT(mState == LOADING);
|
|
|
|
mState = (aIsNew || NS_FAILED(aResult))
|
|
? EMPTY
|
|
: READY;
|
|
|
|
mFileStatus = aResult;
|
|
|
|
mPinned = mFile->IsPinned();;
|
|
mPinningKnown = true;
|
|
LOG((" pinning=%d", mPinned));
|
|
|
|
if (mState == READY) {
|
|
mHasData = true;
|
|
|
|
uint32_t frecency;
|
|
mFile->GetFrecency(&frecency);
|
|
// mFrecency is held in a double to increase computance precision.
|
|
// It is ok to persist frecency only as a uint32 with some math involved.
|
|
mFrecency = INT2FRECENCY(frecency);
|
|
}
|
|
|
|
InvokeCallbacks();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::OnFileDoomed(nsresult aResult)
|
|
{
|
|
if (mDoomCallback) {
|
|
RefPtr<DoomCallbackRunnable> event =
|
|
new DoomCallbackRunnable(this, aResult);
|
|
NS_DispatchToMainThread(event);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
|
|
nsICacheEntryOpenCallback* aCallback)
|
|
{
|
|
LOG(("CacheEntry::ReopenTruncated [this=%p]", this));
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
// Hold callbacks invocation, AddStorageEntry would invoke from doom prematurly
|
|
mPreventCallbacks = true;
|
|
|
|
RefPtr<CacheEntryHandle> handle;
|
|
RefPtr<CacheEntry> newEntry;
|
|
{
|
|
if (mPinned) {
|
|
MOZ_ASSERT(mUseDisk);
|
|
// We want to pin even no-store entries (the case we recreate a disk entry as
|
|
// a memory-only entry.)
|
|
aMemoryOnly = false;
|
|
}
|
|
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
|
|
// The following call dooms this entry (calls DoomAlreadyRemoved on us)
|
|
nsresult rv = CacheStorageService::Self()->AddStorageEntry(
|
|
GetStorageID(), GetURI(), GetEnhanceID(),
|
|
mUseDisk && !aMemoryOnly,
|
|
mSkipSizeCheck,
|
|
mPinned,
|
|
true, // truncate existing (this one)
|
|
getter_AddRefs(handle));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
newEntry = handle->Entry();
|
|
LOG((" exchanged entry %p by entry %p, rv=0x%08x", this, newEntry.get(), rv));
|
|
newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE);
|
|
} else {
|
|
LOG((" exchanged of entry %p failed, rv=0x%08x", this, rv));
|
|
AsyncDoom(nullptr);
|
|
}
|
|
}
|
|
|
|
mPreventCallbacks = false;
|
|
|
|
if (!newEntry)
|
|
return nullptr;
|
|
|
|
newEntry->TransferCallbacks(*this);
|
|
mCallbacks.Clear();
|
|
|
|
// Must return a new write handle, since the consumer is expected to
|
|
// write to this newly recreated entry. The |handle| is only a common
|
|
// reference counter and doesn't revert entry state back when write
|
|
// fails and also doesn't update the entry frecency. Not updating
|
|
// frecency causes entries to not be purged from our memory pools.
|
|
RefPtr<CacheEntryHandle> writeHandle =
|
|
newEntry->NewWriteHandle();
|
|
return writeHandle.forget();
|
|
}
|
|
|
|
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
|
|
this, &aFromEntry));
|
|
|
|
if (!mCallbacks.Length())
|
|
mCallbacks.SwapElements(aFromEntry.mCallbacks);
|
|
else
|
|
mCallbacks.AppendElements(aFromEntry.mCallbacks);
|
|
|
|
uint32_t callbacksLength = mCallbacks.Length();
|
|
if (callbacksLength) {
|
|
// Carry the entry reference (unfortunatelly, needs to be done manually...)
|
|
for (uint32_t i = 0; i < callbacksLength; ++i)
|
|
mCallbacks[i].ExchangeEntry(this);
|
|
|
|
BackgroundOp(Ops::CALLBACKS, true);
|
|
}
|
|
}
|
|
|
|
void CacheEntry::RememberCallback(Callback & aCallback)
|
|
{
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
|
|
this, aCallback.mCallback.get(), StateString(mState)));
|
|
|
|
mCallbacks.AppendElement(aCallback);
|
|
}
|
|
|
|
void CacheEntry::InvokeCallbacksLock()
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
InvokeCallbacks();
|
|
}
|
|
|
|
void CacheEntry::InvokeCallbacks()
|
|
{
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
|
|
|
|
// Invoke first all r/w callbacks, then all r/o callbacks.
|
|
if (InvokeCallbacks(false))
|
|
InvokeCallbacks(true);
|
|
|
|
LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
|
|
}
|
|
|
|
bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
|
{
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
RefPtr<CacheEntryHandle> recreatedHandle;
|
|
|
|
uint32_t i = 0;
|
|
while (i < mCallbacks.Length()) {
|
|
if (mPreventCallbacks) {
|
|
LOG((" callbacks prevented!"));
|
|
return false;
|
|
}
|
|
|
|
if (!mIsDoomed && (mState == WRITING || mState == REVALIDATING)) {
|
|
LOG((" entry is being written/revalidated"));
|
|
return false;
|
|
}
|
|
|
|
bool recreate;
|
|
if (mCallbacks[i].DeferDoom(&recreate)) {
|
|
mCallbacks.RemoveElementAt(i);
|
|
if (!recreate) {
|
|
continue;
|
|
}
|
|
|
|
LOG((" defer doom marker callback hit positive, recreating"));
|
|
recreatedHandle = ReopenTruncated(!mUseDisk, nullptr);
|
|
break;
|
|
}
|
|
|
|
if (mCallbacks[i].mReadOnly != aReadOnly) {
|
|
// Callback is not r/w or r/o, go to another one in line
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
bool onCheckThread;
|
|
nsresult rv = mCallbacks[i].OnCheckThread(&onCheckThread);
|
|
|
|
if (NS_SUCCEEDED(rv) && !onCheckThread) {
|
|
// Redispatch to the target thread
|
|
RefPtr<nsRunnableMethod<CacheEntry> > event =
|
|
NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock);
|
|
|
|
rv = mCallbacks[i].mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
LOG((" re-dispatching to target thread"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Callback callback = mCallbacks[i];
|
|
mCallbacks.RemoveElementAt(i);
|
|
|
|
if (NS_SUCCEEDED(rv) && !InvokeCallback(callback)) {
|
|
// Callback didn't fire, put it back and go to another one in line.
|
|
// Only reason InvokeCallback returns false is that onCacheEntryCheck
|
|
// returns RECHECK_AFTER_WRITE_FINISHED. If we would stop the loop, other
|
|
// readers or potential writers would be unnecessarily kept from being
|
|
// invoked.
|
|
size_t pos = std::min(mCallbacks.Length(), static_cast<size_t>(i));
|
|
mCallbacks.InsertElementAt(pos, callback);
|
|
++i;
|
|
}
|
|
}
|
|
|
|
if (recreatedHandle) {
|
|
// Must be released outside of the lock, enters InvokeCallback on the new entry
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
recreatedHandle = nullptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CacheEntry::InvokeCallback(Callback & aCallback)
|
|
{
|
|
LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
|
|
this, StateString(mState), aCallback.mCallback.get()));
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
// When this entry is doomed we want to notify the callback any time
|
|
if (!mIsDoomed) {
|
|
// When we are here, the entry must be loaded from disk
|
|
MOZ_ASSERT(mState > LOADING);
|
|
|
|
if (mState == WRITING || mState == REVALIDATING) {
|
|
// Prevent invoking other callbacks since one of them is now writing
|
|
// or revalidating this entry. No consumers should get this entry
|
|
// until metadata are filled with values downloaded from the server
|
|
// or the entry revalidated and output stream has been opened.
|
|
LOG((" entry is being written/revalidated, callback bypassed"));
|
|
return false;
|
|
}
|
|
|
|
// mRecheckAfterWrite flag already set means the callback has already passed
|
|
// the onCacheEntryCheck call. Until the current write is not finished this
|
|
// callback will be bypassed.
|
|
if (!aCallback.mRecheckAfterWrite) {
|
|
|
|
if (!aCallback.mReadOnly) {
|
|
if (mState == EMPTY) {
|
|
// Advance to writing state, we expect to invoke the callback and let
|
|
// it fill content of this entry. Must set and check the state here
|
|
// to prevent more then one
|
|
mState = WRITING;
|
|
LOG((" advancing to WRITING state"));
|
|
}
|
|
|
|
if (!aCallback.mCallback) {
|
|
// We can be given no callback only in case of recreate, it is ok
|
|
// to advance to WRITING state since the caller of recreate is expected
|
|
// to write this entry now.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (mState == READY) {
|
|
// Metadata present, validate the entry
|
|
uint32_t checkResult;
|
|
{
|
|
// mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
|
|
nsresult rv = aCallback.mCallback->OnCacheEntryCheck(
|
|
this, nullptr, &checkResult);
|
|
LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));
|
|
|
|
if (NS_FAILED(rv))
|
|
checkResult = ENTRY_NOT_WANTED;
|
|
}
|
|
|
|
aCallback.mRevalidating = checkResult == ENTRY_NEEDS_REVALIDATION;
|
|
|
|
switch (checkResult) {
|
|
case ENTRY_WANTED:
|
|
// Nothing more to do here, the consumer is responsible to handle
|
|
// the result of OnCacheEntryCheck it self.
|
|
// Proceed to callback...
|
|
break;
|
|
|
|
case RECHECK_AFTER_WRITE_FINISHED:
|
|
LOG((" consumer will check on the entry again after write is done"));
|
|
// The consumer wants the entry to complete first.
|
|
aCallback.mRecheckAfterWrite = true;
|
|
break;
|
|
|
|
case ENTRY_NEEDS_REVALIDATION:
|
|
LOG((" will be holding callbacks until entry is revalidated"));
|
|
// State is READY now and from that state entry cannot transit to any other
|
|
// state then REVALIDATING for which cocurrency is not an issue. Potentially
|
|
// no need to lock here.
|
|
mState = REVALIDATING;
|
|
break;
|
|
|
|
case ENTRY_NOT_WANTED:
|
|
LOG((" consumer not interested in the entry"));
|
|
// Do not give this entry to the consumer, it is not interested in us.
|
|
aCallback.mNotWanted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aCallback.mCallback) {
|
|
if (!mIsDoomed && aCallback.mRecheckAfterWrite) {
|
|
// If we don't have data and the callback wants a complete entry,
|
|
// don't invoke now.
|
|
bool bypass = !mHasData;
|
|
if (!bypass && NS_SUCCEEDED(mFileStatus)) {
|
|
int64_t _unused;
|
|
bypass = !mFile->DataSize(&_unused);
|
|
}
|
|
|
|
if (bypass) {
|
|
LOG((" bypassing, entry data still being written"));
|
|
return false;
|
|
}
|
|
|
|
// Entry is complete now, do the check+avail call again
|
|
aCallback.mRecheckAfterWrite = false;
|
|
return InvokeCallback(aCallback);
|
|
}
|
|
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
InvokeAvailableCallback(aCallback);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
|
|
{
|
|
LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
|
|
this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));
|
|
|
|
nsresult rv;
|
|
|
|
uint32_t const state = mState;
|
|
|
|
// When we are here, the entry must be loaded from disk
|
|
MOZ_ASSERT(state > LOADING || mIsDoomed);
|
|
|
|
bool onAvailThread;
|
|
rv = aCallback.OnAvailThread(&onAvailThread);
|
|
if (NS_FAILED(rv)) {
|
|
LOG((" target thread dead?"));
|
|
return;
|
|
}
|
|
|
|
if (!onAvailThread) {
|
|
// Dispatch to the right thread
|
|
RefPtr<AvailableCallbackRunnable> event =
|
|
new AvailableCallbackRunnable(this, aCallback);
|
|
|
|
rv = aCallback.mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
|
|
LOG((" redispatched, (rv = 0x%08x)", rv));
|
|
return;
|
|
}
|
|
|
|
if (NS_SUCCEEDED(mFileStatus) && !aCallback.mSecret) {
|
|
// Let the last-fetched and fetch-count properties be updated.
|
|
mFile->OnFetched();
|
|
}
|
|
|
|
if (mIsDoomed || aCallback.mNotWanted) {
|
|
LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
|
aCallback.mCallback->OnCacheEntryAvailable(
|
|
nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (state == READY) {
|
|
LOG((" ready/has-meta, notifying OCEA with entry and NS_OK"));
|
|
|
|
if (!aCallback.mSecret)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
BackgroundOp(Ops::FRECENCYUPDATE);
|
|
}
|
|
|
|
RefPtr<CacheEntryHandle> handle = NewHandle();
|
|
aCallback.mCallback->OnCacheEntryAvailable(
|
|
handle, false, nullptr, NS_OK);
|
|
return;
|
|
}
|
|
|
|
// R/O callbacks may do revalidation, let them fall through
|
|
if (aCallback.mReadOnly && !aCallback.mRevalidating) {
|
|
LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
|
aCallback.mCallback->OnCacheEntryAvailable(
|
|
nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
// This is a new or potentially non-valid entry and needs to be fetched first.
|
|
// The CacheEntryHandle blocks other consumers until the channel
|
|
// either releases the entry or marks metadata as filled or whole entry valid,
|
|
// i.e. until MetaDataReady() or SetValid() on the entry is called respectively.
|
|
|
|
// Consumer will be responsible to fill or validate the entry metadata and data.
|
|
|
|
RefPtr<CacheEntryHandle> handle = NewWriteHandle();
|
|
rv = aCallback.mCallback->OnCacheEntryAvailable(
|
|
handle, state == WRITING, nullptr, NS_OK);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
LOG((" writing/revalidating failed (0x%08x)", rv));
|
|
|
|
// Consumer given a new entry failed to take care of the entry.
|
|
OnHandleClosed(handle);
|
|
return;
|
|
}
|
|
|
|
LOG((" writing/revalidating"));
|
|
}
|
|
|
|
CacheEntryHandle* CacheEntry::NewHandle()
|
|
{
|
|
return new CacheEntryHandle(this);
|
|
}
|
|
|
|
CacheEntryHandle* CacheEntry::NewWriteHandle()
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
// Ignore the OPEN_SECRETLY flag on purpose here, which should actually be
|
|
// used only along with OPEN_READONLY, but there is no need to enforce that.
|
|
BackgroundOp(Ops::FRECENCYUPDATE);
|
|
|
|
return (mWriter = NewHandle());
|
|
}
|
|
|
|
void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle)
|
|
{
|
|
LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle));
|
|
|
|
nsCOMPtr<nsIOutputStream> outputStream;
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
if (mWriter != aHandle) {
|
|
LOG((" not the writer"));
|
|
return;
|
|
}
|
|
|
|
if (mOutputStream) {
|
|
// No one took our internal output stream, so there are no data
|
|
// and output stream has to be open symultaneously with input stream
|
|
// on this entry again.
|
|
mHasData = false;
|
|
}
|
|
|
|
outputStream.swap(mOutputStream);
|
|
mWriter = nullptr;
|
|
|
|
if (mState == WRITING) {
|
|
LOG((" reverting to state EMPTY - write failed"));
|
|
mState = EMPTY;
|
|
}
|
|
else if (mState == REVALIDATING) {
|
|
LOG((" reverting to state READY - reval failed"));
|
|
mState = READY;
|
|
}
|
|
|
|
if (mState == READY && !mHasData) {
|
|
// We may get to this state when following steps happen:
|
|
// 1. a new entry is given to a consumer
|
|
// 2. the consumer calls MetaDataReady(), we transit to READY
|
|
// 3. abandons the entry w/o opening the output stream, mHasData left false
|
|
//
|
|
// In this case any following consumer will get a ready entry (with metadata)
|
|
// but in state like the entry data write was still happening (was in progress)
|
|
// and will indefinitely wait for the entry data or even the entry itself when
|
|
// RECHECK_AFTER_WRITE is returned from onCacheEntryCheck.
|
|
LOG((" we are in READY state, pretend we have data regardless it"
|
|
" has actully been never touched"));
|
|
mHasData = true;
|
|
}
|
|
|
|
InvokeCallbacks();
|
|
}
|
|
|
|
if (outputStream) {
|
|
LOG((" abandoning phantom output stream"));
|
|
outputStream->Close();
|
|
}
|
|
}
|
|
|
|
void CacheEntry::OnOutputClosed()
|
|
{
|
|
// Called when the file's output stream is closed. Invoke any callbacks
|
|
// waiting for complete entry.
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
InvokeCallbacks();
|
|
}
|
|
|
|
bool CacheEntry::IsReferenced() const
|
|
{
|
|
CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
|
|
|
|
// Increasing this counter from 0 to non-null and this check both happen only
|
|
// under the service lock.
|
|
return mHandlesCount > 0;
|
|
}
|
|
|
|
bool CacheEntry::IsFileDoomed()
|
|
{
|
|
if (NS_SUCCEEDED(mFileStatus)) {
|
|
return mFile->IsDoomed();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t CacheEntry::GetMetadataMemoryConsumption()
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, 0);
|
|
|
|
uint32_t size;
|
|
if (NS_FAILED(mFile->ElementsSize(&size)))
|
|
return 0;
|
|
|
|
return size;
|
|
}
|
|
|
|
// nsICacheEntry
|
|
|
|
NS_IMETHODIMP CacheEntry::GetPersistent(bool *aPersistToDisk)
|
|
{
|
|
// No need to sync when only reading.
|
|
// When consumer needs to be consistent with state of the memory storage entries
|
|
// table, then let it use GetUseDisk getter that must be called under the service lock.
|
|
*aPersistToDisk = mUseDisk;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetKey(nsACString & aKey)
|
|
{
|
|
return mURI->GetAsciiSpec(aKey);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetFetchCount(int32_t *aFetchCount)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->GetFetchCount(reinterpret_cast<uint32_t*>(aFetchCount));
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetLastFetched(uint32_t *aLastFetched)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->GetLastFetched(aLastFetched);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetLastModified(uint32_t *aLastModified)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->GetLastModified(aLastModified);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetExpirationTime(uint32_t *aExpirationTime)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->GetExpirationTime(aExpirationTime);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetIsForcedValid(bool *aIsForcedValid)
|
|
{
|
|
NS_ENSURE_ARG(aIsForcedValid);
|
|
|
|
MOZ_ASSERT(mState > LOADING);
|
|
|
|
if (mPinned) {
|
|
*aIsForcedValid = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString key;
|
|
nsresult rv = HashingKey(key);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
*aIsForcedValid = CacheStorageService::Self()->IsForcedValidEntry(mStorageID, key);
|
|
LOG(("CacheEntry::GetIsForcedValid [this=%p, IsForcedValid=%d]", this, *aIsForcedValid));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::ForceValidFor(uint32_t aSecondsToTheFuture)
|
|
{
|
|
LOG(("CacheEntry::ForceValidFor [this=%p, aSecondsToTheFuture=%d]", this, aSecondsToTheFuture));
|
|
|
|
nsAutoCString key;
|
|
nsresult rv = HashingKey(key);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
CacheStorageService::Self()->ForceEntryValidFor(mStorageID, key, aSecondsToTheFuture);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::SetExpirationTime(uint32_t aExpirationTime)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
nsresult rv = mFile->SetExpirationTime(aExpirationTime);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Aligned assignment, thus atomic.
|
|
mSortingExpirationTime = aExpirationTime;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::OpenInputStream(int64_t offset, nsIInputStream * *_retval)
|
|
{
|
|
LOG(("CacheEntry::OpenInputStream [this=%p]", this));
|
|
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
rv = mFile->OpenInputStream(getter_AddRefs(stream));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsISeekableStream> seekable =
|
|
do_QueryInterface(stream, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
if (!mHasData) {
|
|
// So far output stream on this new entry not opened, do it now.
|
|
LOG((" creating phantom output stream"));
|
|
rv = OpenOutputStreamInternal(0, getter_AddRefs(mOutputStream));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
stream.forget(_retval);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::OpenOutputStream(int64_t offset, nsIOutputStream * *_retval)
|
|
{
|
|
LOG(("CacheEntry::OpenOutputStream [this=%p]", this));
|
|
|
|
nsresult rv;
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
MOZ_ASSERT(mState > EMPTY);
|
|
|
|
if (mOutputStream && !mIsDoomed) {
|
|
LOG((" giving phantom output stream"));
|
|
mOutputStream.forget(_retval);
|
|
}
|
|
else {
|
|
rv = OpenOutputStreamInternal(offset, _retval);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
// Entry considered ready when writer opens output stream.
|
|
if (mState < READY)
|
|
mState = READY;
|
|
|
|
// Invoke any pending readers now.
|
|
InvokeCallbacks();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult CacheEntry::OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval)
|
|
{
|
|
LOG(("CacheEntry::OpenOutputStreamInternal [this=%p]", this));
|
|
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
if (mIsDoomed) {
|
|
LOG((" doomed..."));
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
MOZ_ASSERT(mState > LOADING);
|
|
|
|
nsresult rv;
|
|
|
|
// No need to sync on mUseDisk here, we don't need to be consistent
|
|
// with content of the memory storage entries hash table.
|
|
if (!mUseDisk) {
|
|
rv = mFile->SetMemoryOnly();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
RefPtr<CacheOutputCloseListener> listener =
|
|
new CacheOutputCloseListener(this);
|
|
|
|
nsCOMPtr<nsIOutputStream> stream;
|
|
rv = mFile->OpenOutputStream(listener, getter_AddRefs(stream));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsISeekableStream> seekable =
|
|
do_QueryInterface(stream, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Prevent opening output stream again.
|
|
mHasData = true;
|
|
|
|
stream.swap(*_retval);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetPredictedDataSize(int64_t *aPredictedDataSize)
|
|
{
|
|
*aPredictedDataSize = mPredictedDataSize;
|
|
return NS_OK;
|
|
}
|
|
NS_IMETHODIMP CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize)
|
|
{
|
|
mPredictedDataSize = aPredictedDataSize;
|
|
|
|
if (!mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) {
|
|
LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
|
|
AsyncDoom(nullptr);
|
|
|
|
return NS_ERROR_FILE_TOO_BIG;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetSecurityInfo(nsISupports * *aSecurityInfo)
|
|
{
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
if (mSecurityInfoLoaded) {
|
|
NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
nsXPIDLCString info;
|
|
nsCOMPtr<nsISupports> secInfo;
|
|
nsresult rv;
|
|
|
|
rv = mFile->GetElement("security-info", getter_Copies(info));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (info) {
|
|
rv = NS_DeserializeObject(info, getter_AddRefs(secInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
mSecurityInfo.swap(secInfo);
|
|
mSecurityInfoLoaded = true;
|
|
|
|
NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
NS_IMETHODIMP CacheEntry::SetSecurityInfo(nsISupports *aSecurityInfo)
|
|
{
|
|
nsresult rv;
|
|
|
|
NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
mSecurityInfo = aSecurityInfo;
|
|
mSecurityInfoLoaded = true;
|
|
}
|
|
|
|
nsCOMPtr<nsISerializable> serializable =
|
|
do_QueryInterface(aSecurityInfo);
|
|
if (aSecurityInfo && !serializable)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
nsCString info;
|
|
if (serializable) {
|
|
rv = NS_SerializeToString(serializable, info);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
rv = mFile->SetElement("security-info", info.Length() ? info.get() : nullptr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetStorageDataSize(uint32_t *aStorageDataSize)
|
|
{
|
|
NS_ENSURE_ARG(aStorageDataSize);
|
|
|
|
int64_t dataSize;
|
|
nsresult rv = GetDataSize(&dataSize);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
*aStorageDataSize = (uint32_t)std::min(int64_t(uint32_t(-1)), dataSize);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::AsyncDoom(nsICacheEntryDoomCallback *aCallback)
|
|
{
|
|
LOG(("CacheEntry::AsyncDoom [this=%p]", this));
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
if (mIsDoomed || mDoomCallback)
|
|
return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state
|
|
|
|
RemoveForcedValidity();
|
|
|
|
mIsDoomed = true;
|
|
mDoomCallback = aCallback;
|
|
}
|
|
|
|
// This immediately removes the entry from the master hashtable and also
|
|
// immediately dooms the file. This way we make sure that any consumer
|
|
// after this point asking for the same entry won't get
|
|
// a) this entry
|
|
// b) a new entry with the same file
|
|
PurgeAndDoom();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetMetaDataElement(const char * aKey, char * *aRetval)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->GetElement(aKey, aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::SetMetaDataElement(const char * aKey, const char * aValue)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->SetElement(aKey, aValue);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::VisitMetaData(nsICacheEntryMetaDataVisitor *aVisitor)
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
return mFile->VisitMetaData(aVisitor);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::MetaDataReady()
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState)));
|
|
|
|
MOZ_ASSERT(mState > EMPTY);
|
|
|
|
if (mState == WRITING)
|
|
mState = READY;
|
|
|
|
InvokeCallbacks();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::SetValid()
|
|
{
|
|
LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState)));
|
|
|
|
nsCOMPtr<nsIOutputStream> outputStream;
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
MOZ_ASSERT(mState > EMPTY);
|
|
|
|
mState = READY;
|
|
mHasData = true;
|
|
|
|
InvokeCallbacks();
|
|
|
|
outputStream.swap(mOutputStream);
|
|
}
|
|
|
|
if (outputStream) {
|
|
LOG((" abandoning phantom output stream"));
|
|
outputStream->Close();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::Recreate(bool aMemoryOnly,
|
|
nsICacheEntry **_retval)
|
|
{
|
|
LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
RefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
|
|
if (handle) {
|
|
handle.forget(_retval);
|
|
return NS_OK;
|
|
}
|
|
|
|
BackgroundOp(Ops::CALLBACKS, true);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::GetDataSize(int64_t *aDataSize)
|
|
{
|
|
LOG(("CacheEntry::GetDataSize [this=%p]", this));
|
|
*aDataSize = 0;
|
|
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
if (!mHasData) {
|
|
LOG((" write in progress (no data)"));
|
|
return NS_ERROR_IN_PROGRESS;
|
|
}
|
|
}
|
|
|
|
NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
|
|
|
|
// mayhemer: TODO Problem with compression?
|
|
if (!mFile->DataSize(aDataSize)) {
|
|
LOG((" write in progress (stream active)"));
|
|
return NS_ERROR_IN_PROGRESS;
|
|
}
|
|
|
|
LOG((" size=%lld", *aDataSize));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::MarkValid()
|
|
{
|
|
// NOT IMPLEMENTED ACTUALLY
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::MaybeMarkValid()
|
|
{
|
|
// NOT IMPLEMENTED ACTUALLY
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::HasWriteAccess(bool aWriteAllowed, bool *aWriteAccess)
|
|
{
|
|
*aWriteAccess = aWriteAllowed;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP CacheEntry::Close()
|
|
{
|
|
// NOT IMPLEMENTED ACTUALLY
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIRunnable
|
|
|
|
NS_IMETHODIMP CacheEntry::Run()
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
BackgroundOp(mBackgroundOperations.Grab());
|
|
return NS_OK;
|
|
}
|
|
|
|
// Management methods
|
|
|
|
double CacheEntry::GetFrecency() const
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
return mFrecency;
|
|
}
|
|
|
|
uint32_t CacheEntry::GetExpirationTime() const
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
return mSortingExpirationTime;
|
|
}
|
|
|
|
bool CacheEntry::IsRegistered() const
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
return mRegistration == REGISTERED;
|
|
}
|
|
|
|
bool CacheEntry::CanRegister() const
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
return mRegistration == NEVERREGISTERED;
|
|
}
|
|
|
|
void CacheEntry::SetRegistered(bool aRegistered)
|
|
{
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
|
|
if (aRegistered) {
|
|
MOZ_ASSERT(mRegistration == NEVERREGISTERED);
|
|
mRegistration = REGISTERED;
|
|
}
|
|
else {
|
|
MOZ_ASSERT(mRegistration == REGISTERED);
|
|
mRegistration = DEREGISTERED;
|
|
}
|
|
}
|
|
|
|
bool CacheEntry::DeferOrBypassRemovalOnPinStatus(bool aPinned)
|
|
{
|
|
LOG(("CacheEntry::DeferOrBypassRemovalOnPinStatus [this=%p]", this));
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
if (mPinningKnown) {
|
|
LOG((" pinned=%d, caller=%d", mPinned, aPinned));
|
|
// Bypass when the pin status of this entry doesn't match the pin status
|
|
// caller wants to remove
|
|
return mPinned != aPinned;
|
|
}
|
|
|
|
LOG((" pinning unknown, caller=%d", aPinned));
|
|
// Oterwise, remember to doom after the status is determined for any
|
|
// callback opening the entry after this point...
|
|
Callback c(this, aPinned);
|
|
RememberCallback(c);
|
|
// ...and always bypass
|
|
return true;
|
|
}
|
|
|
|
bool CacheEntry::Purge(uint32_t aWhat)
|
|
{
|
|
LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat));
|
|
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
|
|
switch (aWhat) {
|
|
case PURGE_DATA_ONLY_DISK_BACKED:
|
|
case PURGE_WHOLE_ONLY_DISK_BACKED:
|
|
// This is an in-memory only entry, don't purge it
|
|
if (!mUseDisk) {
|
|
LOG((" not using disk"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mState == WRITING || mState == LOADING || mFrecency == 0) {
|
|
// In-progress (write or load) entries should (at least for consistency and from
|
|
// the logical point of view) stay in memory.
|
|
// Zero-frecency entries are those which have never been given to any consumer, those
|
|
// are actually very fresh and should not go just because frecency had not been set
|
|
// so far.
|
|
LOG((" state=%s, frecency=%1.10f", StateString(mState), mFrecency));
|
|
return false;
|
|
}
|
|
|
|
if (NS_SUCCEEDED(mFileStatus) && mFile->IsWriteInProgress()) {
|
|
// The file is used when there are open streams or chunks/metadata still waiting for
|
|
// write. In this case, this entry cannot be purged, otherwise reopenned entry
|
|
// would may not even find the data on disk - CacheFile is not shared and cannot be
|
|
// left orphan when its job is not done, hence keep the whole entry.
|
|
LOG((" file still under use"));
|
|
return false;
|
|
}
|
|
|
|
switch (aWhat) {
|
|
case PURGE_WHOLE_ONLY_DISK_BACKED:
|
|
case PURGE_WHOLE:
|
|
{
|
|
if (!CacheStorageService::Self()->RemoveEntry(this, true)) {
|
|
LOG((" not purging, still referenced"));
|
|
return false;
|
|
}
|
|
|
|
CacheStorageService::Self()->UnregisterEntry(this);
|
|
|
|
// Entry removed it self from control arrays, return true
|
|
return true;
|
|
}
|
|
|
|
case PURGE_DATA_ONLY_DISK_BACKED:
|
|
{
|
|
NS_ENSURE_SUCCESS(mFileStatus, false);
|
|
|
|
mFile->ThrowMemoryCachedData();
|
|
|
|
// Entry has been left in control arrays, return false (not purged)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LOG((" ?"));
|
|
return false;
|
|
}
|
|
|
|
void CacheEntry::PurgeAndDoom()
|
|
{
|
|
LOG(("CacheEntry::PurgeAndDoom [this=%p]", this));
|
|
|
|
CacheStorageService::Self()->RemoveEntry(this);
|
|
DoomAlreadyRemoved();
|
|
}
|
|
|
|
void CacheEntry::DoomAlreadyRemoved()
|
|
{
|
|
LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
|
|
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
|
|
RemoveForcedValidity();
|
|
|
|
mIsDoomed = true;
|
|
|
|
// Pretend pinning is know. This entry is now doomed for good, so don't
|
|
// bother with defering doom because of unknown pinning state any more.
|
|
mPinningKnown = true;
|
|
|
|
// This schedules dooming of the file, dooming is ensured to happen
|
|
// sooner than demand to open the same file made after this point
|
|
// so that we don't get this file for any newer opened entry(s).
|
|
DoomFile();
|
|
|
|
// Must force post here since may be indirectly called from
|
|
// InvokeCallbacks of this entry and we don't want reentrancy here.
|
|
BackgroundOp(Ops::CALLBACKS, true);
|
|
// Process immediately when on the management thread.
|
|
BackgroundOp(Ops::UNREGISTER);
|
|
}
|
|
|
|
void CacheEntry::DoomFile()
|
|
{
|
|
nsresult rv = NS_ERROR_NOT_AVAILABLE;
|
|
|
|
if (NS_SUCCEEDED(mFileStatus)) {
|
|
// Always calls the callback asynchronously.
|
|
rv = mFile->Doom(mDoomCallback ? this : nullptr);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
LOG((" file doomed"));
|
|
return;
|
|
}
|
|
|
|
if (NS_ERROR_FILE_NOT_FOUND == rv) {
|
|
// File is set to be just memory-only, notify the callbacks
|
|
// and pretend dooming has succeeded. From point of view of
|
|
// the entry it actually did - the data is gone and cannot be
|
|
// reused.
|
|
rv = NS_OK;
|
|
}
|
|
}
|
|
|
|
// Always posts to the main thread.
|
|
OnFileDoomed(rv);
|
|
}
|
|
|
|
void CacheEntry::RemoveForcedValidity()
|
|
{
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
nsresult rv;
|
|
|
|
if (mIsDoomed) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString entryKey;
|
|
rv = HashingKey(entryKey);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
CacheStorageService::Self()->RemoveEntryForceValid(mStorageID, entryKey);
|
|
}
|
|
|
|
void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync)
|
|
{
|
|
mLock.AssertCurrentThreadOwns();
|
|
|
|
if (!CacheStorageService::IsOnManagementThread() || aForceAsync) {
|
|
if (mBackgroundOperations.Set(aOperations))
|
|
CacheStorageService::Self()->Dispatch(this);
|
|
|
|
LOG(("CacheEntry::BackgroundOp this=%p dipatch of %x", this, aOperations));
|
|
return;
|
|
}
|
|
|
|
{
|
|
mozilla::MutexAutoUnlock unlock(mLock);
|
|
|
|
MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
|
|
|
|
if (aOperations & Ops::FRECENCYUPDATE) {
|
|
++mUseCount;
|
|
|
|
#ifndef M_LN2
|
|
#define M_LN2 0.69314718055994530942
|
|
#endif
|
|
|
|
// Half-life is dynamic, in seconds.
|
|
static double half_life = CacheObserver::HalfLifeSeconds();
|
|
// Must convert from seconds to milliseconds since PR_Now() gives usecs.
|
|
static double const decay = (M_LN2 / half_life) / static_cast<double>(PR_USEC_PER_SEC);
|
|
|
|
double now_decay = static_cast<double>(PR_Now()) * decay;
|
|
|
|
if (mFrecency == 0) {
|
|
mFrecency = now_decay;
|
|
}
|
|
else {
|
|
// TODO: when C++11 enabled, use std::log1p(n) which is equal to log(n + 1) but
|
|
// more precise.
|
|
mFrecency = log(exp(mFrecency - now_decay) + 1) + now_decay;
|
|
}
|
|
LOG(("CacheEntry FRECENCYUPDATE [this=%p, frecency=%1.10f]", this, mFrecency));
|
|
|
|
// Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that
|
|
// is not thread-safe) we must post to the main thread...
|
|
RefPtr<nsRunnableMethod<CacheEntry> > event =
|
|
NS_NewRunnableMethodWithArg<double>(this, &CacheEntry::StoreFrecency, mFrecency);
|
|
NS_DispatchToMainThread(event);
|
|
}
|
|
|
|
if (aOperations & Ops::REGISTER) {
|
|
LOG(("CacheEntry REGISTER [this=%p]", this));
|
|
|
|
CacheStorageService::Self()->RegisterEntry(this);
|
|
}
|
|
|
|
if (aOperations & Ops::UNREGISTER) {
|
|
LOG(("CacheEntry UNREGISTER [this=%p]", this));
|
|
|
|
CacheStorageService::Self()->UnregisterEntry(this);
|
|
}
|
|
} // unlock
|
|
|
|
if (aOperations & Ops::CALLBACKS) {
|
|
LOG(("CacheEntry CALLBACKS (invoke) [this=%p]", this));
|
|
|
|
InvokeCallbacks();
|
|
}
|
|
}
|
|
|
|
void CacheEntry::StoreFrecency(double aFrecency)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (NS_SUCCEEDED(mFileStatus)) {
|
|
mFile->SetFrecency(FRECENCY2INT(aFrecency));
|
|
}
|
|
}
|
|
|
|
// CacheOutputCloseListener
|
|
|
|
CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry* aEntry)
|
|
: mEntry(aEntry)
|
|
{
|
|
MOZ_COUNT_CTOR(CacheOutputCloseListener);
|
|
}
|
|
|
|
CacheOutputCloseListener::~CacheOutputCloseListener()
|
|
{
|
|
MOZ_COUNT_DTOR(CacheOutputCloseListener);
|
|
}
|
|
|
|
void CacheOutputCloseListener::OnOutputClosed()
|
|
{
|
|
// We need this class and to redispatch since this callback is invoked
|
|
// under the file's lock and to do the job we need to enter the entry's
|
|
// lock too. That would lead to potential deadlocks.
|
|
NS_DispatchToCurrentThread(this);
|
|
}
|
|
|
|
NS_IMETHODIMP CacheOutputCloseListener::Run()
|
|
{
|
|
mEntry->OnOutputClosed();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Memory reporting
|
|
|
|
size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
{
|
|
size_t n = 0;
|
|
nsCOMPtr<nsISizeOf> sizeOf;
|
|
|
|
n += mCallbacks.ShallowSizeOfExcludingThis(mallocSizeOf);
|
|
if (mFile) {
|
|
n += mFile->SizeOfIncludingThis(mallocSizeOf);
|
|
}
|
|
|
|
sizeOf = do_QueryInterface(mURI);
|
|
if (sizeOf) {
|
|
n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
|
|
}
|
|
|
|
n += mEnhanceID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
|
|
n += mStorageID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
|
|
|
|
// mDoomCallback is an arbitrary class that is probably reported elsewhere.
|
|
// mOutputStream is reported in mFile.
|
|
// mWriter is one of many handles we create, but (intentionally) not keep
|
|
// any reference to, so those unfortunatelly cannot be reported. Handles are
|
|
// small, though.
|
|
// mSecurityInfo doesn't impl nsISizeOf.
|
|
|
|
return n;
|
|
}
|
|
|
|
size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
{
|
|
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|