1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908 |
- /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cin: */
- /* 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 <inttypes.h>
- #include "mozilla/ArrayUtils.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/Sprintf.h"
- #include "mozilla/ThreadLocal.h"
- #include "mozilla/Unused.h"
- #include "nsCache.h"
- #include "nsDiskCache.h"
- #include "nsDiskCacheDeviceSQL.h"
- #include "nsCacheService.h"
- #include "nsApplicationCache.h"
- #include "nsNetCID.h"
- #include "nsNetUtil.h"
- #include "nsIURI.h"
- #include "nsAutoPtr.h"
- #include "nsEscape.h"
- #include "nsIPrefBranch.h"
- #include "nsIPrefService.h"
- #include "nsString.h"
- #include "nsPrintfCString.h"
- #include "nsCRT.h"
- #include "nsArrayUtils.h"
- #include "nsIArray.h"
- #include "nsIVariant.h"
- #include "nsILoadContextInfo.h"
- #include "nsThreadUtils.h"
- #include "nsISerializable.h"
- #include "nsIInputStream.h"
- #include "nsIOutputStream.h"
- #include "nsSerializationHelper.h"
- #include "mozIStorageService.h"
- #include "mozIStorageStatement.h"
- #include "mozIStorageFunction.h"
- #include "mozStorageHelper.h"
- #include "nsICacheVisitor.h"
- #include "nsISeekableStream.h"
- #include "mozilla/Telemetry.h"
- #include "sqlite3.h"
- #include "mozilla/storage.h"
- #include "nsVariant.h"
- #include "mozilla/BasePrincipal.h"
- using namespace mozilla;
- using namespace mozilla::storage;
- using mozilla::NeckoOriginAttributes;
- static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
- static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
- #define LOG(args) CACHE_LOG_DEBUG(args)
- static uint32_t gNextTemporaryClientID = 0;
- /*****************************************************************************
- * helpers
- */
- static nsresult
- EnsureDir(nsIFile *dir)
- {
- bool exists;
- nsresult rv = dir->Exists(&exists);
- if (NS_SUCCEEDED(rv) && !exists)
- rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
- return rv;
- }
- static bool
- DecomposeCacheEntryKey(const nsCString *fullKey,
- const char **cid,
- const char **key,
- nsCString &buf)
- {
- buf = *fullKey;
- int32_t colon = buf.FindChar(':');
- if (colon == kNotFound)
- {
- NS_ERROR("Invalid key");
- return false;
- }
- buf.SetCharAt('\0', colon);
- *cid = buf.get();
- *key = buf.get() + colon + 1;
- return true;
- }
- class AutoResetStatement
- {
- public:
- explicit AutoResetStatement(mozIStorageStatement *s)
- : mStatement(s) {}
- ~AutoResetStatement() { mStatement->Reset(); }
- mozIStorageStatement *operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mStatement; }
- private:
- mozIStorageStatement *mStatement;
- };
- class EvictionObserver
- {
- public:
- EvictionObserver(mozIStorageConnection *db,
- nsOfflineCacheEvictionFunction *evictionFunction)
- : mDB(db), mEvictionFunction(evictionFunction)
- {
- mEvictionFunction->Init();
- mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE TEMP TRIGGER cache_on_delete BEFORE DELETE"
- " ON moz_cache FOR EACH ROW BEGIN SELECT"
- " cache_eviction_observer("
- " OLD.ClientID, OLD.key, OLD.generation);"
- " END;"));
- }
- ~EvictionObserver()
- {
- mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("DROP TRIGGER cache_on_delete;"));
- mEvictionFunction->Reset();
- }
- void Apply() { return mEvictionFunction->Apply(); }
- private:
- mozIStorageConnection *mDB;
- RefPtr<nsOfflineCacheEvictionFunction> mEvictionFunction;
- };
- #define DCACHE_HASH_MAX INT64_MAX
- #define DCACHE_HASH_BITS 64
- /**
- * nsOfflineCache::Hash(const char * key)
- *
- * This algorithm of this method implies nsOfflineCacheRecords will be stored
- * in a certain order on disk. If the algorithm changes, existing cache
- * map files may become invalid, and therefore the kCurrentVersion needs
- * to be revised.
- */
- static uint64_t
- DCacheHash(const char * key)
- {
- // initval 0x7416f295 was chosen randomly
- return (uint64_t(nsDiskCache::Hash(key, 0)) << 32) | nsDiskCache::Hash(key, 0x7416f295);
- }
- /******************************************************************************
- * nsOfflineCacheEvictionFunction
- */
- NS_IMPL_ISUPPORTS(nsOfflineCacheEvictionFunction, mozIStorageFunction)
- // helper function for directly exposing the same data file binding
- // path algorithm used in nsOfflineCacheBinding::Create
- static nsresult
- GetCacheDataFile(nsIFile *cacheDir, const char *key,
- int generation, nsCOMPtr<nsIFile> &file)
- {
- cacheDir->Clone(getter_AddRefs(file));
- if (!file)
- return NS_ERROR_OUT_OF_MEMORY;
- uint64_t hash = DCacheHash(key);
- uint32_t dir1 = (uint32_t) (hash & 0x0F);
- uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4);
- hash >>= 8;
- file->AppendNative(nsPrintfCString("%X", dir1));
- file->AppendNative(nsPrintfCString("%X", dir2));
- char leaf[64];
- SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
- return file->AppendNative(nsDependentCString(leaf));
- }
- namespace appcachedetail {
- typedef nsCOMArray<nsIFile> FileArray;
- static MOZ_THREAD_LOCAL(FileArray*) tlsEvictionItems;
- } // appcachedetail
- NS_IMETHODIMP
- nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray *values, nsIVariant **_retval)
- {
- LOG(("nsOfflineCacheEvictionFunction::OnFunctionCall\n"));
- *_retval = nullptr;
- uint32_t numEntries;
- nsresult rv = values->GetNumEntries(&numEntries);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(numEntries == 3, "unexpected number of arguments");
- uint32_t valueLen;
- const char *clientID = values->AsSharedUTF8String(0, &valueLen);
- const char *key = values->AsSharedUTF8String(1, &valueLen);
- nsAutoCString fullKey(clientID);
- fullKey.Append(':');
- fullKey.Append(key);
- int generation = values->AsInt32(2);
- // If the key is currently locked, refuse to delete this row.
- if (mDevice->IsLocked(fullKey)) {
- NS_ADDREF(*_retval = new IntegerVariant(SQLITE_IGNORE));
- return NS_OK;
- }
- nsCOMPtr<nsIFile> file;
- rv = GetCacheDataFile(mDevice->CacheDirectory(), key,
- generation, file);
- if (NS_FAILED(rv))
- {
- LOG(("GetCacheDataFile [key=%s generation=%d] failed [rv=%x]!\n",
- key, generation, rv));
- return rv;
- }
- appcachedetail::FileArray* items = appcachedetail::tlsEvictionItems.get();
- MOZ_ASSERT(items);
- if (items) {
- items->AppendObject(file);
- }
- return NS_OK;
- }
- nsOfflineCacheEvictionFunction::nsOfflineCacheEvictionFunction(nsOfflineCacheDevice * device)
- : mDevice(device)
- {
- mTLSInited = appcachedetail::tlsEvictionItems.init();
- }
- void nsOfflineCacheEvictionFunction::Init()
- {
- if (mTLSInited) {
- appcachedetail::tlsEvictionItems.set(new appcachedetail::FileArray());
- }
- }
- void nsOfflineCacheEvictionFunction::Reset()
- {
- if (!mTLSInited) {
- return;
- }
- appcachedetail::FileArray* items = appcachedetail::tlsEvictionItems.get();
- if (!items) {
- return;
- }
- appcachedetail::tlsEvictionItems.set(nullptr);
- delete items;
- }
- void
- nsOfflineCacheEvictionFunction::Apply()
- {
- LOG(("nsOfflineCacheEvictionFunction::Apply\n"));
- if (!mTLSInited) {
- return;
- }
- appcachedetail::FileArray* pitems = appcachedetail::tlsEvictionItems.get();
- if (!pitems) {
- return;
- }
- appcachedetail::FileArray items;
- items.SwapElements(*pitems);
- for (int32_t i = 0; i < items.Count(); i++) {
- if (MOZ_LOG_TEST(gCacheLog, LogLevel::Debug)) {
- nsAutoCString path;
- items[i]->GetNativePath(path);
- LOG((" removing %s\n", path.get()));
- }
- items[i]->Remove(false);
- }
- }
- class nsOfflineCacheDiscardCache : public Runnable
- {
- public:
- nsOfflineCacheDiscardCache(nsOfflineCacheDevice *device,
- nsCString &group,
- nsCString &clientID)
- : mDevice(device)
- , mGroup(group)
- , mClientID(clientID)
- {
- }
- NS_IMETHOD Run() override
- {
- if (mDevice->IsActiveCache(mGroup, mClientID))
- {
- mDevice->DeactivateGroup(mGroup);
- }
- return mDevice->EvictEntries(mClientID.get());
- }
- private:
- RefPtr<nsOfflineCacheDevice> mDevice;
- nsCString mGroup;
- nsCString mClientID;
- };
- /******************************************************************************
- * nsOfflineCacheDeviceInfo
- */
- class nsOfflineCacheDeviceInfo final : public nsICacheDeviceInfo
- {
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSICACHEDEVICEINFO
- explicit nsOfflineCacheDeviceInfo(nsOfflineCacheDevice* device)
- : mDevice(device)
- {}
- private:
- ~nsOfflineCacheDeviceInfo() {}
- nsOfflineCacheDevice* mDevice;
- };
- NS_IMPL_ISUPPORTS(nsOfflineCacheDeviceInfo, nsICacheDeviceInfo)
- NS_IMETHODIMP
- nsOfflineCacheDeviceInfo::GetDescription(char **aDescription)
- {
- *aDescription = NS_strdup("Offline cache device");
- return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
- }
- NS_IMETHODIMP
- nsOfflineCacheDeviceInfo::GetUsageReport(char ** usageReport)
- {
- nsAutoCString buffer;
- buffer.AssignLiteral(" <tr>\n"
- " <th>Cache Directory:</th>\n"
- " <td>");
- nsIFile *cacheDir = mDevice->CacheDirectory();
- if (!cacheDir)
- return NS_OK;
- nsAutoString path;
- nsresult rv = cacheDir->GetPath(path);
- if (NS_SUCCEEDED(rv))
- AppendUTF16toUTF8(path, buffer);
- else
- buffer.AppendLiteral("directory unavailable");
-
- buffer.AppendLiteral("</td>\n"
- " </tr>\n");
- *usageReport = ToNewCString(buffer);
- if (!*usageReport)
- return NS_ERROR_OUT_OF_MEMORY;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheDeviceInfo::GetEntryCount(uint32_t *aEntryCount)
- {
- *aEntryCount = mDevice->EntryCount();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheDeviceInfo::GetTotalSize(uint32_t *aTotalSize)
- {
- *aTotalSize = mDevice->CacheSize();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheDeviceInfo::GetMaximumSize(uint32_t *aMaximumSize)
- {
- *aMaximumSize = mDevice->CacheCapacity();
- return NS_OK;
- }
- /******************************************************************************
- * nsOfflineCacheBinding
- */
- class nsOfflineCacheBinding final : public nsISupports
- {
- ~nsOfflineCacheBinding() {}
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- static nsOfflineCacheBinding *
- Create(nsIFile *cacheDir, const nsCString *key, int generation);
- enum { FLAG_NEW_ENTRY = 1 };
- nsCOMPtr<nsIFile> mDataFile;
- int mGeneration;
- int mFlags;
- bool IsNewEntry() { return mFlags & FLAG_NEW_ENTRY; }
- void MarkNewEntry() { mFlags |= FLAG_NEW_ENTRY; }
- void ClearNewEntry() { mFlags &= ~FLAG_NEW_ENTRY; }
- };
- NS_IMPL_ISUPPORTS0(nsOfflineCacheBinding)
- nsOfflineCacheBinding *
- nsOfflineCacheBinding::Create(nsIFile *cacheDir,
- const nsCString *fullKey,
- int generation)
- {
- nsCOMPtr<nsIFile> file;
- cacheDir->Clone(getter_AddRefs(file));
- if (!file)
- return nullptr;
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
- return nullptr;
- uint64_t hash = DCacheHash(key);
- uint32_t dir1 = (uint32_t) (hash & 0x0F);
- uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4);
- hash >>= 8;
- // XXX we might want to create these directories up-front
- file->AppendNative(nsPrintfCString("%X", dir1));
- Unused << file->Create(nsIFile::DIRECTORY_TYPE, 00700);
- file->AppendNative(nsPrintfCString("%X", dir2));
- Unused << file->Create(nsIFile::DIRECTORY_TYPE, 00700);
- nsresult rv;
- char leaf[64];
- if (generation == -1)
- {
- file->AppendNative(NS_LITERAL_CSTRING("placeholder"));
- for (generation = 0; ; ++generation)
- {
- SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
- rv = file->SetNativeLeafName(nsDependentCString(leaf));
- if (NS_FAILED(rv))
- return nullptr;
- rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
- if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
- return nullptr;
- if (NS_SUCCEEDED(rv))
- break;
- }
- }
- else
- {
- SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
- rv = file->AppendNative(nsDependentCString(leaf));
- if (NS_FAILED(rv))
- return nullptr;
- }
- nsOfflineCacheBinding *binding = new nsOfflineCacheBinding;
- if (!binding)
- return nullptr;
- binding->mDataFile.swap(file);
- binding->mGeneration = generation;
- binding->mFlags = 0;
- return binding;
- }
- /******************************************************************************
- * nsOfflineCacheRecord
- */
- struct nsOfflineCacheRecord
- {
- const char *clientID;
- const char *key;
- const uint8_t *metaData;
- uint32_t metaDataLen;
- int32_t generation;
- int32_t dataSize;
- int32_t fetchCount;
- int64_t lastFetched;
- int64_t lastModified;
- int64_t expirationTime;
- };
- static nsCacheEntry *
- CreateCacheEntry(nsOfflineCacheDevice *device,
- const nsCString *fullKey,
- const nsOfflineCacheRecord &rec)
- {
- nsCacheEntry *entry;
- if (device->IsLocked(*fullKey)) {
- return nullptr;
- }
-
- nsresult rv = nsCacheEntry::Create(fullKey->get(), // XXX enable sharing
- nsICache::STREAM_BASED,
- nsICache::STORE_OFFLINE,
- device, &entry);
- if (NS_FAILED(rv))
- return nullptr;
- entry->SetFetchCount((uint32_t) rec.fetchCount);
- entry->SetLastFetched(SecondsFromPRTime(rec.lastFetched));
- entry->SetLastModified(SecondsFromPRTime(rec.lastModified));
- entry->SetExpirationTime(SecondsFromPRTime(rec.expirationTime));
- entry->SetDataSize((uint32_t) rec.dataSize);
- entry->UnflattenMetaData((const char *) rec.metaData, rec.metaDataLen);
- // Restore security info, if present
- const char* info = entry->GetMetaDataElement("security-info");
- if (info) {
- nsCOMPtr<nsISupports> infoObj;
- rv = NS_DeserializeObject(nsDependentCString(info),
- getter_AddRefs(infoObj));
- if (NS_FAILED(rv)) {
- delete entry;
- return nullptr;
- }
- entry->SetSecurityInfo(infoObj);
- }
- // create a binding object for this entry
- nsOfflineCacheBinding *binding =
- nsOfflineCacheBinding::Create(device->CacheDirectory(),
- fullKey,
- rec.generation);
- if (!binding)
- {
- delete entry;
- return nullptr;
- }
- entry->SetData(binding);
- return entry;
- }
- /******************************************************************************
- * nsOfflineCacheEntryInfo
- */
- class nsOfflineCacheEntryInfo final : public nsICacheEntryInfo
- {
- ~nsOfflineCacheEntryInfo() {}
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSICACHEENTRYINFO
- nsOfflineCacheRecord *mRec;
- };
- NS_IMPL_ISUPPORTS(nsOfflineCacheEntryInfo, nsICacheEntryInfo)
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetClientID(char **result)
- {
- *result = NS_strdup(mRec->clientID);
- return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetDeviceID(char ** deviceID)
- {
- *deviceID = NS_strdup(OFFLINE_CACHE_DEVICE_ID);
- return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetKey(nsACString &clientKey)
- {
- clientKey.Assign(mRec->key);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetFetchCount(int32_t *aFetchCount)
- {
- *aFetchCount = mRec->fetchCount;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetLastFetched(uint32_t *aLastFetched)
- {
- *aLastFetched = SecondsFromPRTime(mRec->lastFetched);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetLastModified(uint32_t *aLastModified)
- {
- *aLastModified = SecondsFromPRTime(mRec->lastModified);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetExpirationTime(uint32_t *aExpirationTime)
- {
- *aExpirationTime = SecondsFromPRTime(mRec->expirationTime);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::IsStreamBased(bool *aStreamBased)
- {
- *aStreamBased = true;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsOfflineCacheEntryInfo::GetDataSize(uint32_t *aDataSize)
- {
- *aDataSize = mRec->dataSize;
- return NS_OK;
- }
- /******************************************************************************
- * nsApplicationCacheNamespace
- */
- NS_IMPL_ISUPPORTS(nsApplicationCacheNamespace, nsIApplicationCacheNamespace)
- NS_IMETHODIMP
- nsApplicationCacheNamespace::Init(uint32_t itemType,
- const nsACString &namespaceSpec,
- const nsACString &data)
- {
- mItemType = itemType;
- mNamespaceSpec = namespaceSpec;
- mData = data;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCacheNamespace::GetItemType(uint32_t *out)
- {
- *out = mItemType;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCacheNamespace::GetNamespaceSpec(nsACString &out)
- {
- out = mNamespaceSpec;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCacheNamespace::GetData(nsACString &out)
- {
- out = mData;
- return NS_OK;
- }
- /******************************************************************************
- * nsApplicationCache
- */
- NS_IMPL_ISUPPORTS(nsApplicationCache,
- nsIApplicationCache,
- nsISupportsWeakReference)
- nsApplicationCache::nsApplicationCache()
- : mDevice(nullptr)
- , mValid(true)
- {
- }
- nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device,
- const nsACString &group,
- const nsACString &clientID)
- : mDevice(device)
- , mGroup(group)
- , mClientID(clientID)
- , mValid(true)
- {
- }
- nsApplicationCache::~nsApplicationCache()
- {
- if (!mDevice)
- return;
- {
- MutexAutoLock lock(mDevice->mLock);
- mDevice->mCaches.Remove(mClientID);
- }
- // If this isn't an active cache anymore, it can be destroyed.
- if (mValid && !mDevice->IsActiveCache(mGroup, mClientID))
- Discard();
- }
- void
- nsApplicationCache::MarkInvalid()
- {
- mValid = false;
- }
- NS_IMETHODIMP
- nsApplicationCache::InitAsHandle(const nsACString &groupId,
- const nsACString &clientId)
- {
- NS_ENSURE_FALSE(mDevice, NS_ERROR_ALREADY_INITIALIZED);
- NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
- mGroup = groupId;
- mClientID = clientId;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetManifestURI(nsIURI **out)
- {
- nsCOMPtr<nsIURI> uri;
- nsresult rv = NS_NewURI(getter_AddRefs(uri), mGroup);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = uri->CloneIgnoringRef(out);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetGroupID(nsACString &out)
- {
- out = mGroup;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetClientID(nsACString &out)
- {
- out = mClientID;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetProfileDirectory(nsIFile **out)
- {
- if (mDevice->BaseDirectory())
- NS_ADDREF(*out = mDevice->BaseDirectory());
- else
- *out = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetActive(bool *out)
- {
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- *out = mDevice->IsActiveCache(mGroup, mClientID);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::Activate()
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- mDevice->ActivateCache(mGroup, mClientID);
- if (mDevice->AutoShutdown(this))
- mDevice = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::Discard()
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- mValid = false;
- nsCOMPtr<nsIRunnable> ev =
- new nsOfflineCacheDiscardCache(mDevice, mGroup, mClientID);
- nsresult rv = nsCacheService::DispatchToCacheIOThread(ev);
- return rv;
- }
- NS_IMETHODIMP
- nsApplicationCache::MarkEntry(const nsACString &key,
- uint32_t typeBits)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->MarkEntry(mClientID, key, typeBits);
- }
- NS_IMETHODIMP
- nsApplicationCache::UnmarkEntry(const nsACString &key,
- uint32_t typeBits)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->UnmarkEntry(mClientID, key, typeBits);
- }
- NS_IMETHODIMP
- nsApplicationCache::GetTypes(const nsACString &key,
- uint32_t *typeBits)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->GetTypes(mClientID, key, typeBits);
- }
- NS_IMETHODIMP
- nsApplicationCache::GatherEntries(uint32_t typeBits,
- uint32_t * count,
- char *** keys)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->GatherEntries(mClientID, typeBits, count, keys);
- }
- NS_IMETHODIMP
- nsApplicationCache::AddNamespaces(nsIArray *namespaces)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- if (!namespaces)
- return NS_OK;
- mozStorageTransaction transaction(mDevice->mDB, false);
- uint32_t length;
- nsresult rv = namespaces->GetLength(&length);
- NS_ENSURE_SUCCESS(rv, rv);
- for (uint32_t i = 0; i < length; i++) {
- nsCOMPtr<nsIApplicationCacheNamespace> ns =
- do_QueryElementAt(namespaces, i);
- if (ns) {
- rv = mDevice->AddNamespace(mClientID, ns);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- rv = transaction.Commit();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsApplicationCache::GetMatchingNamespace(const nsACString &key,
- nsIApplicationCacheNamespace **out)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->GetMatchingNamespace(mClientID, key, out);
- }
- NS_IMETHODIMP
- nsApplicationCache::GetUsage(uint32_t *usage)
- {
- NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
- NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
- return mDevice->GetUsage(mClientID, usage);
- }
- /******************************************************************************
- * nsCloseDBEvent
- *****************************************************************************/
- class nsCloseDBEvent : public Runnable {
- public:
- explicit nsCloseDBEvent(mozIStorageConnection *aDB)
- {
- mDB = aDB;
- }
- NS_IMETHOD Run() override
- {
- mDB->Close();
- return NS_OK;
- }
- protected:
- virtual ~nsCloseDBEvent() {}
- private:
- nsCOMPtr<mozIStorageConnection> mDB;
- };
- /******************************************************************************
- * nsOfflineCacheDevice
- */
- NS_IMPL_ISUPPORTS0(nsOfflineCacheDevice)
- nsOfflineCacheDevice::nsOfflineCacheDevice()
- : mDB(nullptr)
- , mCacheCapacity(0)
- , mDeltaCounter(0)
- , mAutoShutdown(false)
- , mLock("nsOfflineCacheDevice.lock")
- , mActiveCaches(4)
- , mLockedEntries(32)
- {
- }
- nsOfflineCacheDevice::~nsOfflineCacheDevice()
- {}
- /* static */
- bool
- nsOfflineCacheDevice::GetStrictFileOriginPolicy()
- {
- nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
- bool retval;
- if (prefs && NS_SUCCEEDED(prefs->GetBoolPref("security.fileuri.strict_origin_policy", &retval)))
- return retval;
- // As default value use true (be more strict)
- return true;
- }
- uint32_t
- nsOfflineCacheDevice::CacheSize()
- {
- NS_ENSURE_TRUE(Initialized(), 0);
- AutoResetStatement statement(mStatement_CacheSize);
- bool hasRows;
- nsresult rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
-
- return (uint32_t) statement->AsInt32(0);
- }
- uint32_t
- nsOfflineCacheDevice::EntryCount()
- {
- NS_ENSURE_TRUE(Initialized(), 0);
- AutoResetStatement statement(mStatement_EntryCount);
- bool hasRows;
- nsresult rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
- return (uint32_t) statement->AsInt32(0);
- }
- nsresult
- nsOfflineCacheDevice::UpdateEntry(nsCacheEntry *entry)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- // Decompose the key into "ClientID" and "Key"
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
- return NS_ERROR_UNEXPECTED;
- // Store security info, if it is serializable
- nsCOMPtr<nsISupports> infoObj = entry->SecurityInfo();
- nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
- if (infoObj && !serializable)
- return NS_ERROR_UNEXPECTED;
- if (serializable) {
- nsCString info;
- nsresult rv = NS_SerializeToString(serializable, info);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = entry->SetMetaDataElement("security-info", info.get());
- NS_ENSURE_SUCCESS(rv, rv);
- }
- nsCString metaDataBuf;
- uint32_t mdSize = entry->MetaDataSize();
- if (!metaDataBuf.SetLength(mdSize, fallible))
- return NS_ERROR_OUT_OF_MEMORY;
- char *md = metaDataBuf.BeginWriting();
- entry->FlattenMetaData(md, mdSize);
- nsOfflineCacheRecord rec;
- rec.metaData = (const uint8_t *) md;
- rec.metaDataLen = mdSize;
- rec.dataSize = entry->DataSize();
- rec.fetchCount = entry->FetchCount();
- rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
- rec.lastModified = PRTimeFromSeconds(entry->LastModified());
- rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
- AutoResetStatement statement(mStatement_UpdateEntry);
- nsresult rv;
- rv = statement->BindBlobByIndex(0, rec.metaData, rec.metaDataLen);
- nsresult tmp = statement->BindInt32ByIndex(1, rec.dataSize);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt32ByIndex(2, rec.fetchCount);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(3, rec.lastFetched);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(4, rec.lastModified);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(5, rec.expirationTime);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindUTF8StringByIndex(6, nsDependentCString(cid));
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindUTF8StringByIndex(7, nsDependentCString(key));
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(!hasRows, "UPDATE should not result in output");
- return rv;
- }
- nsresult
- nsOfflineCacheDevice::UpdateEntrySize(nsCacheEntry *entry, uint32_t newSize)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- // Decompose the key into "ClientID" and "Key"
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
- return NS_ERROR_UNEXPECTED;
- AutoResetStatement statement(mStatement_UpdateEntrySize);
- nsresult rv = statement->BindInt32ByIndex(0, newSize);
- nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(cid));
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindUTF8StringByIndex(2, nsDependentCString(key));
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(!hasRows, "UPDATE should not result in output");
- return rv;
- }
- nsresult
- nsOfflineCacheDevice::DeleteEntry(nsCacheEntry *entry, bool deleteData)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- if (deleteData)
- {
- nsresult rv = DeleteData(entry);
- if (NS_FAILED(rv))
- return rv;
- }
- // Decompose the key into "ClientID" and "Key"
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
- return NS_ERROR_UNEXPECTED;
- AutoResetStatement statement(mStatement_DeleteEntry);
- nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid));
- nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(!hasRows, "DELETE should not result in output");
- return rv;
- }
- nsresult
- nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry)
- {
- nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
- NS_ENSURE_STATE(binding);
- return binding->mDataFile->Remove(false);
- }
- /**
- * nsCacheDevice implementation
- */
- // This struct is local to nsOfflineCacheDevice::Init, but ISO C++98 doesn't
- // allow a template (mozilla::ArrayLength) to be instantiated based on a local
- // type. Boo-urns!
- struct StatementSql {
- nsCOMPtr<mozIStorageStatement> &statement;
- const char *sql;
- StatementSql (nsCOMPtr<mozIStorageStatement> &aStatement, const char *aSql):
- statement (aStatement), sql (aSql) {}
- };
- nsresult
- nsOfflineCacheDevice::Init()
- {
- MOZ_ASSERT(false, "Need to be initialized with sqlite");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- nsresult
- nsOfflineCacheDevice::InitWithSqlite(mozIStorageService * ss)
- {
- NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
- // SetCacheParentDirectory must have been called
- NS_ENSURE_TRUE(mCacheDirectory, NS_ERROR_UNEXPECTED);
- // make sure the cache directory exists
- nsresult rv = EnsureDir(mCacheDirectory);
- NS_ENSURE_SUCCESS(rv, rv);
- // build path to index file
- nsCOMPtr<nsIFile> indexFile;
- rv = mCacheDirectory->Clone(getter_AddRefs(indexFile));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = indexFile->AppendNative(NS_LITERAL_CSTRING("index.sqlite"));
- NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(ss, "nsOfflineCacheDevice::InitWithSqlite called before nsCacheService::Init() ?");
- NS_ENSURE_TRUE(ss, NS_ERROR_UNEXPECTED);
- rv = ss->OpenDatabase(indexFile, getter_AddRefs(mDB));
- NS_ENSURE_SUCCESS(rv, rv);
- mInitThread = do_GetCurrentThread();
- mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;"));
- // XXX ... other initialization steps
- // XXX in the future we may wish to verify the schema for moz_cache
- // perhaps using "PRAGMA table_info" ?
- // build the table
- //
- // "Generation" is the data file generation number.
- //
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n"
- " ClientID TEXT,\n"
- " Key TEXT,\n"
- " MetaData BLOB,\n"
- " Generation INTEGER,\n"
- " DataSize INTEGER,\n"
- " FetchCount INTEGER,\n"
- " LastFetched INTEGER,\n"
- " LastModified INTEGER,\n"
- " ExpirationTime INTEGER,\n"
- " ItemType INTEGER DEFAULT 0\n"
- ");\n"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Databases from 1.9.0 don't have the ItemType column. Add the column
- // here, but don't worry about failures (the column probably already exists)
- mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0"));
- // Create the table for storing cache groups. All actions on
- // moz_cache_groups use the GroupID, so use it as the primary key.
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n"
- " GroupID TEXT PRIMARY KEY,\n"
- " ActiveClientID TEXT\n"
- ");\n"));
- NS_ENSURE_SUCCESS(rv, rv);
- mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("ALTER TABLE moz_cache_groups "
- "ADD ActivateTimeStamp INTEGER DEFAULT 0"));
- // ClientID: clientID joining moz_cache and moz_cache_namespaces
- // tables.
- // Data: Data associated with this namespace (e.g. a fallback URI
- // for fallback entries).
- // ItemType: the type of namespace.
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS"
- " moz_cache_namespaces (\n"
- " ClientID TEXT,\n"
- " NameSpace TEXT,\n"
- " Data TEXT,\n"
- " ItemType INTEGER\n"
- ");\n"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Databases from 1.9.0 have a moz_cache_index that should be dropped
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Key/ClientID pairs should be unique in the database. All queries
- // against moz_cache use the Key (which is also the most unique), so
- // use it as the primary key for this index.
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS "
- " moz_cache_key_clientid_index"
- " ON moz_cache (Key, ClientID);"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Used for ClientID lookups and to keep ClientID/NameSpace pairs unique.
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS"
- " moz_cache_namespaces_clientid_index"
- " ON moz_cache_namespaces (ClientID, NameSpace);"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Used for namespace lookups.
- rv = mDB->ExecuteSimpleSQL(
- NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS"
- " moz_cache_namespaces_namespace_index"
- " ON moz_cache_namespaces (NameSpace);"));
- NS_ENSURE_SUCCESS(rv, rv);
- mEvictionFunction = new nsOfflineCacheEvictionFunction(this);
- if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY;
- rv = mDB->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 3, mEvictionFunction);
- NS_ENSURE_SUCCESS(rv, rv);
- // create all (most) of our statements up front
- StatementSql prepared[] = {
- StatementSql ( mStatement_CacheSize, "SELECT Sum(DataSize) from moz_cache;" ),
- StatementSql ( mStatement_ApplicationCacheSize, "SELECT Sum(DataSize) from moz_cache WHERE ClientID = ?;" ),
- StatementSql ( mStatement_EntryCount, "SELECT count(*) from moz_cache;" ),
- StatementSql ( mStatement_UpdateEntry, "UPDATE moz_cache SET MetaData = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_UpdateEntrySize, "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_DeleteEntry, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_FindEntry, "SELECT MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime, ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_BindEntry, "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?);" ),
- StatementSql ( mStatement_MarkEntry, "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_UnmarkEntry, "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ),
- StatementSql ( mStatement_GetTypes, "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"),
- StatementSql ( mStatement_CleanupUnmarked, "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ),
- StatementSql ( mStatement_GatherEntries, "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ),
- StatementSql ( mStatement_ActivateClient, "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID, ActivateTimeStamp) VALUES (?, ?, ?);" ),
- StatementSql ( mStatement_DeactivateGroup, "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ),
- StatementSql ( mStatement_FindClient, "SELECT ClientID, ItemType FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;" ),
- // Search for namespaces that match the URI. Use the <= operator
- // to ensure that we use the index on moz_cache_namespaces.
- StatementSql ( mStatement_FindClientByNamespace, "SELECT ns.ClientID, ns.ItemType FROM"
- " moz_cache_namespaces AS ns JOIN moz_cache_groups AS groups"
- " ON ns.ClientID = groups.ActiveClientID"
- " WHERE ns.NameSpace <= ?1 AND ?1 GLOB ns.NameSpace || '*'"
- " ORDER BY ns.NameSpace DESC, groups.ActivateTimeStamp DESC;"),
- StatementSql ( mStatement_FindNamespaceEntry, "SELECT NameSpace, Data, ItemType FROM moz_cache_namespaces"
- " WHERE ClientID = ?1"
- " AND NameSpace <= ?2 AND ?2 GLOB NameSpace || '*'"
- " ORDER BY NameSpace DESC;"),
- StatementSql ( mStatement_InsertNamespaceEntry, "INSERT INTO moz_cache_namespaces (ClientID, NameSpace, Data, ItemType) VALUES(?, ?, ?, ?);"),
- StatementSql ( mStatement_EnumerateApps, "SELECT GroupID, ActiveClientID FROM moz_cache_groups WHERE GroupID LIKE ?1;"),
- StatementSql ( mStatement_EnumerateGroups, "SELECT GroupID, ActiveClientID FROM moz_cache_groups;"),
- StatementSql ( mStatement_EnumerateGroupsTimeOrder, "SELECT GroupID, ActiveClientID FROM moz_cache_groups ORDER BY ActivateTimeStamp;")
- };
- for (uint32_t i = 0; NS_SUCCEEDED(rv) && i < ArrayLength(prepared); ++i)
- {
- LOG(("Creating statement: %s\n", prepared[i].sql));
- rv = mDB->CreateStatement(nsDependentCString(prepared[i].sql),
- getter_AddRefs(prepared[i].statement));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- rv = InitActiveCaches();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- namespace {
- nsresult
- GetGroupForCache(const nsCSubstring &clientID, nsCString &group)
- {
- group.Assign(clientID);
- group.Truncate(group.FindChar('|'));
- NS_UnescapeURL(group);
- return NS_OK;
- }
- } // namespace
- // static
- nsresult
- nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL,
- nsACString const &aOriginSuffix,
- nsACString &_result)
- {
- nsCOMPtr<nsIURI> newURI;
- nsresult rv = aManifestURL->CloneIgnoringRef(getter_AddRefs(newURI));
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString manifestSpec;
- rv = newURI->GetAsciiSpec(manifestSpec);
- NS_ENSURE_SUCCESS(rv, rv);
- _result.Assign(manifestSpec);
- _result.Append('#');
- _result.Append(aOriginSuffix);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::InitActiveCaches()
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- MutexAutoLock lock(mLock);
- AutoResetStatement statement(mStatement_EnumerateGroups);
- bool hasRows;
- nsresult rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- while (hasRows)
- {
- nsAutoCString group;
- statement->GetUTF8String(0, group);
- nsCString clientID;
- statement->GetUTF8String(1, clientID);
- mActiveCaches.PutEntry(clientID);
- mActiveCachesByGroup.Put(group, new nsCString(clientID));
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::Shutdown()
- {
- NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED);
- {
- MutexAutoLock lock(mLock);
- for (auto iter = mCaches.Iter(); !iter.Done(); iter.Next()) {
- nsCOMPtr<nsIApplicationCache> obj = do_QueryReferent(iter.UserData());
- if (obj) {
- auto appCache = static_cast<nsApplicationCache*>(obj.get());
- appCache->MarkInvalid();
- }
- }
- }
- {
- EvictionObserver evictionObserver(mDB, mEvictionFunction);
- // Delete all rows whose clientID is not an active clientID.
- nsresult rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "DELETE FROM moz_cache WHERE rowid IN"
- " (SELECT moz_cache.rowid FROM"
- " moz_cache LEFT OUTER JOIN moz_cache_groups ON"
- " (moz_cache.ClientID = moz_cache_groups.ActiveClientID)"
- " WHERE moz_cache_groups.GroupID ISNULL)"));
- if (NS_FAILED(rv))
- NS_WARNING("Failed to clean up unused application caches.");
- else
- evictionObserver.Apply();
- // Delete all namespaces whose clientID is not an active clientID.
- rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "DELETE FROM moz_cache_namespaces WHERE rowid IN"
- " (SELECT moz_cache_namespaces.rowid FROM"
- " moz_cache_namespaces LEFT OUTER JOIN moz_cache_groups ON"
- " (moz_cache_namespaces.ClientID = moz_cache_groups.ActiveClientID)"
- " WHERE moz_cache_groups.GroupID ISNULL)"));
- if (NS_FAILED(rv))
- NS_WARNING("Failed to clean up namespaces.");
- mEvictionFunction = nullptr;
- mStatement_CacheSize = nullptr;
- mStatement_ApplicationCacheSize = nullptr;
- mStatement_EntryCount = nullptr;
- mStatement_UpdateEntry = nullptr;
- mStatement_UpdateEntrySize = nullptr;
- mStatement_DeleteEntry = nullptr;
- mStatement_FindEntry = nullptr;
- mStatement_BindEntry = nullptr;
- mStatement_ClearDomain = nullptr;
- mStatement_MarkEntry = nullptr;
- mStatement_UnmarkEntry = nullptr;
- mStatement_GetTypes = nullptr;
- mStatement_FindNamespaceEntry = nullptr;
- mStatement_InsertNamespaceEntry = nullptr;
- mStatement_CleanupUnmarked = nullptr;
- mStatement_GatherEntries = nullptr;
- mStatement_ActivateClient = nullptr;
- mStatement_DeactivateGroup = nullptr;
- mStatement_FindClient = nullptr;
- mStatement_FindClientByNamespace = nullptr;
- mStatement_EnumerateApps = nullptr;
- mStatement_EnumerateGroups = nullptr;
- mStatement_EnumerateGroupsTimeOrder = nullptr;
- }
- // Close Database on the correct thread
- bool isOnCurrentThread = true;
- if (mInitThread)
- mInitThread->IsOnCurrentThread(&isOnCurrentThread);
- if (!isOnCurrentThread) {
- nsCOMPtr<nsIRunnable> ev = new nsCloseDBEvent(mDB);
- if (ev) {
- mInitThread->Dispatch(ev, NS_DISPATCH_NORMAL);
- }
- }
- else {
- mDB->Close();
- }
- mDB = nullptr;
- mInitThread = nullptr;
- return NS_OK;
- }
- const char *
- nsOfflineCacheDevice::GetDeviceID()
- {
- return OFFLINE_CACHE_DEVICE_ID;
- }
- nsCacheEntry *
- nsOfflineCacheDevice::FindEntry(nsCString *fullKey, bool *collision)
- {
- NS_ENSURE_TRUE(Initialized(), nullptr);
- LOG(("nsOfflineCacheDevice::FindEntry [key=%s]\n", fullKey->get()));
- // SELECT * FROM moz_cache WHERE key = ?
- // Decompose the key into "ClientID" and "Key"
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
- return nullptr;
- AutoResetStatement statement(mStatement_FindEntry);
- nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid));
- nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key));
- NS_ENSURE_SUCCESS(rv, nullptr);
- NS_ENSURE_SUCCESS(rv2, nullptr);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- if (NS_FAILED(rv) || !hasRows)
- return nullptr; // entry not found
- nsOfflineCacheRecord rec;
- statement->GetSharedBlob(0, &rec.metaDataLen,
- (const uint8_t **) &rec.metaData);
- rec.generation = statement->AsInt32(1);
- rec.dataSize = statement->AsInt32(2);
- rec.fetchCount = statement->AsInt32(3);
- rec.lastFetched = statement->AsInt64(4);
- rec.lastModified = statement->AsInt64(5);
- rec.expirationTime = statement->AsInt64(6);
- LOG(("entry: [%u %d %d %d %lld %lld %lld]\n",
- rec.metaDataLen,
- rec.generation,
- rec.dataSize,
- rec.fetchCount,
- rec.lastFetched,
- rec.lastModified,
- rec.expirationTime));
- nsCacheEntry *entry = CreateCacheEntry(this, fullKey, rec);
- if (entry)
- {
- // make sure that the data file exists
- nsOfflineCacheBinding *binding = (nsOfflineCacheBinding*)entry->Data();
- bool isFile;
- rv = binding->mDataFile->IsFile(&isFile);
- if (NS_FAILED(rv) || !isFile)
- {
- DeleteEntry(entry, false);
- delete entry;
- return nullptr;
- }
- // lock the entry
- Lock(*fullKey);
- }
- return entry;
- }
- nsresult
- nsOfflineCacheDevice::DeactivateEntry(nsCacheEntry *entry)
- {
- LOG(("nsOfflineCacheDevice::DeactivateEntry [key=%s]\n",
- entry->Key()->get()));
- // This method is called to inform us that the nsCacheEntry object is going
- // away. We should persist anything that needs to be persisted, or if the
- // entry is doomed, we can go ahead and clear its storage.
- if (entry->IsDoomed())
- {
- // remove corresponding row and file if they exist
- // the row should have been removed in DoomEntry... we could assert that
- // that happened. otherwise, all we have to do here is delete the file
- // on disk.
- DeleteData(entry);
- }
- else if (((nsOfflineCacheBinding *)entry->Data())->IsNewEntry())
- {
- // UPDATE the database row
- // Only new entries are updated, since offline cache is updated in
- // transactions. New entries are those who is returned from
- // BindEntry().
- LOG(("nsOfflineCacheDevice::DeactivateEntry updating new entry\n"));
- UpdateEntry(entry);
- } else {
- LOG(("nsOfflineCacheDevice::DeactivateEntry "
- "skipping update since entry is not dirty\n"));
- }
- // Unlock the entry
- Unlock(*entry->Key());
- delete entry;
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::BindEntry [key=%s]\n", entry->Key()->get()));
- NS_ENSURE_STATE(!entry->Data());
- // This method is called to inform us that we have a new entry. The entry
- // may collide with an existing entry in our DB, but if that happens we can
- // assume that the entry is not being used.
- // INSERT the database row
- // XXX Assumption: if the row already exists, then FindEntry would have
- // returned it. if that entry was doomed, then DoomEntry would have removed
- // it from the table. so, we should always have to insert at this point.
- // Decompose the key into "ClientID" and "Key"
- nsAutoCString keyBuf;
- const char *cid, *key;
- if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
- return NS_ERROR_UNEXPECTED;
- // create binding, pick best generation number
- RefPtr<nsOfflineCacheBinding> binding =
- nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key(), -1);
- if (!binding)
- return NS_ERROR_OUT_OF_MEMORY;
- binding->MarkNewEntry();
- nsOfflineCacheRecord rec;
- rec.clientID = cid;
- rec.key = key;
- rec.metaData = nullptr; // don't write any metadata now.
- rec.metaDataLen = 0;
- rec.generation = binding->mGeneration;
- rec.dataSize = 0;
- rec.fetchCount = entry->FetchCount();
- rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
- rec.lastModified = PRTimeFromSeconds(entry->LastModified());
- rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
- AutoResetStatement statement(mStatement_BindEntry);
- nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(rec.clientID));
- nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(rec.key));
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindBlobByIndex(2, rec.metaData, rec.metaDataLen);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt32ByIndex(3, rec.generation);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt32ByIndex(4, rec.dataSize);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt32ByIndex(5, rec.fetchCount);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(6, rec.lastFetched);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(7, rec.lastModified);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = statement->BindInt64ByIndex(8, rec.expirationTime);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(!hasRows, "INSERT should not result in output");
- entry->SetData(binding);
- // lock the entry
- Lock(*entry->Key());
- return NS_OK;
- }
- void
- nsOfflineCacheDevice::DoomEntry(nsCacheEntry *entry)
- {
- LOG(("nsOfflineCacheDevice::DoomEntry [key=%s]\n", entry->Key()->get()));
- // This method is called to inform us that we should mark the entry to be
- // deleted when it is no longer in use.
- // We can go ahead and delete the corresponding row in our table,
- // but we must not delete the file on disk until we are deactivated.
- // In another word, the file should be deleted if the entry had been
- // deactivated.
-
- DeleteEntry(entry, !entry->IsActive());
- }
- nsresult
- nsOfflineCacheDevice::OpenInputStreamForEntry(nsCacheEntry *entry,
- nsCacheAccessMode mode,
- uint32_t offset,
- nsIInputStream **result)
- {
- LOG(("nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s]\n",
- entry->Key()->get()));
- *result = nullptr;
- NS_ENSURE_TRUE(!offset || (offset < entry->DataSize()), NS_ERROR_INVALID_ARG);
- // return an input stream to the entry's data file. the stream
- // may be read on a background thread.
- nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
- NS_ENSURE_STATE(binding);
- nsCOMPtr<nsIInputStream> in;
- NS_NewLocalFileInputStream(getter_AddRefs(in), binding->mDataFile, PR_RDONLY);
- if (!in)
- return NS_ERROR_UNEXPECTED;
- // respect |offset| param
- if (offset != 0)
- {
- nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(in);
- NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED);
- seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
- }
- in.swap(*result);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::OpenOutputStreamForEntry(nsCacheEntry *entry,
- nsCacheAccessMode mode,
- uint32_t offset,
- nsIOutputStream **result)
- {
- LOG(("nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s]\n",
- entry->Key()->get()));
- *result = nullptr;
- NS_ENSURE_TRUE(offset <= entry->DataSize(), NS_ERROR_INVALID_ARG);
- // return an output stream to the entry's data file. we can assume
- // that the output stream will only be used on the main thread.
- nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
- NS_ENSURE_STATE(binding);
- nsCOMPtr<nsIOutputStream> out;
- NS_NewLocalFileOutputStream(getter_AddRefs(out), binding->mDataFile,
- PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
- 00600);
- if (!out)
- return NS_ERROR_UNEXPECTED;
- // respect |offset| param
- nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(out);
- NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED);
- if (offset != 0)
- seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
- // truncate the file at the given offset
- seekable->SetEOF();
- nsCOMPtr<nsIOutputStream> bufferedOut;
- nsresult rv =
- NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 16 * 1024);
- NS_ENSURE_SUCCESS(rv, rv);
- bufferedOut.swap(*result);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetFileForEntry(nsCacheEntry *entry, nsIFile **result)
- {
- LOG(("nsOfflineCacheDevice::GetFileForEntry [key=%s]\n",
- entry->Key()->get()));
- nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
- NS_ENSURE_STATE(binding);
- NS_IF_ADDREF(*result = binding->mDataFile);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::OnDataSizeChange(nsCacheEntry *entry, int32_t deltaSize)
- {
- LOG(("nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d]\n",
- entry->Key()->get(), deltaSize));
- const int32_t DELTA_THRESHOLD = 1<<14; // 16k
- // called to notify us of an impending change in the total size of the
- // specified entry.
- uint32_t oldSize = entry->DataSize();
- NS_ASSERTION(deltaSize >= 0 || int32_t(oldSize) + deltaSize >= 0, "oops");
- uint32_t newSize = int32_t(oldSize) + deltaSize;
- UpdateEntrySize(entry, newSize);
- mDeltaCounter += deltaSize; // this may go negative
- if (mDeltaCounter >= DELTA_THRESHOLD)
- {
- if (CacheSize() > mCacheCapacity) {
- // the entry will overrun the cache capacity, doom the entry
- // and abort
- #ifdef DEBUG
- nsresult rv =
- #endif
- nsCacheService::DoomEntry(entry);
- NS_ASSERTION(NS_SUCCEEDED(rv), "DoomEntry() failed.");
- return NS_ERROR_ABORT;
- }
- mDeltaCounter = 0; // reset counter
- }
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::Visit(nsICacheVisitor *visitor)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- // called to enumerate the offline cache.
- nsCOMPtr<nsICacheDeviceInfo> deviceInfo =
- new nsOfflineCacheDeviceInfo(this);
- bool keepGoing;
- nsresult rv = visitor->VisitDevice(OFFLINE_CACHE_DEVICE_ID, deviceInfo,
- &keepGoing);
- if (NS_FAILED(rv))
- return rv;
-
- if (!keepGoing)
- return NS_OK;
- // SELECT * from moz_cache;
- nsOfflineCacheRecord rec;
- RefPtr<nsOfflineCacheEntryInfo> info = new nsOfflineCacheEntryInfo;
- if (!info)
- return NS_ERROR_OUT_OF_MEMORY;
- info->mRec = &rec;
- // XXX may want to list columns explicitly
- nsCOMPtr<mozIStorageStatement> statement;
- rv = mDB->CreateStatement(
- NS_LITERAL_CSTRING("SELECT * FROM moz_cache;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- for (;;)
- {
- rv = statement->ExecuteStep(&hasRows);
- if (NS_FAILED(rv) || !hasRows)
- break;
- statement->GetSharedUTF8String(0, nullptr, &rec.clientID);
- statement->GetSharedUTF8String(1, nullptr, &rec.key);
- statement->GetSharedBlob(2, &rec.metaDataLen,
- (const uint8_t **) &rec.metaData);
- rec.generation = statement->AsInt32(3);
- rec.dataSize = statement->AsInt32(4);
- rec.fetchCount = statement->AsInt32(5);
- rec.lastFetched = statement->AsInt64(6);
- rec.lastModified = statement->AsInt64(7);
- rec.expirationTime = statement->AsInt64(8);
- bool keepGoing;
- rv = visitor->VisitEntry(OFFLINE_CACHE_DEVICE_ID, info, &keepGoing);
- if (NS_FAILED(rv) || !keepGoing)
- break;
- }
- info->mRec = nullptr;
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::EvictEntries(const char *clientID)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::EvictEntries [cid=%s]\n",
- clientID ? clientID : ""));
- // called to evict all entries matching the given clientID.
- // need trigger to fire user defined function after a row is deleted
- // so we can delete the corresponding data file.
- EvictionObserver evictionObserver(mDB, mEvictionFunction);
- nsCOMPtr<mozIStorageStatement> statement;
- nsresult rv;
- if (clientID)
- {
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=?;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups WHERE ActiveClientID=?;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- // TODO - Should update internal hashtables.
- // Low priority, since this API is not widely used.
- }
- else
- {
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- MutexAutoLock lock(mLock);
- mCaches.Clear();
- mActiveCaches.Clear();
- mActiveCachesByGroup.Clear();
- }
- evictionObserver.Apply();
- statement = nullptr;
- // Also evict any namespaces associated with this clientID.
- if (clientID)
- {
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces WHERE ClientID=?"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- else
- {
- rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces;"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::MarkEntry(const nsCString &clientID,
- const nsACString &key,
- uint32_t typeBits)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::MarkEntry [cid=%s, key=%s, typeBits=%d]\n",
- clientID.get(), PromiseFlatCString(key).get(), typeBits));
- AutoResetStatement statement(mStatement_MarkEntry);
- nsresult rv = statement->BindInt32ByIndex(0, typeBits);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(2, key);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::UnmarkEntry(const nsCString &clientID,
- const nsACString &key,
- uint32_t typeBits)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::UnmarkEntry [cid=%s, key=%s, typeBits=%d]\n",
- clientID.get(), PromiseFlatCString(key).get(), typeBits));
- AutoResetStatement statement(mStatement_UnmarkEntry);
- nsresult rv = statement->BindInt32ByIndex(0, typeBits);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(2, key);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- // Remove the entry if it is now empty.
- EvictionObserver evictionObserver(mDB, mEvictionFunction);
- AutoResetStatement cleanupStatement(mStatement_CleanupUnmarked);
- rv = cleanupStatement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = cleanupStatement->BindUTF8StringByIndex(1, key);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = cleanupStatement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- evictionObserver.Apply();
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetMatchingNamespace(const nsCString &clientID,
- const nsACString &key,
- nsIApplicationCacheNamespace **out)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GetMatchingNamespace [cid=%s, key=%s]\n",
- clientID.get(), PromiseFlatCString(key).get()));
- nsresult rv;
- AutoResetStatement statement(mStatement_FindNamespaceEntry);
- rv = statement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, key);
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- *out = nullptr;
- bool found = false;
- nsCString nsSpec;
- int32_t nsType = 0;
- nsCString nsData;
- while (hasRows)
- {
- int32_t itemType;
- rv = statement->GetInt32(2, &itemType);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!found || itemType > nsType)
- {
- nsType = itemType;
- rv = statement->GetUTF8String(0, nsSpec);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->GetUTF8String(1, nsData);
- NS_ENSURE_SUCCESS(rv, rv);
- found = true;
- }
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (found) {
- nsCOMPtr<nsIApplicationCacheNamespace> ns =
- new nsApplicationCacheNamespace();
- if (!ns)
- return NS_ERROR_OUT_OF_MEMORY;
- rv = ns->Init(nsType, nsSpec, nsData);
- NS_ENSURE_SUCCESS(rv, rv);
- ns.swap(*out);
- }
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::CacheOpportunistically(const nsCString &clientID,
- const nsACString &key)
- {
- // XXX: We should also be propagating this cache entry to other matching
- // caches. See bug 444807.
- return MarkEntry(clientID, key, nsIApplicationCache::ITEM_OPPORTUNISTIC);
- }
- nsresult
- nsOfflineCacheDevice::GetTypes(const nsCString &clientID,
- const nsACString &key,
- uint32_t *typeBits)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GetTypes [cid=%s, key=%s]\n",
- clientID.get(), PromiseFlatCString(key).get()));
- AutoResetStatement statement(mStatement_GetTypes);
- nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, key);
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!hasRows)
- return NS_ERROR_CACHE_KEY_NOT_FOUND;
- *typeBits = statement->AsInt32(0);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GatherEntries(const nsCString &clientID,
- uint32_t typeBits,
- uint32_t *count,
- char ***keys)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GatherEntries [cid=%s, typeBits=%X]\n",
- clientID.get(), typeBits));
- AutoResetStatement statement(mStatement_GatherEntries);
- nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindInt32ByIndex(1, typeBits);
- NS_ENSURE_SUCCESS(rv, rv);
- return RunSimpleQuery(mStatement_GatherEntries, 0, count, keys);
- }
- nsresult
- nsOfflineCacheDevice::AddNamespace(const nsCString &clientID,
- nsIApplicationCacheNamespace *ns)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- nsCString namespaceSpec;
- nsresult rv = ns->GetNamespaceSpec(namespaceSpec);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCString data;
- rv = ns->GetData(data);
- NS_ENSURE_SUCCESS(rv, rv);
- uint32_t itemType;
- rv = ns->GetItemType(&itemType);
- NS_ENSURE_SUCCESS(rv, rv);
- LOG(("nsOfflineCacheDevice::AddNamespace [cid=%s, ns=%s, data=%s, type=%d]",
- clientID.get(), namespaceSpec.get(), data.get(), itemType));
- AutoResetStatement statement(mStatement_InsertNamespaceEntry);
- rv = statement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, namespaceSpec);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(2, data);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindInt32ByIndex(3, itemType);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetUsage(const nsACString &clientID,
- uint32_t *usage)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GetUsage [cid=%s]\n",
- PromiseFlatCString(clientID).get()));
- *usage = 0;
- AutoResetStatement statement(mStatement_ApplicationCacheSize);
- nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!hasRows)
- return NS_OK;
- *usage = static_cast<uint32_t>(statement->AsInt32(0));
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetGroups(uint32_t *count,
- char ***keys)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GetGroups"));
- return RunSimpleQuery(mStatement_EnumerateGroups, 0, count, keys);
- }
- nsresult
- nsOfflineCacheDevice::GetGroupsTimeOrdered(uint32_t *count,
- char ***keys)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- LOG(("nsOfflineCacheDevice::GetGroupsTimeOrder"));
- return RunSimpleQuery(mStatement_EnumerateGroupsTimeOrder, 0, count, keys);
- }
- bool
- nsOfflineCacheDevice::IsLocked(const nsACString &key)
- {
- MutexAutoLock lock(mLock);
- return mLockedEntries.GetEntry(key);
- }
- void
- nsOfflineCacheDevice::Lock(const nsACString &key)
- {
- MutexAutoLock lock(mLock);
- mLockedEntries.PutEntry(key);
- }
- void
- nsOfflineCacheDevice::Unlock(const nsACString &key)
- {
- MutexAutoLock lock(mLock);
- mLockedEntries.RemoveEntry(key);
- }
- nsresult
- nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
- uint32_t resultIndex,
- uint32_t * count,
- char *** values)
- {
- bool hasRows;
- nsresult rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- nsTArray<nsCString> valArray;
- while (hasRows)
- {
- uint32_t length;
- valArray.AppendElement(
- nsDependentCString(statement->AsSharedUTF8String(resultIndex, &length)));
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- *count = valArray.Length();
- char **ret = static_cast<char **>(moz_xmalloc(*count * sizeof(char*)));
- if (!ret) return NS_ERROR_OUT_OF_MEMORY;
- for (uint32_t i = 0; i < *count; i++) {
- ret[i] = NS_strdup(valArray[i].get());
- if (!ret[i]) {
- NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- *values = ret;
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::CreateApplicationCache(const nsACString &group,
- nsIApplicationCache **out)
- {
- *out = nullptr;
- nsCString clientID;
- // Some characters are special in the clientID. Escape the groupID
- // before putting it in to the client key.
- if (!NS_Escape(nsCString(group), clientID, url_Path)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- PRTime now = PR_Now();
- // Include the timestamp to guarantee uniqueness across runs, and
- // the gNextTemporaryClientID for uniqueness within a second.
- clientID.Append(nsPrintfCString("|%016lld|%d",
- now / PR_USEC_PER_SEC,
- gNextTemporaryClientID++));
- nsCOMPtr<nsIApplicationCache> cache = new nsApplicationCache(this,
- group,
- clientID);
- if (!cache)
- return NS_ERROR_OUT_OF_MEMORY;
- nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(cache);
- if (!weak)
- return NS_ERROR_OUT_OF_MEMORY;
- MutexAutoLock lock(mLock);
- mCaches.Put(clientID, weak);
- cache.swap(*out);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetApplicationCache(const nsACString &clientID,
- nsIApplicationCache **out)
- {
- MutexAutoLock lock(mLock);
- return GetApplicationCache_Unlocked(clientID, out);
- }
- nsresult
- nsOfflineCacheDevice::GetApplicationCache_Unlocked(const nsACString &clientID,
- nsIApplicationCache **out)
- {
- *out = nullptr;
- nsCOMPtr<nsIApplicationCache> cache;
- nsWeakPtr weak;
- if (mCaches.Get(clientID, getter_AddRefs(weak)))
- cache = do_QueryReferent(weak);
- if (!cache)
- {
- nsCString group;
- nsresult rv = GetGroupForCache(clientID, group);
- NS_ENSURE_SUCCESS(rv, rv);
- if (group.IsEmpty()) {
- return NS_OK;
- }
- cache = new nsApplicationCache(this, group, clientID);
- weak = do_GetWeakReference(cache);
- if (!weak)
- return NS_ERROR_OUT_OF_MEMORY;
- mCaches.Put(clientID, weak);
- }
- cache.swap(*out);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::GetActiveCache(const nsACString &group,
- nsIApplicationCache **out)
- {
- *out = nullptr;
- MutexAutoLock lock(mLock);
- nsCString *clientID;
- if (mActiveCachesByGroup.Get(group, &clientID))
- return GetApplicationCache_Unlocked(*clientID, out);
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::DeactivateGroup(const nsACString &group)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- nsCString *active = nullptr;
- AutoResetStatement statement(mStatement_DeactivateGroup);
- nsresult rv = statement->BindUTF8StringByIndex(0, group);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- MutexAutoLock lock(mLock);
- if (mActiveCachesByGroup.Get(group, &active))
- {
- mActiveCaches.RemoveEntry(*active);
- mActiveCachesByGroup.Remove(group);
- active = nullptr;
- }
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::Evict(nsILoadContextInfo *aInfo)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_ARG(aInfo);
- nsresult rv;
- mozilla::OriginAttributes const *oa = aInfo->OriginAttributesPtr();
- if (oa->mAppId == NECKO_NO_APP_ID && oa->mInIsolatedMozBrowser == false) {
- nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- return nsCacheService::GlobalInstance()->EvictEntriesInternal(nsICache::STORE_OFFLINE);
- }
- nsAutoCString jaridsuffix;
- jaridsuffix.Append('%');
- nsAutoCString suffix;
- oa->CreateSuffix(suffix);
- jaridsuffix.Append('#');
- jaridsuffix.Append(suffix);
- AutoResetStatement statement(mStatement_EnumerateApps);
- rv = statement->BindUTF8StringByIndex(0, jaridsuffix);
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- while (hasRows) {
- nsAutoCString group;
- rv = statement->GetUTF8String(0, group);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCString clientID;
- rv = statement->GetUTF8String(1, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIRunnable> ev =
- new nsOfflineCacheDiscardCache(this, group, clientID);
- rv = nsCacheService::DispatchToCacheIOThread(ev);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- namespace { // anon
- class OriginMatch final : public mozIStorageFunction
- {
- ~OriginMatch() {}
- mozilla::OriginAttributesPattern const mPattern;
- NS_DECL_ISUPPORTS
- NS_DECL_MOZISTORAGEFUNCTION
- explicit OriginMatch(mozilla::OriginAttributesPattern const &aPattern)
- : mPattern(aPattern) {}
- };
- NS_IMPL_ISUPPORTS(OriginMatch, mozIStorageFunction)
- NS_IMETHODIMP
- OriginMatch::OnFunctionCall(mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
- {
- nsresult rv;
- nsAutoCString groupId;
- rv = aFunctionArguments->GetUTF8String(0, groupId);
- NS_ENSURE_SUCCESS(rv, rv);
- int32_t hash = groupId.Find(NS_LITERAL_CSTRING("#"));
- if (hash == kNotFound) {
- // Just ignore...
- return NS_OK;
- }
- ++hash;
- nsDependentCSubstring suffix(groupId.BeginReading() + hash, groupId.Length() - hash);
- mozilla::NeckoOriginAttributes oa;
- bool ok = oa.PopulateFromSuffix(suffix);
- NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
- bool match = mPattern.Matches(oa);
- RefPtr<nsVariant> outVar(new nsVariant());
- rv = outVar->SetAsUint32(match ? 1 : 0);
- NS_ENSURE_SUCCESS(rv, rv);
- outVar.forget(aResult);
- return NS_OK;
- }
- } // anon
- nsresult
- nsOfflineCacheDevice::Evict(mozilla::OriginAttributesPattern const &aPattern)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- nsresult rv;
- nsCOMPtr<mozIStorageFunction> function1(new OriginMatch(aPattern));
- rv = mDB->CreateFunction(NS_LITERAL_CSTRING("ORIGIN_MATCH"), 1, function1);
- NS_ENSURE_SUCCESS(rv, rv);
- class AutoRemoveFunc {
- public:
- mozIStorageConnection* mDB;
- explicit AutoRemoveFunc(mozIStorageConnection* aDB) : mDB(aDB) {}
- ~AutoRemoveFunc() {
- mDB->RemoveFunction(NS_LITERAL_CSTRING("ORIGIN_MATCH"));
- }
- };
- AutoRemoveFunc autoRemove(mDB);
- nsCOMPtr<mozIStorageStatement> statement;
- rv = mDB->CreateStatement(
- NS_LITERAL_CSTRING("SELECT GroupID, ActiveClientID FROM moz_cache_groups WHERE ORIGIN_MATCH(GroupID);"),
- getter_AddRefs(statement));
- NS_ENSURE_SUCCESS(rv, rv);
- AutoResetStatement statementScope(statement);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- while (hasRows) {
- nsAutoCString group;
- rv = statement->GetUTF8String(0, group);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCString clientID;
- rv = statement->GetUTF8String(1, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIRunnable> ev =
- new nsOfflineCacheDiscardCache(this, group, clientID);
- rv = nsCacheService::DispatchToCacheIOThread(ev);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- bool
- nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI,
- const nsACString &clientID,
- nsILoadContextInfo *loadContextInfo)
- {
- {
- MutexAutoLock lock(mLock);
- if (!mActiveCaches.Contains(clientID))
- return false;
- }
- nsAutoCString groupID;
- nsresult rv = GetGroupForCache(clientID, groupID);
- NS_ENSURE_SUCCESS(rv, false);
- nsCOMPtr<nsIURI> groupURI;
- rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
- if (NS_FAILED(rv)) {
- return false;
- }
- // When we are choosing an initial cache to load the top
- // level document from, the URL of that document must have
- // the same origin as the manifest, according to the spec.
- // The following check is here because explicit, fallback
- // and dynamic entries might have origin different from the
- // manifest origin.
- if (!NS_SecurityCompareURIs(keyURI, groupURI,
- GetStrictFileOriginPolicy())) {
- return false;
- }
- // Check the groupID we found is equal to groupID based
- // on the load context demanding load from app cache.
- // This is check of extended origin.
- nsAutoCString originSuffix;
- loadContextInfo->OriginAttributesPtr()->CreateSuffix(originSuffix);
- nsAutoCString demandedGroupID;
- rv = BuildApplicationCacheGroupID(groupURI, originSuffix, demandedGroupID);
- NS_ENSURE_SUCCESS(rv, false);
- if (groupID != demandedGroupID) {
- return false;
- }
- return true;
- }
- nsresult
- nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key,
- nsILoadContextInfo *loadContextInfo,
- nsIApplicationCache **out)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_ARG(loadContextInfo);
- nsresult rv;
- *out = nullptr;
- nsCOMPtr<nsIURI> keyURI;
- rv = NS_NewURI(getter_AddRefs(keyURI), key);
- NS_ENSURE_SUCCESS(rv, rv);
- // First try to find a matching cache entry.
- AutoResetStatement statement(mStatement_FindClient);
- rv = statement->BindUTF8StringByIndex(0, key);
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasRows;
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- while (hasRows) {
- int32_t itemType;
- rv = statement->GetInt32(1, &itemType);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!(itemType & nsIApplicationCache::ITEM_FOREIGN)) {
- nsAutoCString clientID;
- rv = statement->GetUTF8String(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- if (CanUseCache(keyURI, clientID, loadContextInfo)) {
- return GetApplicationCache(clientID, out);
- }
- }
- rv = statement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // OK, we didn't find an exact match. Search for a client with a
- // matching namespace.
- AutoResetStatement nsstatement(mStatement_FindClientByNamespace);
- rv = nsstatement->BindUTF8StringByIndex(0, key);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = nsstatement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- while (hasRows)
- {
- int32_t itemType;
- rv = nsstatement->GetInt32(1, &itemType);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't associate with a cache based solely on a whitelist entry
- if (!(itemType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) {
- nsAutoCString clientID;
- rv = nsstatement->GetUTF8String(0, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- if (CanUseCache(keyURI, clientID, loadContextInfo)) {
- return GetApplicationCache(clientID, out);
- }
- }
- rv = nsstatement->ExecuteStep(&hasRows);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- nsresult
- nsOfflineCacheDevice::CacheOpportunistically(nsIApplicationCache* cache,
- const nsACString &key)
- {
- NS_ENSURE_ARG_POINTER(cache);
- nsresult rv;
- nsAutoCString clientID;
- rv = cache->GetClientID(clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- return CacheOpportunistically(clientID, key);
- }
- nsresult
- nsOfflineCacheDevice::ActivateCache(const nsCSubstring &group,
- const nsCSubstring &clientID)
- {
- NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
- AutoResetStatement statement(mStatement_ActivateClient);
- nsresult rv = statement->BindUTF8StringByIndex(0, group);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindUTF8StringByIndex(1, clientID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->BindInt32ByIndex(2, SecondsFromPRTime(PR_Now()));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = statement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- MutexAutoLock lock(mLock);
- nsCString *active;
- if (mActiveCachesByGroup.Get(group, &active))
- {
- mActiveCaches.RemoveEntry(*active);
- mActiveCachesByGroup.Remove(group);
- active = nullptr;
- }
- if (!clientID.IsEmpty())
- {
- mActiveCaches.PutEntry(clientID);
- mActiveCachesByGroup.Put(group, new nsCString(clientID));
- }
- return NS_OK;
- }
- bool
- nsOfflineCacheDevice::IsActiveCache(const nsCSubstring &group,
- const nsCSubstring &clientID)
- {
- nsCString *active = nullptr;
- MutexAutoLock lock(mLock);
- return mActiveCachesByGroup.Get(group, &active) && *active == clientID;
- }
- /**
- * Preference accessors
- */
- void
- nsOfflineCacheDevice::SetCacheParentDirectory(nsIFile *parentDir)
- {
- if (Initialized())
- {
- NS_ERROR("cannot switch cache directory once initialized");
- return;
- }
- if (!parentDir)
- {
- mCacheDirectory = nullptr;
- return;
- }
- // ensure parent directory exists
- nsresult rv = EnsureDir(parentDir);
- if (NS_FAILED(rv))
- {
- NS_WARNING("unable to create parent directory");
- return;
- }
- mBaseDirectory = parentDir;
- // cache dir may not exist, but that's ok
- nsCOMPtr<nsIFile> dir;
- rv = parentDir->Clone(getter_AddRefs(dir));
- if (NS_FAILED(rv))
- return;
- rv = dir->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
- if (NS_FAILED(rv))
- return;
- mCacheDirectory = do_QueryInterface(dir);
- }
- void
- nsOfflineCacheDevice::SetCapacity(uint32_t capacity)
- {
- mCacheCapacity = capacity * 1024;
- }
- bool
- nsOfflineCacheDevice::AutoShutdown(nsIApplicationCache * aAppCache)
- {
- if (!mAutoShutdown)
- return false;
- mAutoShutdown = false;
- Shutdown();
- nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID);
- RefPtr<nsCacheService> cacheService = nsCacheService::GlobalInstance();
- cacheService->RemoveCustomOfflineDevice(this);
- nsAutoCString clientID;
- aAppCache->GetClientID(clientID);
- MutexAutoLock lock(mLock);
- mCaches.Remove(clientID);
- return true;
- }
|