12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "mozilla/dom/cache/Manager.h"
- #include "mozilla/AutoRestore.h"
- #include "mozilla/Mutex.h"
- #include "mozilla/StaticMutex.h"
- #include "mozilla/StaticPtr.h"
- #include "mozilla/Unused.h"
- #include "mozilla/dom/cache/Context.h"
- #include "mozilla/dom/cache/DBAction.h"
- #include "mozilla/dom/cache/DBSchema.h"
- #include "mozilla/dom/cache/FileUtils.h"
- #include "mozilla/dom/cache/ManagerId.h"
- #include "mozilla/dom/cache/CacheTypes.h"
- #include "mozilla/dom/cache/SavedTypes.h"
- #include "mozilla/dom/cache/StreamList.h"
- #include "mozilla/dom/cache/Types.h"
- #include "mozilla/ipc/BackgroundParent.h"
- #include "mozStorageHelper.h"
- #include "nsIInputStream.h"
- #include "nsID.h"
- #include "nsIFile.h"
- #include "nsIThread.h"
- #include "nsThreadUtils.h"
- #include "nsTObserverArray.h"
- namespace mozilla {
- namespace dom {
- namespace cache {
- namespace {
- // An Action that is executed when a Context is first created. It ensures that
- // the directory and database are setup properly. This lets other actions
- // not worry about these details.
- class SetupAction final : public SyncDBAction
- {
- public:
- SetupAction()
- : SyncDBAction(DBAction::Create)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- nsresult rv = BodyCreateDir(aDBDir);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- // executes in its own transaction
- rv = db::CreateOrMigrateSchema(aConn);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- // If the Context marker file exists, then the last session was
- // not cleanly shutdown. In these cases sqlite will ensure that
- // the database is valid, but we might still orphan data. Both
- // Cache objects and body files can be referenced by DOM objects
- // after they are "removed" from their parent. So we need to
- // look and see if any of these late access objects have been
- // orphaned.
- //
- // Note, this must be done after any schema version updates to
- // ensure our DBSchema methods work correctly.
- if (MarkerFileExists(aQuotaInfo)) {
- NS_WARNING("Cache not shutdown cleanly! Cleaning up stale data...");
- mozStorageTransaction trans(aConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- // Clean up orphaned Cache objects
- AutoTArray<CacheId, 8> orphanedCacheIdList;
- nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
- AutoTArray<nsID, 16> deletedBodyIdList;
- rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = BodyDeleteFiles(aDBDir, deletedBodyIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- }
- // Clean up orphaned body objects
- AutoTArray<nsID, 64> knownBodyIdList;
- rv = db::GetKnownBodyIds(aConn, knownBodyIdList);
- rv = BodyDeleteOrphanedFiles(aDBDir, knownBodyIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- }
- return rv;
- }
- };
- // ----------------------------------------------------------------------------
- // Action that is executed when we determine that content has stopped using
- // a body file that has been orphaned.
- class DeleteOrphanedBodyAction final : public Action
- {
- public:
- explicit DeleteOrphanedBodyAction(const nsTArray<nsID>& aDeletedBodyIdList)
- : mDeletedBodyIdList(aDeletedBodyIdList)
- { }
- explicit DeleteOrphanedBodyAction(const nsID& aBodyId)
- {
- mDeletedBodyIdList.AppendElement(aBodyId);
- }
- virtual void
- RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, Data*) override
- {
- MOZ_DIAGNOSTIC_ASSERT(aResolver);
- MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
- // Note that since DeleteOrphanedBodyAction isn't used while the context is
- // being initialized, we don't need to check for cancellation here.
- nsCOMPtr<nsIFile> dbDir;
- nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- aResolver->Resolve(rv);
- return;
- }
- rv = dbDir->Append(NS_LITERAL_STRING("cache"));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- aResolver->Resolve(rv);
- return;
- }
- rv = BodyDeleteFiles(dbDir, mDeletedBodyIdList);
- Unused << NS_WARN_IF(NS_FAILED(rv));
- aResolver->Resolve(rv);
- }
- private:
- nsTArray<nsID> mDeletedBodyIdList;
- };
- bool IsHeadRequest(const CacheRequest& aRequest, const CacheQueryParams& aParams)
- {
- return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head");
- }
- bool IsHeadRequest(const CacheRequestOrVoid& aRequest, const CacheQueryParams& aParams)
- {
- if (aRequest.type() == CacheRequestOrVoid::TCacheRequest) {
- return !aParams.ignoreMethod() &&
- aRequest.get_CacheRequest().method().LowerCaseEqualsLiteral("head");
- }
- return false;
- }
- } // namespace
- // ----------------------------------------------------------------------------
- // Singleton class to track Manager instances and ensure there is only
- // one for each unique ManagerId.
- class Manager::Factory
- {
- public:
- friend class StaticAutoPtr<Manager::Factory>;
- static nsresult
- GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- // Ensure there is a factory instance. This forces the Get() call
- // below to use the same factory.
- nsresult rv = MaybeCreateInstance();
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- RefPtr<Manager> ref = Get(aManagerId);
- if (!ref) {
- // TODO: replace this with a thread pool (bug 1119864)
- nsCOMPtr<nsIThread> ioThread;
- rv = NS_NewNamedThread("DOMCacheThread", getter_AddRefs(ioThread));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- ref = new Manager(aManagerId, ioThread);
- // There may be an old manager for this origin in the process of
- // cleaning up. We need to tell the new manager about this so
- // that it won't actually start until the old manager is done.
- RefPtr<Manager> oldManager = Get(aManagerId, Closing);
- ref->Init(oldManager);
- MOZ_ASSERT(!sFactory->mManagerList.Contains(ref));
- sFactory->mManagerList.AppendElement(ref);
- }
- ref.forget(aManagerOut);
- return NS_OK;
- }
- static already_AddRefed<Manager>
- Get(ManagerId* aManagerId, State aState = Open)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- nsresult rv = MaybeCreateInstance();
- if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
- // Iterate in reverse to find the most recent, matching Manager. This
- // is important when looking for a Closing Manager. If a new Manager
- // chains to an old Manager we want it to be the most recent one.
- ManagerList::BackwardIterator iter(sFactory->mManagerList);
- while (iter.HasMore()) {
- RefPtr<Manager> manager = iter.GetNext();
- if (aState == manager->GetState() && *manager->mManagerId == *aManagerId) {
- return manager.forget();
- }
- }
- return nullptr;
- }
- static void
- Remove(Manager* aManager)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- MOZ_DIAGNOSTIC_ASSERT(aManager);
- MOZ_DIAGNOSTIC_ASSERT(sFactory);
- MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
- // clean up the factory singleton if there are no more managers
- MaybeDestroyInstance();
- }
- static void
- Abort(const nsACString& aOrigin)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- if (!sFactory) {
- return;
- }
- MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
- {
- ManagerList::ForwardIterator iter(sFactory->mManagerList);
- while (iter.HasMore()) {
- RefPtr<Manager> manager = iter.GetNext();
- if (aOrigin.IsVoid() ||
- manager->mManagerId->QuotaOrigin() == aOrigin) {
- manager->Abort();
- }
- }
- }
- }
- static void
- ShutdownAll()
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- if (!sFactory) {
- return;
- }
- MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
- {
- // Note that we are synchronously calling shutdown code here. If any
- // of the shutdown code synchronously decides to delete the Factory
- // we need to delay that delete until the end of this method.
- AutoRestore<bool> restore(sFactory->mInSyncShutdown);
- sFactory->mInSyncShutdown = true;
- ManagerList::ForwardIterator iter(sFactory->mManagerList);
- while (iter.HasMore()) {
- RefPtr<Manager> manager = iter.GetNext();
- manager->Shutdown();
- }
- }
- MaybeDestroyInstance();
- }
- static bool
- IsShutdownAllComplete()
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- return !sFactory;
- }
- private:
- Factory()
- : mInSyncShutdown(false)
- {
- MOZ_COUNT_CTOR(cache::Manager::Factory);
- }
- ~Factory()
- {
- MOZ_COUNT_DTOR(cache::Manager::Factory);
- MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
- MOZ_DIAGNOSTIC_ASSERT(!mInSyncShutdown);
- }
- static nsresult
- MaybeCreateInstance()
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- if (!sFactory) {
- // Be clear about what we are locking. sFactory is bg thread only, so
- // we don't need to lock it here. Just protect sFactoryShutdown and
- // sBackgroundThread.
- {
- StaticMutexAutoLock lock(sMutex);
- if (sFactoryShutdown) {
- return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
- }
- }
- // We cannot use ClearOnShutdown() here because we're not on the main
- // thread. Instead, we delete sFactory in Factory::Remove() after the
- // last manager is removed. ShutdownObserver ensures this happens
- // before shutdown.
- sFactory = new Factory();
- }
- // Never return sFactory to code outside Factory. We need to delete it
- // out from under ourselves just before we return from Remove(). This
- // would be (even more) dangerous if other code had a pointer to the
- // factory itself.
- return NS_OK;
- }
- static void
- MaybeDestroyInstance()
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- MOZ_DIAGNOSTIC_ASSERT(sFactory);
- // If the factory is is still in use then we cannot delete yet. This
- // could be due to managers still existing or because we are in the
- // middle of shutting down. We need to be careful not to delete ourself
- // synchronously during shutdown.
- if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
- return;
- }
- sFactory = nullptr;
- }
- // Singleton created on demand and deleted when last Manager is cleared
- // in Remove().
- // PBackground thread only.
- static StaticAutoPtr<Factory> sFactory;
- // protects following static attribute
- static StaticMutex sMutex;
- // Indicate if shutdown has occurred to block re-creation of sFactory.
- // Must hold sMutex to access.
- static bool sFactoryShutdown;
- // Weak references as we don't want to keep Manager objects alive forever.
- // When a Manager is destroyed it calls Factory::Remove() to clear itself.
- // PBackground thread only.
- typedef nsTObserverArray<Manager*> ManagerList;
- ManagerList mManagerList;
- // This flag is set when we are looping through the list and calling
- // Shutdown() on each Manager. We need to be careful not to synchronously
- // trigger the deletion of the factory while still executing this loop.
- bool mInSyncShutdown;
- };
- // static
- StaticAutoPtr<Manager::Factory> Manager::Factory::sFactory;
- // static
- StaticMutex Manager::Factory::sMutex;
- // static
- bool Manager::Factory::sFactoryShutdown = false;
- // ----------------------------------------------------------------------------
- // Abstract class to help implement the various Actions. The vast majority
- // of Actions are synchronous and need to report back to a Listener on the
- // Manager.
- class Manager::BaseAction : public SyncDBAction
- {
- protected:
- BaseAction(Manager* aManager, ListenerId aListenerId)
- : SyncDBAction(DBAction::Existing)
- , mManager(aManager)
- , mListenerId(aListenerId)
- {
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) = 0;
- virtual void
- CompleteOnInitiatingThread(nsresult aRv) override
- {
- NS_ASSERT_OWNINGTHREAD(Manager::BaseAction);
- Listener* listener = mManager->GetListener(mListenerId);
- if (listener) {
- Complete(listener, ErrorResult(aRv));
- }
- // ensure we release the manager on the initiating thread
- mManager = nullptr;
- }
- RefPtr<Manager> mManager;
- const ListenerId mListenerId;
- };
- // ----------------------------------------------------------------------------
- // Action that is executed when we determine that content has stopped using
- // a Cache object that has been orphaned.
- class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
- {
- public:
- DeleteOrphanedCacheAction(Manager* aManager, CacheId aCacheId)
- : SyncDBAction(DBAction::Existing)
- , mManager(aManager)
- , mCacheId(aCacheId)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- mozStorageTransaction trans(aConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = trans.Commit();
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- return rv;
- }
- virtual void
- CompleteOnInitiatingThread(nsresult aRv) override
- {
- mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
- // ensure we release the manager on the initiating thread
- mManager = nullptr;
- }
- private:
- RefPtr<Manager> mManager;
- const CacheId mCacheId;
- nsTArray<nsID> mDeletedBodyIdList;
- };
- // ----------------------------------------------------------------------------
- class Manager::CacheMatchAction final : public Manager::BaseAction
- {
- public:
- CacheMatchAction(Manager* aManager, ListenerId aListenerId,
- CacheId aCacheId, const CacheMatchArgs& aArgs,
- StreamList* aStreamList)
- : BaseAction(aManager, aListenerId)
- , mCacheId(aCacheId)
- , mArgs(aArgs)
- , mStreamList(aStreamList)
- , mFoundResponse(false)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- nsresult rv = db::CacheMatch(aConn, mCacheId, mArgs.request(),
- mArgs.params(), &mFoundResponse, &mResponse);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (!mFoundResponse || !mResponse.mHasBodyId
- || IsHeadRequest(mArgs.request(), mArgs.params())) {
- mResponse.mHasBodyId = false;
- return rv;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
- mStreamList->Add(mResponse.mBodyId, stream);
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- if (!mFoundResponse) {
- aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()));
- } else {
- mStreamList->Activate(mCacheId);
- aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()), mResponse,
- mStreamList);
- }
- mStreamList = nullptr;
- }
- virtual bool MatchesCacheId(CacheId aCacheId) const override
- {
- return aCacheId == mCacheId;
- }
- private:
- const CacheId mCacheId;
- const CacheMatchArgs mArgs;
- RefPtr<StreamList> mStreamList;
- bool mFoundResponse;
- SavedResponse mResponse;
- };
- // ----------------------------------------------------------------------------
- class Manager::CacheMatchAllAction final : public Manager::BaseAction
- {
- public:
- CacheMatchAllAction(Manager* aManager, ListenerId aListenerId,
- CacheId aCacheId, const CacheMatchAllArgs& aArgs,
- StreamList* aStreamList)
- : BaseAction(aManager, aListenerId)
- , mCacheId(aCacheId)
- , mArgs(aArgs)
- , mStreamList(aStreamList)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- nsresult rv = db::CacheMatchAll(aConn, mCacheId, mArgs.requestOrVoid(),
- mArgs.params(), mSavedResponses);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- for (uint32_t i = 0; i < mSavedResponses.Length(); ++i) {
- if (!mSavedResponses[i].mHasBodyId
- || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
- mSavedResponses[i].mHasBodyId = false;
- continue;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
- mStreamList->Add(mSavedResponses[i].mBodyId, stream);
- }
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- mStreamList->Activate(mCacheId);
- aListener->OnOpComplete(Move(aRv), CacheMatchAllResult(), mSavedResponses,
- mStreamList);
- mStreamList = nullptr;
- }
- virtual bool MatchesCacheId(CacheId aCacheId) const override
- {
- return aCacheId == mCacheId;
- }
- private:
- const CacheId mCacheId;
- const CacheMatchAllArgs mArgs;
- RefPtr<StreamList> mStreamList;
- nsTArray<SavedResponse> mSavedResponses;
- };
- // ----------------------------------------------------------------------------
- // This is the most complex Action. It puts a request/response pair into the
- // Cache. It does not complete until all of the body data has been saved to
- // disk. This means its an asynchronous Action.
- class Manager::CachePutAllAction final : public DBAction
- {
- public:
- CachePutAllAction(Manager* aManager, ListenerId aListenerId,
- CacheId aCacheId,
- const nsTArray<CacheRequestResponse>& aPutList,
- const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
- const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
- : DBAction(DBAction::Existing)
- , mManager(aManager)
- , mListenerId(aListenerId)
- , mCacheId(aCacheId)
- , mList(aPutList.Length())
- , mExpectedAsyncCopyCompletions(1)
- , mAsyncResult(NS_OK)
- , mMutex("cache::Manager::CachePutAllAction")
- {
- MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
- MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
- MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
- for (uint32_t i = 0; i < aPutList.Length(); ++i) {
- Entry* entry = mList.AppendElement();
- entry->mRequest = aPutList[i].request();
- entry->mRequestStream = aRequestStreamList[i];
- entry->mResponse = aPutList[i].response();
- entry->mResponseStream = aResponseStreamList[i];
- }
- }
- private:
- ~CachePutAllAction() { }
- virtual void
- RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
- nsIFile* aDBDir, mozIStorageConnection* aConn) override
- {
- MOZ_DIAGNOSTIC_ASSERT(aResolver);
- MOZ_DIAGNOSTIC_ASSERT(aDBDir);
- MOZ_DIAGNOSTIC_ASSERT(aConn);
- MOZ_DIAGNOSTIC_ASSERT(!mResolver);
- MOZ_DIAGNOSTIC_ASSERT(!mDBDir);
- MOZ_DIAGNOSTIC_ASSERT(!mConn);
- MOZ_DIAGNOSTIC_ASSERT(!mTargetThread);
- mTargetThread = NS_GetCurrentThread();
- MOZ_DIAGNOSTIC_ASSERT(mTargetThread);
- // We should be pre-initialized to expect one async completion. This is
- // the "manual" completion we call at the end of this method in all
- // cases.
- MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions == 1);
- mResolver = aResolver;
- mDBDir = aDBDir;
- mConn = aConn;
- // File bodies are streamed to disk via asynchronous copying. Start
- // this copying now. Each copy will eventually result in a call
- // to OnAsyncCopyComplete().
- nsresult rv = NS_OK;
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- rv = StartStreamCopy(aQuotaInfo, mList[i], RequestStream,
- &mExpectedAsyncCopyCompletions);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- break;
- }
- rv = StartStreamCopy(aQuotaInfo, mList[i], ResponseStream,
- &mExpectedAsyncCopyCompletions);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- break;
- }
- }
- // Always call OnAsyncCopyComplete() manually here. This covers the
- // case where there is no async copying and also reports any startup
- // errors correctly. If we hit an error, then OnAsyncCopyComplete()
- // will cancel any async copying.
- OnAsyncCopyComplete(rv);
- }
- // Called once for each asynchronous file copy whether it succeeds or
- // fails. If a file copy is canceled, it still calls this method with
- // an error code.
- void
- OnAsyncCopyComplete(nsresult aRv)
- {
- MOZ_ASSERT(mTargetThread == NS_GetCurrentThread());
- MOZ_DIAGNOSTIC_ASSERT(mConn);
- MOZ_DIAGNOSTIC_ASSERT(mResolver);
- MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions > 0);
- // Explicitly check for cancellation here to catch a race condition.
- // Consider:
- //
- // 1) NS_AsyncCopy() executes on IO thread, but has not saved its
- // copy context yet.
- // 2) CancelAllStreamCopying() occurs on PBackground thread
- // 3) Copy context from (1) is saved on IO thread.
- //
- // Checking for cancellation here catches this condition when we
- // first call OnAsyncCopyComplete() manually from RunWithDBOnTarget().
- //
- // This explicit cancellation check also handles the case where we
- // are canceled just after all stream copying completes. We should
- // abort the synchronous DB operations in this case if we have not
- // started them yet.
- if (NS_SUCCEEDED(aRv) && IsCanceled()) {
- aRv = NS_ERROR_ABORT;
- }
- // If any of the async copies fail, we need to still wait for them all to
- // complete. Cancel any other streams still working and remember the
- // error. All canceled streams will call OnAsyncCopyComplete().
- if (NS_FAILED(aRv) && NS_SUCCEEDED(mAsyncResult)) {
- CancelAllStreamCopying();
- mAsyncResult = aRv;
- }
- // Check to see if async copying is still on-going. If so, then simply
- // return for now. We must wait for a later OnAsyncCopyComplete() call.
- mExpectedAsyncCopyCompletions -= 1;
- if (mExpectedAsyncCopyCompletions > 0) {
- return;
- }
- // We have finished with all async copying. Indicate this by clearing all
- // our copy contexts.
- {
- MutexAutoLock lock(mMutex);
- mCopyContextList.Clear();
- }
- // An error occurred while async copying. Terminate the Action.
- // DoResolve() will clean up any files we may have written.
- if (NS_FAILED(mAsyncResult)) {
- DoResolve(mAsyncResult);
- return;
- }
- mozStorageTransaction trans(mConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- nsresult rv = NS_OK;
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- Entry& e = mList[i];
- if (e.mRequestStream) {
- rv = BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- DoResolve(rv);
- return;
- }
- }
- if (e.mResponseStream) {
- rv = BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- DoResolve(rv);
- return;
- }
- }
- rv = db::CachePut(mConn, mCacheId, e.mRequest,
- e.mRequestStream ? &e.mRequestBodyId : nullptr,
- e.mResponse,
- e.mResponseStream ? &e.mResponseBodyId : nullptr,
- mDeletedBodyIdList);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- DoResolve(rv);
- return;
- }
- }
- rv = trans.Commit();
- Unused << NS_WARN_IF(NS_FAILED(rv));
- DoResolve(rv);
- }
- virtual void
- CompleteOnInitiatingThread(nsresult aRv) override
- {
- NS_ASSERT_OWNINGTHREAD(Action);
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- mList[i].mRequestStream = nullptr;
- mList[i].mResponseStream = nullptr;
- }
- mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
- Listener* listener = mManager->GetListener(mListenerId);
- mManager = nullptr;
- if (listener) {
- listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
- }
- }
- virtual void
- CancelOnInitiatingThread() override
- {
- NS_ASSERT_OWNINGTHREAD(Action);
- Action::CancelOnInitiatingThread();
- CancelAllStreamCopying();
- }
- virtual bool MatchesCacheId(CacheId aCacheId) const override
- {
- NS_ASSERT_OWNINGTHREAD(Action);
- return aCacheId == mCacheId;
- }
- struct Entry
- {
- CacheRequest mRequest;
- nsCOMPtr<nsIInputStream> mRequestStream;
- nsID mRequestBodyId;
- nsCOMPtr<nsISupports> mRequestCopyContext;
- CacheResponse mResponse;
- nsCOMPtr<nsIInputStream> mResponseStream;
- nsID mResponseBodyId;
- nsCOMPtr<nsISupports> mResponseCopyContext;
- };
- enum StreamId
- {
- RequestStream,
- ResponseStream
- };
- nsresult
- StartStreamCopy(const QuotaInfo& aQuotaInfo, Entry& aEntry,
- StreamId aStreamId, uint32_t* aCopyCountOut)
- {
- MOZ_ASSERT(mTargetThread == NS_GetCurrentThread());
- MOZ_DIAGNOSTIC_ASSERT(aCopyCountOut);
- if (IsCanceled()) {
- return NS_ERROR_ABORT;
- }
- nsCOMPtr<nsIInputStream> source;
- nsID* bodyId;
- if (aStreamId == RequestStream) {
- source = aEntry.mRequestStream;
- bodyId = &aEntry.mRequestBodyId;
- } else {
- MOZ_DIAGNOSTIC_ASSERT(aStreamId == ResponseStream);
- source = aEntry.mResponseStream;
- bodyId = &aEntry.mResponseBodyId;
- }
- if (!source) {
- return NS_OK;
- }
- nsCOMPtr<nsISupports> copyContext;
- nsresult rv = BodyStartWriteStream(aQuotaInfo, mDBDir, source, this,
- AsyncCopyCompleteFunc, bodyId,
- getter_AddRefs(copyContext));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- mBodyIdWrittenList.AppendElement(*bodyId);
- if (copyContext) {
- MutexAutoLock lock(mMutex);
- mCopyContextList.AppendElement(copyContext);
- }
- *aCopyCountOut += 1;
- return rv;
- }
- void
- CancelAllStreamCopying()
- {
- // May occur on either owning thread or target thread
- MutexAutoLock lock(mMutex);
- for (uint32_t i = 0; i < mCopyContextList.Length(); ++i) {
- BodyCancelWrite(mDBDir, mCopyContextList[i]);
- }
- mCopyContextList.Clear();
- }
- static void
- AsyncCopyCompleteFunc(void* aClosure, nsresult aRv)
- {
- // May be on any thread, including STS event target.
- MOZ_DIAGNOSTIC_ASSERT(aClosure);
- // Weak ref as we are guaranteed to the action is alive until
- // CompleteOnInitiatingThread is called.
- CachePutAllAction* action = static_cast<CachePutAllAction*>(aClosure);
- action->CallOnAsyncCopyCompleteOnTargetThread(aRv);
- }
- void
- CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
- {
- // May be on any thread, including STS event target. Non-owning runnable
- // here since we are guaranteed the Action will survive until
- // CompleteOnInitiatingThread is called.
- nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod<nsresult>(
- this, &CachePutAllAction::OnAsyncCopyComplete, aRv);
- MOZ_ALWAYS_SUCCEEDS(
- mTargetThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
- }
- void
- DoResolve(nsresult aRv)
- {
- MOZ_ASSERT(mTargetThread == NS_GetCurrentThread());
- // DoResolve() must not be called until all async copying has completed.
- #ifdef DEBUG
- {
- MutexAutoLock lock(mMutex);
- MOZ_ASSERT(mCopyContextList.IsEmpty());
- }
- #endif
- // Clean up any files we might have written before hitting the error.
- if (NS_FAILED(aRv)) {
- BodyDeleteFiles(mDBDir, mBodyIdWrittenList);
- }
- // Must be released on the target thread where it was opened.
- mConn = nullptr;
- // Drop our ref to the target thread as we are done with this thread.
- // Also makes our thread assertions catch any incorrect method calls
- // after resolve.
- mTargetThread = nullptr;
- // Make sure to de-ref the resolver per the Action API contract.
- RefPtr<Action::Resolver> resolver;
- mResolver.swap(resolver);
- resolver->Resolve(aRv);
- }
- // initiating thread only
- RefPtr<Manager> mManager;
- const ListenerId mListenerId;
- // Set on initiating thread, read on target thread. State machine guarantees
- // these are not modified while being read by the target thread.
- const CacheId mCacheId;
- nsTArray<Entry> mList;
- uint32_t mExpectedAsyncCopyCompletions;
- // target thread only
- RefPtr<Resolver> mResolver;
- nsCOMPtr<nsIFile> mDBDir;
- nsCOMPtr<mozIStorageConnection> mConn;
- nsCOMPtr<nsIThread> mTargetThread;
- nsresult mAsyncResult;
- nsTArray<nsID> mBodyIdWrittenList;
- // Written to on target thread, accessed on initiating thread after target
- // thread activity is guaranteed complete
- nsTArray<nsID> mDeletedBodyIdList;
- // accessed from any thread while mMutex locked
- Mutex mMutex;
- nsTArray<nsCOMPtr<nsISupports>> mCopyContextList;
- };
- // ----------------------------------------------------------------------------
- class Manager::CacheDeleteAction final : public Manager::BaseAction
- {
- public:
- CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
- CacheId aCacheId, const CacheDeleteArgs& aArgs)
- : BaseAction(aManager, aListenerId)
- , mCacheId(aCacheId)
- , mArgs(aArgs)
- , mSuccess(false)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- mozStorageTransaction trans(aConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- nsresult rv = db::CacheDelete(aConn, mCacheId, mArgs.request(),
- mArgs.params(), mDeletedBodyIdList,
- &mSuccess);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = trans.Commit();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- mSuccess = false;
- return rv;
- }
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
- aListener->OnOpComplete(Move(aRv), CacheDeleteResult(mSuccess));
- }
- virtual bool MatchesCacheId(CacheId aCacheId) const override
- {
- return aCacheId == mCacheId;
- }
- private:
- const CacheId mCacheId;
- const CacheDeleteArgs mArgs;
- bool mSuccess;
- nsTArray<nsID> mDeletedBodyIdList;
- };
- // ----------------------------------------------------------------------------
- class Manager::CacheKeysAction final : public Manager::BaseAction
- {
- public:
- CacheKeysAction(Manager* aManager, ListenerId aListenerId,
- CacheId aCacheId, const CacheKeysArgs& aArgs,
- StreamList* aStreamList)
- : BaseAction(aManager, aListenerId)
- , mCacheId(aCacheId)
- , mArgs(aArgs)
- , mStreamList(aStreamList)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- nsresult rv = db::CacheKeys(aConn, mCacheId, mArgs.requestOrVoid(),
- mArgs.params(), mSavedRequests);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- for (uint32_t i = 0; i < mSavedRequests.Length(); ++i) {
- if (!mSavedRequests[i].mHasBodyId
- || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
- mSavedRequests[i].mHasBodyId = false;
- continue;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
- mStreamList->Add(mSavedRequests[i].mBodyId, stream);
- }
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- mStreamList->Activate(mCacheId);
- aListener->OnOpComplete(Move(aRv), CacheKeysResult(), mSavedRequests,
- mStreamList);
- mStreamList = nullptr;
- }
- virtual bool MatchesCacheId(CacheId aCacheId) const override
- {
- return aCacheId == mCacheId;
- }
- private:
- const CacheId mCacheId;
- const CacheKeysArgs mArgs;
- RefPtr<StreamList> mStreamList;
- nsTArray<SavedRequest> mSavedRequests;
- };
- // ----------------------------------------------------------------------------
- class Manager::StorageMatchAction final : public Manager::BaseAction
- {
- public:
- StorageMatchAction(Manager* aManager, ListenerId aListenerId,
- Namespace aNamespace,
- const StorageMatchArgs& aArgs,
- StreamList* aStreamList)
- : BaseAction(aManager, aListenerId)
- , mNamespace(aNamespace)
- , mArgs(aArgs)
- , mStreamList(aStreamList)
- , mFoundResponse(false)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- nsresult rv = db::StorageMatch(aConn, mNamespace, mArgs.request(),
- mArgs.params(), &mFoundResponse,
- &mSavedResponse);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (!mFoundResponse || !mSavedResponse.mHasBodyId
- || IsHeadRequest(mArgs.request(), mArgs.params())) {
- mSavedResponse.mHasBodyId = false;
- return rv;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
- mStreamList->Add(mSavedResponse.mBodyId, stream);
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- if (!mFoundResponse) {
- aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()));
- } else {
- mStreamList->Activate(mSavedResponse.mCacheId);
- aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()), mSavedResponse,
- mStreamList);
- }
- mStreamList = nullptr;
- }
- private:
- const Namespace mNamespace;
- const StorageMatchArgs mArgs;
- RefPtr<StreamList> mStreamList;
- bool mFoundResponse;
- SavedResponse mSavedResponse;
- };
- // ----------------------------------------------------------------------------
- class Manager::StorageHasAction final : public Manager::BaseAction
- {
- public:
- StorageHasAction(Manager* aManager, ListenerId aListenerId,
- Namespace aNamespace, const StorageHasArgs& aArgs)
- : BaseAction(aManager, aListenerId)
- , mNamespace(aNamespace)
- , mArgs(aArgs)
- , mCacheFound(false)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- CacheId cacheId;
- return db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
- &mCacheFound, &cacheId);
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- aListener->OnOpComplete(Move(aRv), StorageHasResult(mCacheFound));
- }
- private:
- const Namespace mNamespace;
- const StorageHasArgs mArgs;
- bool mCacheFound;
- };
- // ----------------------------------------------------------------------------
- class Manager::StorageOpenAction final : public Manager::BaseAction
- {
- public:
- StorageOpenAction(Manager* aManager, ListenerId aListenerId,
- Namespace aNamespace, const StorageOpenArgs& aArgs)
- : BaseAction(aManager, aListenerId)
- , mNamespace(aNamespace)
- , mArgs(aArgs)
- , mCacheId(INVALID_CACHE_ID)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- // Cache does not exist, create it instead
- mozStorageTransaction trans(aConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- // Look for existing cache
- bool cacheFound;
- nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
- &cacheFound, &mCacheId);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (cacheFound) {
- MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
- return rv;
- }
- rv = db::CreateCacheId(aConn, &mCacheId);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = db::StoragePutCache(aConn, mNamespace, mArgs.key(), mCacheId);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = trans.Commit();
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- MOZ_DIAGNOSTIC_ASSERT(aRv.Failed() || mCacheId != INVALID_CACHE_ID);
- aListener->OnOpComplete(Move(aRv), StorageOpenResult(), mCacheId);
- }
- private:
- const Namespace mNamespace;
- const StorageOpenArgs mArgs;
- CacheId mCacheId;
- };
- // ----------------------------------------------------------------------------
- class Manager::StorageDeleteAction final : public Manager::BaseAction
- {
- public:
- StorageDeleteAction(Manager* aManager, ListenerId aListenerId,
- Namespace aNamespace, const StorageDeleteArgs& aArgs)
- : BaseAction(aManager, aListenerId)
- , mNamespace(aNamespace)
- , mArgs(aArgs)
- , mCacheDeleted(false)
- , mCacheId(INVALID_CACHE_ID)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- mozStorageTransaction trans(aConn, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- bool exists;
- nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
- &exists, &mCacheId);
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- if (!exists) {
- mCacheDeleted = false;
- return NS_OK;
- }
- rv = db::StorageForgetCache(aConn, mNamespace, mArgs.key());
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- rv = trans.Commit();
- if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
- mCacheDeleted = true;
- return rv;
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- if (mCacheDeleted) {
- // If content is referencing this cache, mark it orphaned to be
- // deleted later.
- if (!mManager->SetCacheIdOrphanedIfRefed(mCacheId)) {
- // no outstanding references, delete immediately
- RefPtr<Context> context = mManager->mContext;
- if (context->IsCanceled()) {
- context->NoteOrphanedData();
- } else {
- context->CancelForCacheId(mCacheId);
- RefPtr<Action> action =
- new DeleteOrphanedCacheAction(mManager, mCacheId);
- context->Dispatch(action);
- }
- }
- }
- aListener->OnOpComplete(Move(aRv), StorageDeleteResult(mCacheDeleted));
- }
- private:
- const Namespace mNamespace;
- const StorageDeleteArgs mArgs;
- bool mCacheDeleted;
- CacheId mCacheId;
- };
- // ----------------------------------------------------------------------------
- class Manager::StorageKeysAction final : public Manager::BaseAction
- {
- public:
- StorageKeysAction(Manager* aManager, ListenerId aListenerId,
- Namespace aNamespace)
- : BaseAction(aManager, aListenerId)
- , mNamespace(aNamespace)
- { }
- virtual nsresult
- RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
- mozIStorageConnection* aConn) override
- {
- return db::StorageGetKeys(aConn, mNamespace, mKeys);
- }
- virtual void
- Complete(Listener* aListener, ErrorResult&& aRv) override
- {
- if (aRv.Failed()) {
- mKeys.Clear();
- }
- aListener->OnOpComplete(Move(aRv), StorageKeysResult(mKeys));
- }
- private:
- const Namespace mNamespace;
- nsTArray<nsString> mKeys;
- };
- // ----------------------------------------------------------------------------
- //static
- Manager::ListenerId Manager::sNextListenerId = 0;
- void
- Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult)
- {
- OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
- nsTArray<SavedRequest>(), nullptr);
- }
- void
- Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
- CacheId aOpenedCacheId)
- {
- OnOpComplete(Move(aRv), aResult, aOpenedCacheId, nsTArray<SavedResponse>(),
- nsTArray<SavedRequest>(), nullptr);
- }
- void
- Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
- const SavedResponse& aSavedResponse,
- StreamList* aStreamList)
- {
- AutoTArray<SavedResponse, 1> responseList;
- responseList.AppendElement(aSavedResponse);
- OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, responseList,
- nsTArray<SavedRequest>(), aStreamList);
- }
- void
- Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
- const nsTArray<SavedResponse>& aSavedResponseList,
- StreamList* aStreamList)
- {
- OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, aSavedResponseList,
- nsTArray<SavedRequest>(), aStreamList);
- }
- void
- Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
- const nsTArray<SavedRequest>& aSavedRequestList,
- StreamList* aStreamList)
- {
- OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
- aSavedRequestList, aStreamList);
- }
- // static
- nsresult
- Manager::GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- return Factory::GetOrCreate(aManagerId, aManagerOut);
- }
- // static
- already_AddRefed<Manager>
- Manager::Get(ManagerId* aManagerId)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- return Factory::Get(aManagerId);
- }
- // static
- void
- Manager::ShutdownAll()
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- Factory::ShutdownAll();
- while (!Factory::IsShutdownAllComplete()) {
- if (!NS_ProcessNextEvent()) {
- NS_WARNING("Something bad happened!");
- break;
- }
- }
- }
- // static
- void
- Manager::Abort(const nsACString& aOrigin)
- {
- mozilla::ipc::AssertIsOnBackgroundThread();
- Factory::Abort(aOrigin);
- }
- void
- Manager::RemoveListener(Listener* aListener)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- // There may not be a listener here in the case where an actor is killed
- // before it can perform any actual async requests on Manager.
- mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
- MOZ_ASSERT(!mListeners.Contains(aListener,
- ListenerEntryListenerComparator()));
- MaybeAllowContextToClose();
- }
- void
- Manager::RemoveContext(Context* aContext)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(mContext);
- MOZ_DIAGNOSTIC_ASSERT(mContext == aContext);
- // Whether the Context destruction was triggered from the Manager going
- // idle or the underlying storage being invalidated, we should know we
- // are closing before the Context is destroyed.
- MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
- // Before forgetting the Context, check to see if we have any outstanding
- // cache or body objects waiting for deletion. If so, note that we've
- // orphaned data so it will be cleaned up on the next open.
- for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
- if (mCacheIdRefs[i].mOrphaned) {
- aContext->NoteOrphanedData();
- break;
- }
- }
- for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
- if (mBodyIdRefs[i].mOrphaned) {
- aContext->NoteOrphanedData();
- break;
- }
- }
- mContext = nullptr;
- // Once the context is gone, we can immediately remove ourself from the
- // Factory list. We don't need to block shutdown by staying in the list
- // any more.
- Factory::Remove(this);
- }
- void
- Manager::NoteClosing()
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- // This can be called more than once legitimately through different paths.
- mState = Closing;
- }
- Manager::State
- Manager::GetState() const
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- return mState;
- }
- void
- Manager::AddRefCacheId(CacheId aCacheId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
- if (mCacheIdRefs[i].mCacheId == aCacheId) {
- mCacheIdRefs[i].mCount += 1;
- return;
- }
- }
- CacheIdRefCounter* entry = mCacheIdRefs.AppendElement();
- entry->mCacheId = aCacheId;
- entry->mCount = 1;
- entry->mOrphaned = false;
- }
- void
- Manager::ReleaseCacheId(CacheId aCacheId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
- if (mCacheIdRefs[i].mCacheId == aCacheId) {
- #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
- uint32_t oldRef = mCacheIdRefs[i].mCount;
- #endif
- mCacheIdRefs[i].mCount -= 1;
- MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount < oldRef);
- if (mCacheIdRefs[i].mCount == 0) {
- bool orphaned = mCacheIdRefs[i].mOrphaned;
- mCacheIdRefs.RemoveElementAt(i);
- RefPtr<Context> context = mContext;
- // If the context is already gone, then orphan flag should have been
- // set in RemoveContext().
- if (orphaned && context) {
- if (context->IsCanceled()) {
- context->NoteOrphanedData();
- } else {
- context->CancelForCacheId(aCacheId);
- RefPtr<Action> action = new DeleteOrphanedCacheAction(this,
- aCacheId);
- context->Dispatch(action);
- }
- }
- }
- MaybeAllowContextToClose();
- return;
- }
- }
- MOZ_ASSERT_UNREACHABLE("Attempt to release CacheId that is not referenced!");
- }
- void
- Manager::AddRefBodyId(const nsID& aBodyId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
- if (mBodyIdRefs[i].mBodyId == aBodyId) {
- mBodyIdRefs[i].mCount += 1;
- return;
- }
- }
- BodyIdRefCounter* entry = mBodyIdRefs.AppendElement();
- entry->mBodyId = aBodyId;
- entry->mCount = 1;
- entry->mOrphaned = false;
- }
- void
- Manager::ReleaseBodyId(const nsID& aBodyId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
- if (mBodyIdRefs[i].mBodyId == aBodyId) {
- #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
- uint32_t oldRef = mBodyIdRefs[i].mCount;
- #endif
- mBodyIdRefs[i].mCount -= 1;
- MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount < oldRef);
- if (mBodyIdRefs[i].mCount < 1) {
- bool orphaned = mBodyIdRefs[i].mOrphaned;
- mBodyIdRefs.RemoveElementAt(i);
- RefPtr<Context> context = mContext;
- // If the context is already gone, then orphan flag should have been
- // set in RemoveContext().
- if (orphaned && context) {
- if (context->IsCanceled()) {
- context->NoteOrphanedData();
- } else {
- RefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
- context->Dispatch(action);
- }
- }
- }
- MaybeAllowContextToClose();
- return;
- }
- }
- MOZ_ASSERT_UNREACHABLE("Attempt to release BodyId that is not referenced!");
- }
- already_AddRefed<ManagerId>
- Manager::GetManagerId() const
- {
- RefPtr<ManagerId> ref = mManagerId;
- return ref.forget();
- }
- void
- Manager::AddStreamList(StreamList* aStreamList)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(aStreamList);
- mStreamLists.AppendElement(aStreamList);
- }
- void
- Manager::RemoveStreamList(StreamList* aStreamList)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(aStreamList);
- mStreamLists.RemoveElement(aStreamList);
- }
- void
- Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
- const CacheOpArgs& aOpArgs)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(aListener);
- MOZ_DIAGNOSTIC_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
- if (NS_WARN_IF(mState == Closing)) {
- aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
- return;
- }
- RefPtr<Context> context = mContext;
- MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
- RefPtr<StreamList> streamList = new StreamList(this, context);
- ListenerId listenerId = SaveListener(aListener);
- RefPtr<Action> action;
- switch(aOpArgs.type()) {
- case CacheOpArgs::TCacheMatchArgs:
- action = new CacheMatchAction(this, listenerId, aCacheId,
- aOpArgs.get_CacheMatchArgs(), streamList);
- break;
- case CacheOpArgs::TCacheMatchAllArgs:
- action = new CacheMatchAllAction(this, listenerId, aCacheId,
- aOpArgs.get_CacheMatchAllArgs(),
- streamList);
- break;
- case CacheOpArgs::TCacheDeleteArgs:
- action = new CacheDeleteAction(this, listenerId, aCacheId,
- aOpArgs.get_CacheDeleteArgs());
- break;
- case CacheOpArgs::TCacheKeysArgs:
- action = new CacheKeysAction(this, listenerId, aCacheId,
- aOpArgs.get_CacheKeysArgs(), streamList);
- break;
- default:
- MOZ_CRASH("Unknown Cache operation!");
- }
- context->Dispatch(action);
- }
- void
- Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
- const CacheOpArgs& aOpArgs)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(aListener);
- if (NS_WARN_IF(mState == Closing)) {
- aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
- return;
- }
- RefPtr<Context> context = mContext;
- MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
- RefPtr<StreamList> streamList = new StreamList(this, context);
- ListenerId listenerId = SaveListener(aListener);
- RefPtr<Action> action;
- switch(aOpArgs.type()) {
- case CacheOpArgs::TStorageMatchArgs:
- action = new StorageMatchAction(this, listenerId, aNamespace,
- aOpArgs.get_StorageMatchArgs(),
- streamList);
- break;
- case CacheOpArgs::TStorageHasArgs:
- action = new StorageHasAction(this, listenerId, aNamespace,
- aOpArgs.get_StorageHasArgs());
- break;
- case CacheOpArgs::TStorageOpenArgs:
- action = new StorageOpenAction(this, listenerId, aNamespace,
- aOpArgs.get_StorageOpenArgs());
- break;
- case CacheOpArgs::TStorageDeleteArgs:
- action = new StorageDeleteAction(this, listenerId, aNamespace,
- aOpArgs.get_StorageDeleteArgs());
- break;
- case CacheOpArgs::TStorageKeysArgs:
- action = new StorageKeysAction(this, listenerId, aNamespace);
- break;
- default:
- MOZ_CRASH("Unknown CacheStorage operation!");
- }
- context->Dispatch(action);
- }
- void
- Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
- const nsTArray<CacheRequestResponse>& aPutList,
- const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
- const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(aListener);
- if (NS_WARN_IF(mState == Closing)) {
- aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
- return;
- }
- RefPtr<Context> context = mContext;
- MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
- ListenerId listenerId = SaveListener(aListener);
- RefPtr<Action> action = new CachePutAllAction(this, listenerId, aCacheId,
- aPutList, aRequestStreamList,
- aResponseStreamList);
- context->Dispatch(action);
- }
- Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
- : mManagerId(aManagerId)
- , mIOThread(aIOThread)
- , mContext(nullptr)
- , mShuttingDown(false)
- , mState(Open)
- {
- MOZ_DIAGNOSTIC_ASSERT(mManagerId);
- MOZ_DIAGNOSTIC_ASSERT(mIOThread);
- }
- Manager::~Manager()
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
- MOZ_DIAGNOSTIC_ASSERT(!mContext);
- nsCOMPtr<nsIThread> ioThread;
- mIOThread.swap(ioThread);
- // Don't spin the event loop in the destructor waiting for the thread to
- // shutdown. Defer this to the main thread, instead.
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::Shutdown)));
- }
- void
- Manager::Init(Manager* aOldManager)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- RefPtr<Context> oldContext;
- if (aOldManager) {
- oldContext = aOldManager->mContext;
- }
- // Create the context immediately. Since there can at most be one Context
- // per Manager now, this lets us cleanly call Factory::Remove() once the
- // Context goes away.
- RefPtr<Action> setupAction = new SetupAction();
- RefPtr<Context> ref = Context::Create(this, mIOThread, setupAction,
- oldContext);
- mContext = ref;
- }
- void
- Manager::Shutdown()
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- // Ignore duplicate attempts to shutdown. This can occur when we start
- // a browser initiated shutdown and then run ~Manager() which also
- // calls Shutdown().
- if (mShuttingDown) {
- return;
- }
- mShuttingDown = true;
- // Note that we are closing to prevent any new requests from coming in and
- // creating a new Context. We must ensure all Contexts and IO operations are
- // complete before shutdown proceeds.
- NoteClosing();
- // If there is a context, then cancel and only note that we are done after
- // its cleaned up.
- if (mContext) {
- RefPtr<Context> context = mContext;
- context->CancelAll();
- return;
- }
- }
- void
- Manager::Abort()
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- MOZ_DIAGNOSTIC_ASSERT(mContext);
- // Note that we are closing to prevent any new requests from coming in and
- // creating a new Context. We must ensure all Contexts and IO operations are
- // complete before origin clear proceeds.
- NoteClosing();
- // Cancel and only note that we are done after the context is cleaned up.
- RefPtr<Context> context = mContext;
- context->CancelAll();
- }
- Manager::ListenerId
- Manager::SaveListener(Listener* aListener)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- // Once a Listener is added, we keep a reference to it until its
- // removed. Since the same Listener might make multiple requests,
- // ensure we only have a single reference in our list.
- ListenerList::index_type index =
- mListeners.IndexOf(aListener, 0, ListenerEntryListenerComparator());
- if (index != ListenerList::NoIndex) {
- return mListeners[index].mId;
- }
- ListenerId id = sNextListenerId;
- sNextListenerId += 1;
- mListeners.AppendElement(ListenerEntry(id, aListener));
- return id;
- }
- Manager::Listener*
- Manager::GetListener(ListenerId aListenerId) const
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- ListenerList::index_type index =
- mListeners.IndexOf(aListenerId, 0, ListenerEntryIdComparator());
- if (index != ListenerList::NoIndex) {
- return mListeners[index].mListener;
- }
- // This can legitimately happen if the actor is deleted while a request is
- // in process. For example, the child process OOMs.
- return nullptr;
- }
- bool
- Manager::SetCacheIdOrphanedIfRefed(CacheId aCacheId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
- if (mCacheIdRefs[i].mCacheId == aCacheId) {
- MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount > 0);
- MOZ_DIAGNOSTIC_ASSERT(!mCacheIdRefs[i].mOrphaned);
- mCacheIdRefs[i].mOrphaned = true;
- return true;
- }
- }
- return false;
- }
- // TODO: provide way to set body non-orphaned if its added back to a cache (bug 1110479)
- bool
- Manager::SetBodyIdOrphanedIfRefed(const nsID& aBodyId)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
- if (mBodyIdRefs[i].mBodyId == aBodyId) {
- MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount > 0);
- MOZ_DIAGNOSTIC_ASSERT(!mBodyIdRefs[i].mOrphaned);
- mBodyIdRefs[i].mOrphaned = true;
- return true;
- }
- }
- return false;
- }
- void
- Manager::NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList)
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- AutoTArray<nsID, 64> deleteNowList;
- deleteNowList.SetCapacity(aDeletedBodyIdList.Length());
- for (uint32_t i = 0; i < aDeletedBodyIdList.Length(); ++i) {
- if (!SetBodyIdOrphanedIfRefed(aDeletedBodyIdList[i])) {
- deleteNowList.AppendElement(aDeletedBodyIdList[i]);
- }
- }
- // TODO: note that we need to check these bodies for staleness on startup (bug 1110446)
- RefPtr<Context> context = mContext;
- if (!deleteNowList.IsEmpty() && context && !context->IsCanceled()) {
- RefPtr<Action> action = new DeleteOrphanedBodyAction(deleteNowList);
- context->Dispatch(action);
- }
- }
- void
- Manager::MaybeAllowContextToClose()
- {
- NS_ASSERT_OWNINGTHREAD(Manager);
- // If we have an active context, but we have no more users of the Manager,
- // then let it shut itself down. We must wait for all possible users of
- // Cache state information to complete before doing this. Once we allow
- // the Context to close we may not reliably get notified of storage
- // invalidation.
- RefPtr<Context> context = mContext;
- if (context && mListeners.IsEmpty()
- && mCacheIdRefs.IsEmpty()
- && mBodyIdRefs.IsEmpty()) {
- // Mark this Manager as invalid so that it won't get used again. We don't
- // want to start any new operations once we allow the Context to close since
- // it may race with the underlying storage getting invalidated.
- NoteClosing();
- context->AllowToClose();
- }
- }
- } // namespace cache
- } // namespace dom
- } // namespace mozilla
|