nsDiskCacheDeviceSQL.cpp 78 KB

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