12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ActorsParent.h"
- #include "mozIStorageConnection.h"
- #include "mozIStorageService.h"
- #include "nsIBinaryInputStream.h"
- #include "nsIBinaryOutputStream.h"
- #include "nsIFile.h"
- #include "nsIFileStreams.h"
- #include "nsIObserverService.h"
- #include "nsIPermissionManager.h"
- #include "nsIPrincipal.h"
- #include "nsIRunnable.h"
- #include "nsISimpleEnumerator.h"
- #include "nsIScriptObjectPrincipal.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsITimer.h"
- #include "nsIURI.h"
- #include "nsPIDOMWindow.h"
- #include <algorithm>
- #include "GeckoProfiler.h"
- #include "mozilla/Atomics.h"
- #include "mozilla/BasePrincipal.h"
- #include "mozilla/CondVar.h"
- #include "mozilla/dom/PContent.h"
- #include "mozilla/dom/asmjscache/AsmJSCache.h"
- #include "mozilla/dom/cache/QuotaClient.h"
- #include "mozilla/dom/indexedDB/ActorsParent.h"
- #include "mozilla/dom/quota/PQuotaParent.h"
- #include "mozilla/dom/quota/PQuotaRequestParent.h"
- #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
- #include "mozilla/ipc/BackgroundParent.h"
- #include "mozilla/ipc/BackgroundUtils.h"
- #include "mozilla/IntegerRange.h"
- #include "mozilla/Mutex.h"
- #include "mozilla/LazyIdleThread.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/Services.h"
- #include "mozilla/StaticPtr.h"
- #include "mozilla/TypeTraits.h"
- #include "mozilla/Unused.h"
- #include "mozStorageCID.h"
- #include "mozStorageHelper.h"
- #include "nsAppDirectoryServiceDefs.h"
- #include "nsComponentManagerUtils.h"
- #include "nsAboutProtocolUtils.h"
- #include "nsCharSeparatedTokenizer.h"
- #include "nsContentUtils.h"
- #include "nsCRTGlue.h"
- #include "nsDirectoryServiceUtils.h"
- #include "nsEscape.h"
- #include "nsNetUtil.h"
- #include "nsPrintfCString.h"
- #include "nsScriptSecurityManager.h"
- #include "nsThreadUtils.h"
- #include "nsXULAppAPI.h"
- #include "prio.h"
- #include "xpcpublic.h"
- #include "OriginScope.h"
- #include "QuotaManager.h"
- #include "QuotaManagerService.h"
- #include "QuotaObject.h"
- #include "UsageInfo.h"
- #define DISABLE_ASSERTS_FOR_FUZZING 0
- #if DISABLE_ASSERTS_FOR_FUZZING
- #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
- #else
- #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
- #endif
- // The amount of time, in milliseconds, that our IO thread will stay alive
- // after the last event it processes.
- #define DEFAULT_THREAD_TIMEOUT_MS 30000
- // The amount of time, in milliseconds, that we will wait for active storage
- // transactions on shutdown before aborting them.
- #define DEFAULT_SHUTDOWN_TIMER_MS 30000
- // Preference that users can set to override temporary storage smart limit
- // calculation.
- #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
- #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
- // Preference that is used to enable testing features
- #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
- // profile-before-change, when we need to shut down quota manager
- #define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm"
- #define KB * 1024ULL
- #define MB * 1024ULL KB
- #define GB * 1024ULL MB
- namespace mozilla {
- namespace dom {
- namespace quota {
- using namespace mozilla::ipc;
- // We want profiles to be platform-independent so we always need to replace
- // the same characters on every platform. Windows has the most extensive set
- // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
- // FILE_PATH_SEPARATOR.
- const char QuotaManager::kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
- namespace {
- /*******************************************************************************
- * Constants
- ******************************************************************************/
- const uint32_t kSQLitePageSizeOverride = 512;
- // Major storage version. Bump for backwards-incompatible changes.
- const uint32_t kMajorStorageVersion = 1;
- // Minor storage version. Bump for backwards-compatible changes.
- const uint32_t kMinorStorageVersion = 0;
- // The storage version we store in the SQLite database is a (signed) 32-bit
- // integer. The major version is left-shifted 16 bits so the max value is
- // 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
- static_assert(kMajorStorageVersion <= 0xFFFF,
- "Major version needs to fit in 16 bits.");
- static_assert(kMinorStorageVersion <= 0xFFFF,
- "Minor version needs to fit in 16 bits.");
- const int32_t kStorageVersion =
- int32_t((kMajorStorageVersion << 16) + kMinorStorageVersion);
- static_assert(
- static_cast<uint32_t>(StorageType::Persistent) ==
- static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
- "Enum values should match.");
- static_assert(
- static_cast<uint32_t>(StorageType::Temporary) ==
- static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
- "Enum values should match.");
- static_assert(
- static_cast<uint32_t>(StorageType::Default) ==
- static_cast<uint32_t>(PERSISTENCE_TYPE_DEFAULT),
- "Enum values should match.");
- const char kChromeOrigin[] = "chrome";
- const char kAboutHomeOriginPrefix[] = "moz-safe-about:home";
- const char kIndexedDBOriginPrefix[] = "indexeddb://";
- const char kResourceOriginPrefix[] = "resource://";
- #define INDEXEDDB_DIRECTORY_NAME "indexedDB"
- #define STORAGE_DIRECTORY_NAME "storage"
- #define PERSISTENT_DIRECTORY_NAME "persistent"
- #define PERMANENT_DIRECTORY_NAME "permanent"
- #define TEMPORARY_DIRECTORY_NAME "temporary"
- #define DEFAULT_DIRECTORY_NAME "default"
- enum AppId {
- kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
- kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
- };
- #define STORAGE_FILE_NAME "storage.sqlite"
- // The name of the file that we use to load/save the last access time of an
- // origin.
- #define METADATA_FILE_NAME ".metadata"
- #define METADATA_V2_FILE_NAME ".metadata-v2"
- /******************************************************************************
- * SQLite functions
- ******************************************************************************/
- #if 0
- int32_t
- MakeStorageVersion(uint32_t aMajorStorageVersion,
- uint32_t aMinorStorageVersion)
- {
- return int32_t((aMajorStorageVersion << 16) + aMinorStorageVersion);
- }
- #endif
- uint32_t
- GetMajorStorageVersion(int32_t aStorageVersion)
- {
- return uint32_t(aStorageVersion >> 16);
- }
- /******************************************************************************
- * Quota manager class declarations
- ******************************************************************************/
- } // namespace
- class DirectoryLockImpl final
- : public DirectoryLock
- {
- RefPtr<QuotaManager> mQuotaManager;
- const Nullable<PersistenceType> mPersistenceType;
- const nsCString mGroup;
- const OriginScope mOriginScope;
- const Nullable<bool> mIsApp;
- const Nullable<Client::Type> mClientType;
- RefPtr<OpenDirectoryListener> mOpenListener;
- nsTArray<DirectoryLockImpl*> mBlocking;
- nsTArray<DirectoryLockImpl*> mBlockedOn;
- const bool mExclusive;
- // Internal quota manager operations use this flag to prevent directory lock
- // registraction/unregistration from updating origin access time, etc.
- const bool mInternal;
- bool mInvalidated;
- public:
- DirectoryLockImpl(QuotaManager* aQuotaManager,
- Nullable<PersistenceType> aPersistenceType,
- const nsACString& aGroup,
- const OriginScope& aOriginScope,
- Nullable<bool> aIsApp,
- Nullable<Client::Type> aClientType,
- bool aExclusive,
- bool aInternal,
- OpenDirectoryListener* aOpenListener);
- void
- AssertIsOnOwningThread() const
- #ifdef DEBUG
- ;
- #else
- { }
- #endif
- const Nullable<PersistenceType>&
- GetPersistenceType() const
- {
- return mPersistenceType;
- }
- const nsACString&
- GetGroup() const
- {
- return mGroup;
- }
- const OriginScope&
- GetOriginScope() const
- {
- return mOriginScope;
- }
- const Nullable<bool>&
- GetIsApp() const
- {
- return mIsApp;
- }
- const Nullable<Client::Type>&
- GetClientType() const
- {
- return mClientType;
- }
- bool
- IsInternal() const
- {
- return mInternal;
- }
- bool
- ShouldUpdateLockTable()
- {
- return !mInternal &&
- mPersistenceType.Value() != PERSISTENCE_TYPE_PERSISTENT;
- }
- // Test whether this DirectoryLock needs to wait for the given lock.
- bool
- MustWaitFor(const DirectoryLockImpl& aLock);
- void
- AddBlockingLock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- mBlocking.AppendElement(aLock);
- }
- const nsTArray<DirectoryLockImpl*>&
- GetBlockedOnLocks()
- {
- return mBlockedOn;
- }
- void
- AddBlockedOnLock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- mBlockedOn.AppendElement(aLock);
- }
- void
- MaybeUnblock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- mBlockedOn.RemoveElement(aLock);
- if (mBlockedOn.IsEmpty()) {
- NotifyOpenListener();
- }
- }
- void
- NotifyOpenListener();
- void
- Invalidate()
- {
- AssertIsOnOwningThread();
- mInvalidated = true;
- }
- NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl)
- private:
- ~DirectoryLockImpl();
- };
- class QuotaManager::CreateRunnable final
- : public BackgroundThreadObject
- , public Runnable
- {
- nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks;
- nsString mBaseDirPath;
- RefPtr<QuotaManager> mManager;
- nsresult mResultCode;
- enum class State
- {
- Initial,
- CreatingManager,
- RegisteringObserver,
- CallingCallbacks,
- Completed
- };
- State mState;
- public:
- CreateRunnable()
- : mResultCode(NS_OK)
- , mState(State::Initial)
- {
- AssertIsOnBackgroundThread();
- }
- void
- AddCallback(nsIRunnable* aCallback)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aCallback);
- mCallbacks.AppendElement(aCallback);
- }
- private:
- ~CreateRunnable()
- { }
- nsresult
- Init();
- nsresult
- CreateManager();
- nsresult
- RegisterObserver();
- void
- CallCallbacks();
- State
- GetNextState(nsCOMPtr<nsIEventTarget>& aThread);
- NS_DECL_NSIRUNNABLE
- };
- class QuotaManager::ShutdownRunnable final
- : public Runnable
- {
- // Only touched on the main thread.
- bool& mDone;
- public:
- explicit ShutdownRunnable(bool& aDone)
- : mDone(aDone)
- {
- MOZ_ASSERT(NS_IsMainThread());
- }
- private:
- ~ShutdownRunnable()
- { }
- NS_DECL_NSIRUNNABLE
- };
- class QuotaManager::ShutdownObserver final
- : public nsIObserver
- {
- nsCOMPtr<nsIEventTarget> mBackgroundThread;
- public:
- explicit ShutdownObserver(nsIEventTarget* aBackgroundThread)
- : mBackgroundThread(aBackgroundThread)
- {
- MOZ_ASSERT(NS_IsMainThread());
- }
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
- private:
- ~ShutdownObserver()
- {
- MOZ_ASSERT(NS_IsMainThread());
- }
- };
- namespace {
- /*******************************************************************************
- * Local class declarations
- ******************************************************************************/
- } // namespace
- class OriginInfo final
- {
- friend class GroupInfo;
- friend class QuotaManager;
- friend class QuotaObject;
- public:
- OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, bool aIsApp,
- uint64_t aUsage, int64_t aAccessTime)
- : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mUsage(aUsage),
- mAccessTime(aAccessTime), mIsApp(aIsApp)
- {
- MOZ_COUNT_CTOR(OriginInfo);
- }
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
- int64_t
- AccessTime() const
- {
- return mAccessTime;
- }
- private:
- // Private destructor, to discourage deletion outside of Release():
- ~OriginInfo()
- {
- MOZ_COUNT_DTOR(OriginInfo);
- MOZ_ASSERT(!mQuotaObjects.Count());
- }
- void
- LockedDecreaseUsage(int64_t aSize);
- void
- LockedUpdateAccessTime(int64_t aAccessTime)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- mAccessTime = aAccessTime;
- }
- nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
- GroupInfo* mGroupInfo;
- const nsCString mOrigin;
- uint64_t mUsage;
- int64_t mAccessTime;
- const bool mIsApp;
- };
- class OriginInfoLRUComparator
- {
- public:
- bool
- Equals(const OriginInfo* a, const OriginInfo* b) const
- {
- return
- a && b ? a->AccessTime() == b->AccessTime() : !a && !b ? true : false;
- }
- bool
- LessThan(const OriginInfo* a, const OriginInfo* b) const
- {
- return a && b ? a->AccessTime() < b->AccessTime() : b ? true : false;
- }
- };
- class GroupInfo final
- {
- friend class GroupInfoPair;
- friend class OriginInfo;
- friend class QuotaManager;
- friend class QuotaObject;
- public:
- GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType,
- const nsACString& aGroup)
- : mGroupInfoPair(aGroupInfoPair), mPersistenceType(aPersistenceType),
- mGroup(aGroup), mUsage(0)
- {
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- MOZ_COUNT_CTOR(GroupInfo);
- }
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
- private:
- // Private destructor, to discourage deletion outside of Release():
- ~GroupInfo()
- {
- MOZ_COUNT_DTOR(GroupInfo);
- }
- already_AddRefed<OriginInfo>
- LockedGetOriginInfo(const nsACString& aOrigin);
- void
- LockedAddOriginInfo(OriginInfo* aOriginInfo);
- void
- LockedRemoveOriginInfo(const nsACString& aOrigin);
- void
- LockedRemoveOriginInfos();
- bool
- LockedHasOriginInfos()
- {
- AssertCurrentThreadOwnsQuotaMutex();
- return !mOriginInfos.IsEmpty();
- }
- nsTArray<RefPtr<OriginInfo> > mOriginInfos;
- GroupInfoPair* mGroupInfoPair;
- PersistenceType mPersistenceType;
- nsCString mGroup;
- uint64_t mUsage;
- };
- class GroupInfoPair
- {
- friend class QuotaManager;
- friend class QuotaObject;
- public:
- GroupInfoPair()
- {
- MOZ_COUNT_CTOR(GroupInfoPair);
- }
- ~GroupInfoPair()
- {
- MOZ_COUNT_DTOR(GroupInfoPair);
- }
- private:
- already_AddRefed<GroupInfo>
- LockedGetGroupInfo(PersistenceType aPersistenceType)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- RefPtr<GroupInfo> groupInfo =
- GetGroupInfoForPersistenceType(aPersistenceType);
- return groupInfo.forget();
- }
- void
- LockedSetGroupInfo(PersistenceType aPersistenceType, GroupInfo* aGroupInfo)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- RefPtr<GroupInfo>& groupInfo =
- GetGroupInfoForPersistenceType(aPersistenceType);
- groupInfo = aGroupInfo;
- }
- void
- LockedClearGroupInfo(PersistenceType aPersistenceType)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- RefPtr<GroupInfo>& groupInfo =
- GetGroupInfoForPersistenceType(aPersistenceType);
- groupInfo = nullptr;
- }
- bool
- LockedHasGroupInfos()
- {
- AssertCurrentThreadOwnsQuotaMutex();
- return mTemporaryStorageGroupInfo || mDefaultStorageGroupInfo;
- }
- RefPtr<GroupInfo>&
- GetGroupInfoForPersistenceType(PersistenceType aPersistenceType);
- RefPtr<GroupInfo> mTemporaryStorageGroupInfo;
- RefPtr<GroupInfo> mDefaultStorageGroupInfo;
- };
- namespace {
- class CollectOriginsHelper final
- : public Runnable
- {
- uint64_t mMinSizeToBeFreed;
- Mutex& mMutex;
- CondVar mCondVar;
- // The members below are protected by mMutex.
- nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
- uint64_t mSizeToBeFreed;
- bool mWaiting;
- public:
- CollectOriginsHelper(mozilla::Mutex& aMutex,
- uint64_t aMinSizeToBeFreed);
- // Blocks the current thread until origins are collected on the main thread.
- // The returned value contains an aggregate size of those origins.
- int64_t
- BlockAndReturnOriginsForEviction(
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
- private:
- ~CollectOriginsHelper()
- { }
- NS_IMETHOD
- Run() override;
- };
- class OriginOperationBase
- : public BackgroundThreadObject
- , public Runnable
- {
- protected:
- nsresult mResultCode;
- enum State {
- // Not yet run.
- State_Initial,
- // Running initialization on the main thread.
- State_Initializing,
- // Running initialization on the owning thread.
- State_FinishingInit,
- // Running quota manager initialization on the owning thread.
- State_CreatingQuotaManager,
- // Running on the owning thread in the listener for OpenDirectory.
- State_DirectoryOpenPending,
- // Running on the IO thread.
- State_DirectoryWorkOpen,
- // Running on the owning thread after all work is done.
- State_UnblockingOpen,
- // All done.
- State_Complete
- };
- private:
- State mState;
- bool mActorDestroyed;
- protected:
- bool mNeedsMainThreadInit;
- bool mNeedsQuotaManagerInit;
- public:
- void
- NoteActorDestroyed()
- {
- AssertIsOnOwningThread();
- mActorDestroyed = true;
- }
- bool
- IsActorDestroyed() const
- {
- AssertIsOnOwningThread();
- return mActorDestroyed;
- }
- protected:
- explicit OriginOperationBase(
- nsIEventTarget* aOwningThread = NS_GetCurrentThread())
- : BackgroundThreadObject(aOwningThread)
- , mResultCode(NS_OK)
- , mState(State_Initial)
- , mActorDestroyed(false)
- , mNeedsMainThreadInit(false)
- , mNeedsQuotaManagerInit(false)
- { }
- // Reference counted.
- virtual ~OriginOperationBase()
- {
- MOZ_ASSERT(mState == State_Complete);
- MOZ_ASSERT(mActorDestroyed);
- }
- State
- GetState() const
- {
- return mState;
- }
- void
- SetState(State aState)
- {
- MOZ_ASSERT(mState == State_Initial);
- mState = aState;
- }
- void
- AdvanceState()
- {
- switch (mState) {
- case State_Initial:
- mState = State_Initializing;
- return;
- case State_Initializing:
- mState = State_FinishingInit;
- return;
- case State_FinishingInit:
- mState = State_CreatingQuotaManager;
- return;
- case State_CreatingQuotaManager:
- mState = State_DirectoryOpenPending;
- return;
- case State_DirectoryOpenPending:
- mState = State_DirectoryWorkOpen;
- return;
- case State_DirectoryWorkOpen:
- mState = State_UnblockingOpen;
- return;
- case State_UnblockingOpen:
- mState = State_Complete;
- return;
- default:
- MOZ_CRASH("Bad state!");
- }
- }
- NS_IMETHOD
- Run() override;
- virtual nsresult
- DoInitOnMainThread()
- {
- return NS_OK;
- }
- virtual void
- Open() = 0;
- nsresult
- DirectoryOpen();
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) = 0;
- void
- Finish(nsresult aResult);
- virtual void
- UnblockOpen() = 0;
- private:
- nsresult
- Init();
- nsresult
- InitOnMainThread();
- nsresult
- FinishInit();
- nsresult
- QuotaManagerOpen();
- nsresult
- DirectoryWork();
- };
- class FinalizeOriginEvictionOp
- : public OriginOperationBase
- {
- nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
- public:
- FinalizeOriginEvictionOp(nsIEventTarget* aBackgroundThread,
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
- : OriginOperationBase(aBackgroundThread)
- {
- MOZ_ASSERT(!NS_IsMainThread());
- mLocks.SwapElements(aLocks);
- }
- void
- Dispatch();
- void
- RunOnIOThreadImmediately();
- private:
- ~FinalizeOriginEvictionOp()
- { }
- virtual void
- Open() override;
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- virtual void
- UnblockOpen() override;
- };
- class NormalOriginOperationBase
- : public OriginOperationBase
- , public OpenDirectoryListener
- {
- RefPtr<DirectoryLock> mDirectoryLock;
- protected:
- Nullable<PersistenceType> mPersistenceType;
- OriginScope mOriginScope;
- mozilla::Atomic<bool> mCanceled;
- const bool mExclusive;
- public:
- void
- RunImmediately()
- {
- MOZ_ASSERT(GetState() == State_Initial);
- MOZ_ALWAYS_SUCCEEDS(this->Run());
- }
- protected:
- NormalOriginOperationBase(Nullable<PersistenceType> aPersistenceType,
- const OriginScope& aOriginScope,
- bool aExclusive)
- : mPersistenceType(aPersistenceType)
- , mOriginScope(aOriginScope)
- , mExclusive(aExclusive)
- {
- AssertIsOnOwningThread();
- }
- ~NormalOriginOperationBase()
- { }
- private:
- NS_DECL_ISUPPORTS_INHERITED
- virtual void
- Open() override;
- virtual void
- UnblockOpen() override;
- // OpenDirectoryListener overrides.
- virtual void
- DirectoryLockAcquired(DirectoryLock* aLock) override;
- virtual void
- DirectoryLockFailed() override;
- // Used to send results before unblocking open.
- virtual void
- SendResults() = 0;
- };
- class SaveOriginAccessTimeOp
- : public NormalOriginOperationBase
- {
- int64_t mTimestamp;
- public:
- SaveOriginAccessTimeOp(PersistenceType aPersistenceType,
- const nsACString& aOrigin,
- int64_t aTimestamp)
- : NormalOriginOperationBase(Nullable<PersistenceType>(aPersistenceType),
- OriginScope::FromOrigin(aOrigin),
- /* aExclusive */ false)
- , mTimestamp(aTimestamp)
- {
- AssertIsOnOwningThread();
- }
- private:
- ~SaveOriginAccessTimeOp()
- { }
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- virtual void
- SendResults() override;
- };
- /*******************************************************************************
- * Actor class declarations
- ******************************************************************************/
- class Quota final
- : public PQuotaParent
- {
- #ifdef DEBUG
- bool mActorDestroyed;
- #endif
- public:
- Quota();
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota)
- private:
- ~Quota();
- void
- StartIdleMaintenance();
- // IPDL methods.
- virtual void
- ActorDestroy(ActorDestroyReason aWhy) override;
- virtual PQuotaUsageRequestParent*
- AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams) override;
- virtual bool
- RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
- const UsageRequestParams& aParams) override;
- virtual bool
- DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor) override;
- virtual PQuotaRequestParent*
- AllocPQuotaRequestParent(const RequestParams& aParams) override;
- virtual bool
- RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
- const RequestParams& aParams) override;
- virtual bool
- DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override;
- virtual bool
- RecvStartIdleMaintenance() override;
- virtual bool
- RecvStopIdleMaintenance() override;
- };
- class QuotaUsageRequestBase
- : public NormalOriginOperationBase
- , public PQuotaUsageRequestParent
- {
- public:
- // May be overridden by subclasses if they need to perform work on the
- // background thread before being run.
- virtual bool
- Init(Quota* aQuota);
- protected:
- QuotaUsageRequestBase()
- : NormalOriginOperationBase(Nullable<PersistenceType>(),
- OriginScope::FromNull(),
- /* aExclusive */ false)
- { }
- nsresult
- GetUsageForOrigin(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- UsageInfo* aUsageInfo);
- // Subclasses use this override to set the IPDL response value.
- virtual void
- GetResponse(UsageRequestResponse& aResponse) = 0;
- private:
- void
- SendResults() override;
- // IPDL methods.
- void
- ActorDestroy(ActorDestroyReason aWhy) override;
- bool
- RecvCancel() override;
- };
- class GetUsageOp final
- : public QuotaUsageRequestBase
- {
- nsTArray<OriginUsage> mOriginUsages;
- nsDataHashtable<nsCStringHashKey, uint32_t> mOriginUsagesIndex;
- bool mGetAll;
- public:
- explicit GetUsageOp(const UsageRequestParams& aParams);
- private:
- ~GetUsageOp()
- { }
- nsresult
- TraverseRepository(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType);
- nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- void
- GetResponse(UsageRequestResponse& aResponse) override;
- };
- class GetOriginUsageOp final
- : public QuotaUsageRequestBase
- {
- // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
- // and the file usage. Otherwise, we use it to record the group usage and the
- // limit.
- UsageInfo mUsageInfo;
- const OriginUsageParams mParams;
- nsCString mSuffix;
- nsCString mGroup;
- bool mIsApp;
- bool mGetGroupUsage;
- public:
- explicit GetOriginUsageOp(const UsageRequestParams& aParams);
- MOZ_IS_CLASS_INIT bool
- Init(Quota* aQuota) override;
- private:
- ~GetOriginUsageOp()
- { }
- MOZ_IS_CLASS_INIT virtual nsresult
- DoInitOnMainThread() override;
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- void
- GetResponse(UsageRequestResponse& aResponse) override;
- };
- class QuotaRequestBase
- : public NormalOriginOperationBase
- , public PQuotaRequestParent
- {
- public:
- // May be overridden by subclasses if they need to perform work on the
- // background thread before being run.
- virtual bool
- Init(Quota* aQuota);
- protected:
- explicit QuotaRequestBase(bool aExclusive)
- : NormalOriginOperationBase(Nullable<PersistenceType>(),
- OriginScope::FromNull(),
- aExclusive)
- { }
- // Subclasses use this override to set the IPDL response value.
- virtual void
- GetResponse(RequestResponse& aResponse) = 0;
- private:
- virtual void
- SendResults() override;
- // IPDL methods.
- virtual void
- ActorDestroy(ActorDestroyReason aWhy) override;
- };
- class ResetOrClearOp final
- : public QuotaRequestBase
- {
- const bool mClear;
- public:
- explicit ResetOrClearOp(bool aClear)
- : QuotaRequestBase(/* aExclusive */ true)
- , mClear(aClear)
- {
- AssertIsOnOwningThread();
- }
- private:
- ~ResetOrClearOp()
- { }
- void
- DeleteFiles(QuotaManager* aQuotaManager);
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- virtual void
- GetResponse(RequestResponse& aResponse) override;
- };
- class OriginClearOp final
- : public QuotaRequestBase
- {
- const RequestParams mParams;
- const bool mMultiple;
- public:
- explicit OriginClearOp(const RequestParams& aParams);
- virtual bool
- Init(Quota* aQuota) override;
- private:
- ~OriginClearOp()
- { }
- virtual nsresult
- DoInitOnMainThread() override;
- void
- DeleteFiles(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType);
- virtual nsresult
- DoDirectoryWork(QuotaManager* aQuotaManager) override;
- virtual void
- GetResponse(RequestResponse& aResponse) override;
- };
- /*******************************************************************************
- * Helper Functions
- ******************************************************************************/
- template <typename T, bool = mozilla::IsUnsigned<T>::value>
- struct IntChecker
- {
- static void
- Assert(T aInt)
- {
- static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
- MOZ_ASSERT(aInt >= 0);
- }
- };
- template <typename T>
- struct IntChecker<T, true>
- {
- static void
- Assert(T aInt)
- {
- static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
- }
- };
- template <typename T>
- void
- AssertNoOverflow(uint64_t aDest, T aArg)
- {
- IntChecker<T>::Assert(aDest);
- IntChecker<T>::Assert(aArg);
- MOZ_ASSERT(UINT64_MAX - aDest >= uint64_t(aArg));
- }
- template <typename T, typename U>
- void
- AssertNoUnderflow(T aDest, U aArg)
- {
- IntChecker<T>::Assert(aDest);
- IntChecker<T>::Assert(aArg);
- MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg));
- }
- } // namespace
- BackgroundThreadObject::BackgroundThreadObject()
- : mOwningThread(NS_GetCurrentThread())
- {
- AssertIsOnOwningThread();
- }
- BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget* aOwningThread)
- : mOwningThread(aOwningThread)
- {
- }
- #ifdef DEBUG
- void
- BackgroundThreadObject::AssertIsOnOwningThread() const
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(mOwningThread);
- bool current;
- MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)));
- MOZ_ASSERT(current);
- }
- #endif // DEBUG
- nsIEventTarget*
- BackgroundThreadObject::OwningThread() const
- {
- MOZ_ASSERT(mOwningThread);
- return mOwningThread;
- }
- bool
- IsOnIOThread()
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- NS_ASSERTION(quotaManager, "Must have a manager here!");
- bool currentThread;
- return NS_SUCCEEDED(quotaManager->IOThread()->
- IsOnCurrentThread(¤tThread)) && currentThread;
- }
- void
- AssertIsOnIOThread()
- {
- NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
- }
- void
- AssertCurrentThreadOwnsQuotaMutex()
- {
- #ifdef DEBUG
- QuotaManager* quotaManager = QuotaManager::Get();
- NS_ASSERTION(quotaManager, "Must have a manager here!");
- quotaManager->AssertCurrentThreadOwnsQuotaMutex();
- #endif
- }
- void
- ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr)
- {
- // Get leaf of file path
- for (const char* p = aFile; *p; ++p) {
- if (*p == '/' && *(p + 1)) {
- aFile = p + 1;
- }
- }
- nsContentUtils::LogSimpleConsoleError(
- NS_ConvertUTF8toUTF16(nsPrintfCString(
- "Quota %s: %s:%lu", aStr, aFile, aLine)),
- "quota");
- }
- namespace {
- StaticRefPtr<QuotaManager> gInstance;
- bool gCreateFailed = false;
- StaticRefPtr<QuotaManager::CreateRunnable> gCreateRunnable;
- mozilla::Atomic<bool> gShutdown(false);
- // Constants for temporary storage limit computing.
- static const int32_t kDefaultFixedLimitKB = -1;
- static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
- int32_t gFixedLimitKB = kDefaultFixedLimitKB;
- uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
- bool gTestingEnabled = false;
- class StorageDirectoryHelper
- : public Runnable
- {
- mozilla::Mutex mMutex;
- mozilla::CondVar mCondVar;
- nsresult mMainThreadResultCode;
- bool mWaiting;
- protected:
- struct OriginProps;
- nsTArray<OriginProps> mOriginProps;
- nsCOMPtr<nsIFile> mDirectory;
- public:
- StorageDirectoryHelper(nsIFile* aDirectory)
- : mMutex("StorageDirectoryHelper::mMutex")
- , mCondVar(mMutex, "StorageDirectoryHelper::mCondVar")
- , mMainThreadResultCode(NS_OK)
- , mWaiting(true)
- , mDirectory(aDirectory)
- {
- AssertIsOnIOThread();
- }
- protected:
- ~StorageDirectoryHelper()
- { }
- nsresult
- AddOriginDirectory(nsIFile* aDirectory,
- OriginProps** aOriginProps);
- nsresult
- ProcessOriginDirectories();
- virtual nsresult
- DoProcessOriginDirectories() = 0;
- private:
- nsresult
- RunOnMainThread();
- NS_IMETHOD
- Run() override;
- };
- struct StorageDirectoryHelper::OriginProps
- {
- enum Type
- {
- eChrome,
- eContent
- };
- nsCOMPtr<nsIFile> mDirectory;
- nsCString mSpec;
- PrincipalOriginAttributes mAttrs;
- int64_t mTimestamp;
- nsCString mSuffix;
- nsCString mGroup;
- nsCString mOrigin;
- Type mType;
- bool mIsApp;
- bool mNeedsRestore;
- bool mIgnore;
- public:
- explicit OriginProps()
- : mTimestamp(0)
- , mType(eContent)
- , mIsApp(false)
- , mNeedsRestore(false)
- , mIgnore(false)
- { }
- };
- class MOZ_STACK_CLASS OriginParser final
- {
- static bool
- IgnoreWhitespace(char16_t /* aChar */)
- {
- return false;
- }
- typedef nsCCharSeparatedTokenizerTemplate<IgnoreWhitespace> Tokenizer;
- enum SchemaType {
- eNone,
- eFile,
- eAbout
- };
- enum State {
- eExpectingAppIdOrSchema,
- eExpectingInMozBrowser,
- eExpectingSchema,
- eExpectingEmptyToken1,
- eExpectingEmptyToken2,
- eExpectingEmptyToken3,
- eExpectingHost,
- eExpectingPort,
- eExpectingEmptyTokenOrDriveLetterOrPathnameComponent,
- eExpectingEmptyTokenOrPathnameComponent,
- eComplete,
- eHandledTrailingSeparator
- };
- const nsCString mOrigin;
- const PrincipalOriginAttributes mOriginAttributes;
- Tokenizer mTokenizer;
- uint32_t mAppId;
- nsCString mSchema;
- nsCString mHost;
- Nullable<uint32_t> mPort;
- nsTArray<nsCString> mPathnameComponents;
- nsCString mHandledTokens;
- SchemaType mSchemaType;
- State mState;
- bool mInIsolatedMozBrowser;
- bool mMaybeDriveLetter;
- bool mError;
- public:
- OriginParser(const nsACString& aOrigin,
- const PrincipalOriginAttributes& aOriginAttributes)
- : mOrigin(aOrigin)
- , mOriginAttributes(aOriginAttributes)
- , mTokenizer(aOrigin, '+')
- , mAppId(kNoAppId)
- , mPort()
- , mSchemaType(eNone)
- , mState(eExpectingAppIdOrSchema)
- , mInIsolatedMozBrowser(false)
- , mMaybeDriveLetter(false)
- , mError(false)
- { }
- static bool
- ParseOrigin(const nsACString& aOrigin,
- nsCString& aSpec,
- PrincipalOriginAttributes* aAttrs);
- bool
- Parse(nsACString& aSpec, PrincipalOriginAttributes* aAttrs);
- private:
- void
- HandleSchema(const nsDependentCSubstring& aSchema);
- void
- HandlePathnameComponent(const nsDependentCSubstring& aSchema);
- void
- HandleToken(const nsDependentCSubstring& aToken);
- void
- HandleTrailingSeparator();
- };
- class CreateOrUpgradeDirectoryMetadataHelper final
- : public StorageDirectoryHelper
- {
- const bool mPersistent;
- public:
- CreateOrUpgradeDirectoryMetadataHelper(nsIFile* aDirectory,
- bool aPersistent)
- : StorageDirectoryHelper(aDirectory)
- , mPersistent(aPersistent)
- { }
- nsresult
- CreateOrUpgradeMetadataFiles();
- private:
- nsresult
- MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
- nsresult
- GetDirectoryMetadata(nsIFile* aDirectory,
- int64_t* aTimestamp,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aHasIsApp);
- virtual nsresult
- DoProcessOriginDirectories();
- };
- class UpgradeDirectoryMetadataFrom1To2Helper final
- : public StorageDirectoryHelper
- {
- const bool mPersistent;
- public:
- UpgradeDirectoryMetadataFrom1To2Helper(nsIFile* aDirectory,
- bool aPersistent)
- : StorageDirectoryHelper(aDirectory)
- , mPersistent(aPersistent)
- { }
- nsresult
- UpgradeMetadataFiles();
- private:
- nsresult
- GetDirectoryMetadata(nsIFile* aDirectory,
- int64_t* aTimestamp,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aIsApp);
- virtual nsresult
- DoProcessOriginDirectories() override;
- };
- class RestoreDirectoryMetadata2Helper final
- : public StorageDirectoryHelper
- {
- const bool mPersistent;
- public:
- RestoreDirectoryMetadata2Helper(nsIFile* aDirectory,
- bool aPersistent)
- : StorageDirectoryHelper(aDirectory)
- , mPersistent(aPersistent)
- { }
- nsresult
- RestoreMetadata2File();
- private:
- virtual nsresult
- DoProcessOriginDirectories();
- };
- class OriginKey : public nsAutoCString
- {
- public:
- OriginKey(PersistenceType aPersistenceType,
- const nsACString& aOrigin)
- {
- PersistenceTypeToText(aPersistenceType, *this);
- Append(':');
- Append(aOrigin);
- }
- };
- void
- SanitizeOriginString(nsCString& aOrigin)
- {
- #ifdef XP_WIN
- NS_ASSERTION(!strcmp(QuotaManager::kReplaceChars,
- FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
- "Illegal file characters have changed!");
- #endif
- aOrigin.ReplaceChar(QuotaManager::kReplaceChars, '+');
- }
- bool
- IsTreatedAsPersistent(PersistenceType aPersistenceType,
- bool aIsApp)
- {
- if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
- (aPersistenceType == PERSISTENCE_TYPE_DEFAULT && aIsApp)) {
- return true;
- }
- return false;
- }
- bool
- IsTreatedAsTemporary(PersistenceType aPersistenceType,
- bool aIsApp)
- {
- return !IsTreatedAsPersistent(aPersistenceType, aIsApp);
- }
- nsresult
- CloneStoragePath(nsIFile* aBaseDir,
- const nsAString& aStorageName,
- nsAString& aStoragePath)
- {
- nsresult rv;
- nsCOMPtr<nsIFile> storageDir;
- rv = aBaseDir->Clone(getter_AddRefs(storageDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = storageDir->Append(aStorageName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = storageDir->GetPath(aStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aFile);
- MOZ_ASSERT(aTimestamp);
- class MOZ_STACK_CLASS Helper final
- {
- public:
- static nsresult
- GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aFile);
- MOZ_ASSERT(aTimestamp);
- bool isDirectory;
- nsresult rv = aFile->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- nsString leafName;
- rv = aFile->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
- leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
- leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- return NS_OK;
- }
- int64_t timestamp;
- rv = aFile->GetLastModifiedTime(×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Need to convert from milliseconds to microseconds.
- MOZ_ASSERT((INT64_MAX / PR_USEC_PER_MSEC) > timestamp);
- timestamp *= int64_t(PR_USEC_PER_MSEC);
- if (timestamp > *aTimestamp) {
- *aTimestamp = timestamp;
- }
- return NS_OK;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- MOZ_ASSERT(file);
- rv = GetLastModifiedTime(file, aTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- };
- int64_t timestamp = INT64_MIN;
- nsresult rv = Helper::GetLastModifiedTime(aFile, ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aTimestamp = timestamp;
- return NS_OK;
- }
- nsresult
- EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
- {
- AssertIsOnIOThread();
- nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
- if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
- bool isDirectory;
- rv = aDirectory->IsDirectory(&isDirectory);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
- *aCreated = false;
- }
- else {
- NS_ENSURE_SUCCESS(rv, rv);
- *aCreated = true;
- }
- return NS_OK;
- }
- enum FileFlag {
- kTruncateFileFlag,
- kUpdateFileFlag,
- kAppendFileFlag
- };
- nsresult
- GetOutputStream(nsIFile* aDirectory,
- const nsAString& aFilename,
- FileFlag aFileFlag,
- nsIOutputStream** aStream)
- {
- AssertIsOnIOThread();
- nsCOMPtr<nsIFile> file;
- nsresult rv = aDirectory->Clone(getter_AddRefs(file));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = file->Append(aFilename);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIOutputStream> outputStream;
- switch (aFileFlag) {
- case kTruncateFileFlag: {
- rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
- file);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- break;
- }
- case kUpdateFileFlag: {
- bool exists;
- rv = file->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- *aStream = nullptr;
- return NS_OK;
- }
- nsCOMPtr<nsIFileStream> stream;
- rv = NS_NewLocalFileStream(getter_AddRefs(stream), file);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- outputStream = do_QueryInterface(stream);
- if (NS_WARN_IF(!outputStream)) {
- return NS_ERROR_FAILURE;
- }
- break;
- }
- case kAppendFileFlag: {
- rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
- file,
- PR_WRONLY | PR_CREATE_FILE | PR_APPEND);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- break;
- }
- default:
- MOZ_CRASH("Should never get here!");
- }
- outputStream.forget(aStream);
- return NS_OK;
- }
- nsresult
- GetBinaryOutputStream(nsIFile* aDirectory,
- const nsAString& aFilename,
- FileFlag aFileFlag,
- nsIBinaryOutputStream** aStream)
- {
- nsCOMPtr<nsIOutputStream> outputStream;
- nsresult rv = GetOutputStream(aDirectory,
- aFilename,
- aFileFlag,
- getter_AddRefs(outputStream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIBinaryOutputStream> binaryStream =
- do_CreateInstance("@mozilla.org/binaryoutputstream;1");
- if (NS_WARN_IF(!binaryStream)) {
- return NS_ERROR_FAILURE;
- }
- rv = binaryStream->SetOutputStream(outputStream);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- binaryStream.forget(aStream);
- return NS_OK;
- }
- void
- GetJarPrefix(uint32_t aAppId,
- bool aInIsolatedMozBrowser,
- nsACString& aJarPrefix)
- {
- MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
- if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
- aAppId = nsIScriptSecurityManager::NO_APP_ID;
- }
- aJarPrefix.Truncate();
- // Fallback.
- if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInIsolatedMozBrowser) {
- return;
- }
- // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
- aJarPrefix.AppendInt(aAppId);
- aJarPrefix.Append('+');
- aJarPrefix.Append(aInIsolatedMozBrowser ? 't' : 'f');
- aJarPrefix.Append('+');
- }
- nsresult
- CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
- const nsACString& aSuffix, const nsACString& aGroup,
- const nsACString& aOrigin, bool aIsApp)
- {
- AssertIsOnIOThread();
- PrincipalOriginAttributes groupAttributes;
- nsCString groupNoSuffix;
- bool ok = groupAttributes.PopulateFromOrigin(aGroup, groupNoSuffix);
- if (!ok) {
- return NS_ERROR_FAILURE;
- }
- nsCString groupPrefix;
- GetJarPrefix(groupAttributes.mAppId,
- groupAttributes.mInIsolatedMozBrowser,
- groupPrefix);
- nsCString group = groupPrefix + groupNoSuffix;
- PrincipalOriginAttributes originAttributes;
- nsCString originNoSuffix;
- ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
- if (!ok) {
- return NS_ERROR_FAILURE;
- }
- nsCString originPrefix;
- GetJarPrefix(originAttributes.mAppId,
- originAttributes.mInIsolatedMozBrowser,
- originPrefix);
- nsCString origin = originPrefix + originNoSuffix;
- MOZ_ASSERT(groupPrefix == originPrefix);
- nsCOMPtr<nsIBinaryOutputStream> stream;
- nsresult rv = GetBinaryOutputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_FILE_NAME),
- kTruncateFileFlag,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(stream);
- rv = stream->Write64(aTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteStringZ(group.get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteStringZ(origin.get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteBoolean(aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- CreateDirectoryMetadata2(nsIFile* aDirectory, int64_t aTimestamp,
- const nsACString& aSuffix, const nsACString& aGroup,
- const nsACString& aOrigin, bool aIsApp)
- {
- AssertIsOnIOThread();
- nsCOMPtr<nsIBinaryOutputStream> stream;
- nsresult rv = GetBinaryOutputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
- kTruncateFileFlag,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(stream);
- rv = stream->Write64(aTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Reserved for navigator.persist()
- rv = stream->WriteBoolean(false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Reserved data 1
- rv = stream->Write32(0);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Reserved data 2
- rv = stream->Write32(0);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteStringZ(PromiseFlatCString(aSuffix).get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = stream->WriteBoolean(aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- GetBinaryInputStream(nsIFile* aDirectory,
- const nsAString& aFilename,
- nsIBinaryInputStream** aStream)
- {
- MOZ_ASSERT(!NS_IsMainThread());
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aStream);
- nsCOMPtr<nsIFile> file;
- nsresult rv = aDirectory->Clone(getter_AddRefs(file));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = file->Append(aFilename);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIInputStream> bufferedStream;
- rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIBinaryInputStream> binaryStream =
- do_CreateInstance("@mozilla.org/binaryinputstream;1");
- if (NS_WARN_IF(!binaryStream)) {
- return NS_ERROR_FAILURE;
- }
- rv = binaryStream->SetInputStream(bufferedStream);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- binaryStream.forget(aStream);
- return NS_OK;
- }
- // This method computes and returns our best guess for the temporary storage
- // limit (in bytes), based on the amount of space users have free on their hard
- // drive and on given temporary storage usage (also in bytes).
- nsresult
- GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
- uint64_t* aLimit)
- {
- // Check for free space on device where temporary storage directory lives.
- int64_t bytesAvailable;
- nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
- uint64_t availableKB =
- static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
- // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
- // we don't shrink temporary storage and evict origin data every time we
- // initialize.
- availableKB = (availableKB / gChunkSizeKB) * gChunkSizeKB;
- // Allow temporary storage to consume up to half the available space.
- uint64_t resultKB = availableKB * .50;
- *aLimit = resultKB * 1024;
- return NS_OK;
- }
- } // namespace
- /*******************************************************************************
- * Exported functions
- ******************************************************************************/
- PQuotaParent*
- AllocPQuotaParent()
- {
- AssertIsOnBackgroundThread();
- if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
- return nullptr;
- }
- RefPtr<Quota> actor = new Quota();
- return actor.forget().take();
- }
- bool
- DeallocPQuotaParent(PQuotaParent* aActor)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aActor);
- RefPtr<Quota> actor = dont_AddRef(static_cast<Quota*>(aActor));
- return true;
- }
- /*******************************************************************************
- * Directory lock
- ******************************************************************************/
- DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager,
- Nullable<PersistenceType> aPersistenceType,
- const nsACString& aGroup,
- const OriginScope& aOriginScope,
- Nullable<bool> aIsApp,
- Nullable<Client::Type> aClientType,
- bool aExclusive,
- bool aInternal,
- OpenDirectoryListener* aOpenListener)
- : mQuotaManager(aQuotaManager)
- , mPersistenceType(aPersistenceType)
- , mGroup(aGroup)
- , mOriginScope(aOriginScope)
- , mIsApp(aIsApp)
- , mClientType(aClientType)
- , mOpenListener(aOpenListener)
- , mExclusive(aExclusive)
- , mInternal(aInternal)
- , mInvalidated(false)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aQuotaManager);
- MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
- MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
- MOZ_ASSERT_IF(!aInternal,
- aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
- MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
- MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
- MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
- MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
- MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
- MOZ_ASSERT_IF(!aInternal, aOpenListener);
- }
- DirectoryLockImpl::~DirectoryLockImpl()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mQuotaManager);
- for (DirectoryLockImpl* blockingLock : mBlocking) {
- blockingLock->MaybeUnblock(this);
- }
- mBlocking.Clear();
- mQuotaManager->UnregisterDirectoryLock(this);
- }
- #ifdef DEBUG
- void
- DirectoryLockImpl::AssertIsOnOwningThread() const
- {
- MOZ_ASSERT(mQuotaManager);
- mQuotaManager->AssertIsOnOwningThread();
- }
- #endif // DEBUG
- bool
- DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock)
- {
- AssertIsOnOwningThread();
- // Waiting is never required if the ops in comparison represent shared locks.
- if (!aExistingLock.mExclusive && !mExclusive) {
- return false;
- }
- // If the persistence types don't overlap, the op can proceed.
- if (!aExistingLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
- aExistingLock.mPersistenceType.Value() != mPersistenceType.Value()) {
- return false;
- }
- // If the origin scopes don't overlap, the op can proceed.
- bool match = aExistingLock.mOriginScope.Matches(mOriginScope);
- if (!match) {
- return false;
- }
- // If the client types don't overlap, the op can proceed.
- if (!aExistingLock.mClientType.IsNull() && !mClientType.IsNull() &&
- aExistingLock.mClientType.Value() != mClientType.Value()) {
- return false;
- }
- // Otherwise, when all attributes overlap (persistence type, origin scope and
- // client type) the op must wait.
- return true;
- }
- void
- DirectoryLockImpl::NotifyOpenListener()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mQuotaManager);
- MOZ_ASSERT(mOpenListener);
- if (mInvalidated) {
- mOpenListener->DirectoryLockFailed();
- } else {
- mOpenListener->DirectoryLockAcquired(this);
- }
- mOpenListener = nullptr;
- mQuotaManager->RemovePendingDirectoryLock(this);
- }
- nsresult
- QuotaManager::
- CreateRunnable::Init()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mState == State::Initial);
- nsresult rv;
- nsCOMPtr<nsIFile> baseDir;
- rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
- getter_AddRefs(baseDir));
- if (NS_FAILED(rv)) {
- rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(baseDir));
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = baseDir->GetPath(mBaseDirPath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::
- CreateRunnable::CreateManager()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State::CreatingManager);
- mManager = new QuotaManager();
- nsresult rv = mManager->Init(mBaseDirPath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::
- CreateRunnable::RegisterObserver()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mState == State::RegisteringObserver);
- if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
- kDefaultFixedLimitKB)) ||
- NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
- PREF_CHUNK_SIZE,
- kDefaultChunkSizeKB))) {
- NS_WARNING("Unable to respond to temp storage pref changes!");
- }
- if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
- PREF_TESTING_FEATURES, false))) {
- NS_WARNING("Unable to respond to testing pref changes!");
- }
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (NS_WARN_IF(!observerService)) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mOwningThread);
- nsresult rv =
- observerService->AddObserver(observer,
- PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID,
- false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // This service has to be started on the main thread currently.
- nsCOMPtr<mozIStorageService> ss =
- do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- QuotaManagerService* qms = QuotaManagerService::GetOrCreate();
- if (NS_WARN_IF(!qms)) {
- return rv;
- }
- qms->NoteLiveManager(mManager);
- for (RefPtr<Client>& client : mManager->mClients) {
- client->DidInitialize(mManager);
- }
- return NS_OK;
- }
- void
- QuotaManager::
- CreateRunnable::CallCallbacks()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State::CallingCallbacks);
- gCreateRunnable = nullptr;
- if (NS_FAILED(mResultCode)) {
- gCreateFailed = true;
- } else {
- gInstance = mManager;
- }
- mManager = nullptr;
- nsTArray<nsCOMPtr<nsIRunnable>> callbacks;
- mCallbacks.SwapElements(callbacks);
- for (nsCOMPtr<nsIRunnable>& callback : callbacks) {
- Unused << callback->Run();
- }
- }
- auto
- QuotaManager::
- CreateRunnable::GetNextState(nsCOMPtr<nsIEventTarget>& aThread) -> State
- {
- switch (mState) {
- case State::Initial:
- aThread = mOwningThread;
- return State::CreatingManager;
- case State::CreatingManager:
- aThread = do_GetMainThread();
- return State::RegisteringObserver;
- case State::RegisteringObserver:
- aThread = mOwningThread;
- return State::CallingCallbacks;
- case State::CallingCallbacks:
- aThread = nullptr;
- return State::Completed;
- default:
- MOZ_CRASH("Bad state!");
- }
- }
- NS_IMETHODIMP
- QuotaManager::
- CreateRunnable::Run()
- {
- nsresult rv;
- switch (mState) {
- case State::Initial:
- rv = Init();
- break;
- case State::CreatingManager:
- rv = CreateManager();
- break;
- case State::RegisteringObserver:
- rv = RegisterObserver();
- break;
- case State::CallingCallbacks:
- CallCallbacks();
- rv = NS_OK;
- break;
- case State::Completed:
- default:
- MOZ_CRASH("Bad state!");
- }
- nsCOMPtr<nsIEventTarget> thread;
- if (NS_WARN_IF(NS_FAILED(rv))) {
- if (NS_SUCCEEDED(mResultCode)) {
- mResultCode = rv;
- }
- mState = State::CallingCallbacks;
- thread = mOwningThread;
- } else {
- mState = GetNextState(thread);
- }
- if (thread) {
- MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(this, NS_DISPATCH_NORMAL));
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- QuotaManager::
- ShutdownRunnable::Run()
- {
- if (NS_IsMainThread()) {
- mDone = true;
- return NS_OK;
- }
- AssertIsOnBackgroundThread();
- RefPtr<QuotaManager> quotaManager = gInstance.get();
- if (quotaManager) {
- quotaManager->Shutdown();
- gInstance = nullptr;
- }
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
- return NS_OK;
- }
- NS_IMPL_ISUPPORTS(QuotaManager::ShutdownObserver, nsIObserver)
- NS_IMETHODIMP
- QuotaManager::
- ShutdownObserver::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID));
- MOZ_ASSERT(gInstance);
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (NS_WARN_IF(!observerService)) {
- return NS_ERROR_FAILURE;
- }
- // Unregister ourselves from the observer service first to make sure the
- // nested event loop below will not cause re-entrancy issues.
- Unused <<
- observerService->RemoveObserver(this,
- PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID);
- QuotaManagerService* qms = QuotaManagerService::Get();
- MOZ_ASSERT(qms);
- qms->NoteShuttingDownManager();
- for (RefPtr<Client>& client : gInstance->mClients) {
- client->WillShutdown();
- }
- bool done = false;
- RefPtr<ShutdownRunnable> shutdownRunnable = new ShutdownRunnable(done);
- MOZ_ALWAYS_SUCCEEDS(
- mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
- nsIThread* currentThread = NS_GetCurrentThread();
- MOZ_ASSERT(currentThread);
- while (!done) {
- MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
- }
- return NS_OK;
- }
- /*******************************************************************************
- * Quota object
- ******************************************************************************/
- void
- QuotaObject::AddRef()
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- if (!quotaManager) {
- NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
- ++mRefCnt;
- return;
- }
- MutexAutoLock lock(quotaManager->mQuotaMutex);
- ++mRefCnt;
- }
- void
- QuotaObject::Release()
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- if (!quotaManager) {
- NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
- nsrefcnt count = --mRefCnt;
- if (count == 0) {
- mRefCnt = 1;
- delete this;
- }
- return;
- }
- {
- MutexAutoLock lock(quotaManager->mQuotaMutex);
- --mRefCnt;
- if (mRefCnt > 0) {
- return;
- }
- if (mOriginInfo) {
- mOriginInfo->mQuotaObjects.Remove(mPath);
- }
- }
- delete this;
- }
- bool
- QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate)
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- MutexAutoLock lock(quotaManager->mQuotaMutex);
- if (mQuotaCheckDisabled) {
- return true;
- }
- if (mSize == aSize) {
- return true;
- }
- if (!mOriginInfo) {
- mSize = aSize;
- return true;
- }
- GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
- MOZ_ASSERT(groupInfo);
- if (mSize > aSize) {
- if (aTruncate) {
- const int64_t delta = mSize - aSize;
- AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta);
- quotaManager->mTemporaryStorageUsage -= delta;
- AssertNoUnderflow(groupInfo->mUsage, delta);
- groupInfo->mUsage -= delta;
- AssertNoUnderflow(mOriginInfo->mUsage, delta);
- mOriginInfo->mUsage -= delta;
- mSize = aSize;
- }
- return true;
- }
- MOZ_ASSERT(mSize < aSize);
- RefPtr<GroupInfo> complementaryGroupInfo =
- groupInfo->mGroupInfoPair->LockedGetGroupInfo(
- ComplementaryPersistenceType(groupInfo->mPersistenceType));
- uint64_t delta = aSize - mSize;
- AssertNoOverflow(mOriginInfo->mUsage, delta);
- uint64_t newUsage = mOriginInfo->mUsage + delta;
- // Temporary storage has no limit for origin usage (there's a group and the
- // global limit though).
- AssertNoOverflow(groupInfo->mUsage, delta);
- uint64_t newGroupUsage = groupInfo->mUsage + delta;
- uint64_t groupUsage = groupInfo->mUsage;
- if (complementaryGroupInfo) {
- AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
- groupUsage += complementaryGroupInfo->mUsage;
- }
- // Temporary storage has a hard limit for group usage (20 % of the global
- // limit).
- AssertNoOverflow(groupUsage, delta);
- if (groupUsage + delta > quotaManager->GetGroupLimit()) {
- return false;
- }
- AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
- uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage +
- delta;
- if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
- // This will block the thread without holding the lock while waitting.
- AutoTArray<RefPtr<DirectoryLockImpl>, 10> locks;
- uint64_t sizeToBeFreed =
- quotaManager->LockedCollectOriginsForEviction(delta, locks);
- if (!sizeToBeFreed) {
- return false;
- }
- NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
- {
- MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
- for (RefPtr<DirectoryLockImpl>& lock : locks) {
- MOZ_ASSERT(!lock->GetPersistenceType().IsNull());
- MOZ_ASSERT(lock->GetOriginScope().IsOrigin());
- MOZ_ASSERT(!lock->GetOriginScope().GetOrigin().IsEmpty());
- quotaManager->DeleteFilesForOrigin(lock->GetPersistenceType().Value(),
- lock->GetOriginScope().GetOrigin());
- }
- }
- // Relocked.
- NS_ASSERTION(mOriginInfo, "How come?!");
- for (DirectoryLockImpl* lock : locks) {
- MOZ_ASSERT(!lock->GetPersistenceType().IsNull());
- MOZ_ASSERT(!lock->GetGroup().IsEmpty());
- MOZ_ASSERT(lock->GetOriginScope().IsOrigin());
- MOZ_ASSERT(!lock->GetOriginScope().GetOrigin().IsEmpty());
- MOZ_ASSERT(lock->GetOriginScope().GetOrigin() != mOriginInfo->mOrigin,
- "Deleted itself!");
- quotaManager->LockedRemoveQuotaForOrigin(
- lock->GetPersistenceType().Value(),
- lock->GetGroup(),
- lock->GetOriginScope().GetOrigin());
- }
- // We unlocked and relocked several times so we need to recompute all the
- // essential variables and recheck the group limit.
- AssertNoUnderflow(aSize, mSize);
- delta = aSize - mSize;
- AssertNoOverflow(mOriginInfo->mUsage, delta);
- newUsage = mOriginInfo->mUsage + delta;
- AssertNoOverflow(groupInfo->mUsage, delta);
- newGroupUsage = groupInfo->mUsage + delta;
- groupUsage = groupInfo->mUsage;
- if (complementaryGroupInfo) {
- AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
- groupUsage += complementaryGroupInfo->mUsage;
- }
- AssertNoOverflow(groupUsage, delta);
- if (groupUsage + delta > quotaManager->GetGroupLimit()) {
- // Unfortunately some other thread increased the group usage in the
- // meantime and we are not below the group limit anymore.
- // However, the origin eviction must be finalized in this case too.
- MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
- quotaManager->FinalizeOriginEviction(locks);
- return false;
- }
- AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
- newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
- NS_ASSERTION(newTemporaryStorageUsage <=
- quotaManager->mTemporaryStorageLimit, "How come?!");
- // Ok, we successfully freed enough space and the operation can continue
- // without throwing the quota error.
- mOriginInfo->mUsage = newUsage;
- groupInfo->mUsage = newGroupUsage;
- quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;;
- // Some other thread could increase the size in the meantime, but no more
- // than this one.
- MOZ_ASSERT(mSize < aSize);
- mSize = aSize;
- // Finally, release IO thread only objects and allow next synchronized
- // ops for the evicted origins.
- MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
- quotaManager->FinalizeOriginEviction(locks);
- return true;
- }
- mOriginInfo->mUsage = newUsage;
- groupInfo->mUsage = newGroupUsage;
- quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
- mSize = aSize;
- return true;
- }
- void
- QuotaObject::DisableQuotaCheck()
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- MutexAutoLock lock(quotaManager->mQuotaMutex);
- mQuotaCheckDisabled = true;
- }
- void
- QuotaObject::EnableQuotaCheck()
- {
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- MutexAutoLock lock(quotaManager->mQuotaMutex);
- mQuotaCheckDisabled = false;
- }
- /*******************************************************************************
- * Quota manager
- ******************************************************************************/
- QuotaManager::QuotaManager()
- : mQuotaMutex("QuotaManager.mQuotaMutex"),
- mTemporaryStorageLimit(0),
- mTemporaryStorageUsage(0),
- mTemporaryStorageInitialized(false),
- mStorageInitialized(false)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(!gInstance);
- }
- QuotaManager::~QuotaManager()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(!gInstance || gInstance == this);
- }
- void
- QuotaManager::GetOrCreate(nsIRunnable* aCallback)
- {
- AssertIsOnBackgroundThread();
- if (IsShuttingDown()) {
- MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
- return;
- }
- if (gInstance || gCreateFailed) {
- MOZ_ASSERT(!gCreateRunnable);
- MOZ_ASSERT_IF(gCreateFailed, !gInstance);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(aCallback));
- } else {
- if (!gCreateRunnable) {
- gCreateRunnable = new CreateRunnable();
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(gCreateRunnable));
- }
- gCreateRunnable->AddCallback(aCallback);
- }
- }
- // static
- QuotaManager*
- QuotaManager::Get()
- {
- // Does not return an owning reference.
- return gInstance;
- }
- // static
- bool
- QuotaManager::IsShuttingDown()
- {
- return gShutdown;
- }
- auto
- QuotaManager::CreateDirectoryLock(Nullable<PersistenceType> aPersistenceType,
- const nsACString& aGroup,
- const OriginScope& aOriginScope,
- Nullable<bool> aIsApp,
- Nullable<Client::Type> aClientType,
- bool aExclusive,
- bool aInternal,
- OpenDirectoryListener* aOpenListener)
- -> already_AddRefed<DirectoryLockImpl>
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
- MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
- MOZ_ASSERT_IF(!aInternal,
- aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
- MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
- MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
- MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
- MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
- MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
- MOZ_ASSERT_IF(!aInternal, aOpenListener);
- RefPtr<DirectoryLockImpl> lock = new DirectoryLockImpl(this,
- aPersistenceType,
- aGroup,
- aOriginScope,
- aIsApp,
- aClientType,
- aExclusive,
- aInternal,
- aOpenListener);
- mPendingDirectoryLocks.AppendElement(lock);
- // See if this lock needs to wait.
- bool blocked = false;
- for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) {
- DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1];
- if (lock->MustWaitFor(*existingLock)) {
- existingLock->AddBlockingLock(lock);
- lock->AddBlockedOnLock(existingLock);
- blocked = true;
- }
- }
- RegisterDirectoryLock(lock);
- // Otherwise, notify the open listener immediately.
- if (!blocked) {
- lock->NotifyOpenListener();
- }
- return lock.forget();
- }
- auto
- QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp)
- -> already_AddRefed<DirectoryLockImpl>
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
- MOZ_ASSERT(!aOrigin.IsEmpty());
- RefPtr<DirectoryLockImpl> lock =
- new DirectoryLockImpl(this,
- Nullable<PersistenceType>(aPersistenceType),
- aGroup,
- OriginScope::FromOrigin(aOrigin),
- Nullable<bool>(aIsApp),
- Nullable<Client::Type>(),
- /* aExclusive */ true,
- /* aInternal */ true,
- nullptr);
- #ifdef DEBUG
- for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) {
- DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1];
- MOZ_ASSERT(!lock->MustWaitFor(*existingLock));
- }
- #endif
- RegisterDirectoryLock(lock);
- return lock.forget();
- }
- void
- QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aLock);
- mDirectoryLocks.AppendElement(aLock);
- if (aLock->ShouldUpdateLockTable()) {
- const Nullable<PersistenceType>& persistenceType =
- aLock->GetPersistenceType();
- const OriginScope& originScope = aLock->GetOriginScope();
- MOZ_ASSERT(!persistenceType.IsNull());
- MOZ_ASSERT(!aLock->GetGroup().IsEmpty());
- MOZ_ASSERT(originScope.IsOrigin());
- MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
- DirectoryLockTable& directoryLockTable =
- GetDirectoryLockTable(persistenceType.Value());
- nsTArray<DirectoryLockImpl*>* array;
- if (!directoryLockTable.Get(originScope.GetOrigin(), &array)) {
- array = new nsTArray<DirectoryLockImpl*>();
- directoryLockTable.Put(originScope.GetOrigin(), array);
- if (!IsShuttingDown()) {
- UpdateOriginAccessTime(persistenceType.Value(),
- aLock->GetGroup(),
- originScope.GetOrigin());
- }
- }
- array->AppendElement(aLock);
- }
- }
- void
- QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock));
- if (aLock->ShouldUpdateLockTable()) {
- const Nullable<PersistenceType>& persistenceType =
- aLock->GetPersistenceType();
- const OriginScope& originScope = aLock->GetOriginScope();
- MOZ_ASSERT(!persistenceType.IsNull());
- MOZ_ASSERT(!aLock->GetGroup().IsEmpty());
- MOZ_ASSERT(originScope.IsOrigin());
- MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
- DirectoryLockTable& directoryLockTable =
- GetDirectoryLockTable(persistenceType.Value());
- nsTArray<DirectoryLockImpl*>* array;
- MOZ_ALWAYS_TRUE(directoryLockTable.Get(originScope.GetOrigin(), &array));
- MOZ_ALWAYS_TRUE(array->RemoveElement(aLock));
- if (array->IsEmpty()) {
- directoryLockTable.Remove(originScope.GetOrigin());
- if (!IsShuttingDown()) {
- UpdateOriginAccessTime(persistenceType.Value(),
- aLock->GetGroup(),
- originScope.GetOrigin());
- }
- }
- }
- }
- void
- QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl* aLock)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aLock);
- MOZ_ALWAYS_TRUE(mPendingDirectoryLocks.RemoveElement(aLock));
- }
- uint64_t
- QuotaManager::CollectOriginsForEviction(
- uint64_t aMinSizeToBeFreed,
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aLocks.IsEmpty());
- struct MOZ_STACK_CLASS Helper final
- {
- static void
- GetInactiveOriginInfos(nsTArray<RefPtr<OriginInfo>>& aOriginInfos,
- nsTArray<DirectoryLockImpl*>& aLocks,
- nsTArray<OriginInfo*>& aInactiveOriginInfos)
- {
- for (OriginInfo* originInfo : aOriginInfos) {
- MOZ_ASSERT(IsTreatedAsTemporary(originInfo->mGroupInfo->mPersistenceType,
- originInfo->mIsApp));
- OriginScope originScope = OriginScope::FromOrigin(originInfo->mOrigin);
- bool match = false;
- for (uint32_t j = aLocks.Length(); j > 0; j--) {
- DirectoryLockImpl* lock = aLocks[j - 1];
- if (originScope.Matches(lock->GetOriginScope())) {
- match = true;
- break;
- }
- }
- if (!match) {
- MOZ_ASSERT(!originInfo->mQuotaObjects.Count(),
- "Inactive origin shouldn't have open files!");
- aInactiveOriginInfos.InsertElementSorted(originInfo,
- OriginInfoLRUComparator());
- }
- }
- }
- };
- // Split locks into separate arrays and filter out locks for persistent
- // storage, they can't block us.
- nsTArray<DirectoryLockImpl*> temporaryStorageLocks;
- nsTArray<DirectoryLockImpl*> defaultStorageLocks;
- for (DirectoryLockImpl* lock : mDirectoryLocks) {
- const Nullable<PersistenceType>& persistenceType =
- lock->GetPersistenceType();
- if (persistenceType.IsNull()) {
- temporaryStorageLocks.AppendElement(lock);
- defaultStorageLocks.AppendElement(lock);
- } else if (persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
- temporaryStorageLocks.AppendElement(lock);
- } else if (persistenceType.Value() == PERSISTENCE_TYPE_DEFAULT) {
- defaultStorageLocks.AppendElement(lock);
- } else {
- MOZ_ASSERT(persistenceType.Value() == PERSISTENCE_TYPE_PERSISTENT);
- // Do nothing here, persistent origins don't need to be collected ever.
- }
- }
- nsTArray<OriginInfo*> inactiveOrigins;
- // Enumerate and process inactive origins. This must be protected by the
- // mutex.
- MutexAutoLock lock(mQuotaMutex);
- for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
- GroupInfoPair* pair = iter.UserData();
- MOZ_ASSERT(!iter.Key().IsEmpty());
- MOZ_ASSERT(pair);
- RefPtr<GroupInfo> groupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
- if (groupInfo) {
- Helper::GetInactiveOriginInfos(groupInfo->mOriginInfos,
- temporaryStorageLocks,
- inactiveOrigins);
- }
- groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
- if (groupInfo) {
- Helper::GetInactiveOriginInfos(groupInfo->mOriginInfos,
- defaultStorageLocks,
- inactiveOrigins);
- }
- }
- #ifdef DEBUG
- // Make sure the array is sorted correctly.
- for (uint32_t index = inactiveOrigins.Length(); index > 1; index--) {
- MOZ_ASSERT(inactiveOrigins[index - 1]->mAccessTime >=
- inactiveOrigins[index - 2]->mAccessTime);
- }
- #endif
- // Create a list of inactive and the least recently used origins
- // whose aggregate size is greater or equals the minimal size to be freed.
- uint64_t sizeToBeFreed = 0;
- for(uint32_t count = inactiveOrigins.Length(), index = 0;
- index < count;
- index++) {
- if (sizeToBeFreed >= aMinSizeToBeFreed) {
- inactiveOrigins.TruncateLength(index);
- break;
- }
- sizeToBeFreed += inactiveOrigins[index]->mUsage;
- }
- if (sizeToBeFreed >= aMinSizeToBeFreed) {
- // Success, add directory locks for these origins, so any other
- // operations for them will be delayed (until origin eviction is finalized).
- for (OriginInfo* originInfo : inactiveOrigins) {
- RefPtr<DirectoryLockImpl> lock =
- CreateDirectoryLockForEviction(originInfo->mGroupInfo->mPersistenceType,
- originInfo->mGroupInfo->mGroup,
- originInfo->mOrigin,
- originInfo->mIsApp);
- aLocks.AppendElement(lock.forget());
- }
- return sizeToBeFreed;
- }
- return 0;
- }
- nsresult
- QuotaManager::Init(const nsAString& aBasePath)
- {
- nsresult rv;
- mBasePath = aBasePath;
- nsCOMPtr<nsIFile> baseDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = baseDir->InitWithPath(aBasePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CloneStoragePath(baseDir,
- NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME),
- mIndexedDBPath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = baseDir->GetPath(mStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CloneStoragePath(baseDir,
- NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME),
- mPermanentStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CloneStoragePath(baseDir,
- NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME),
- mTemporaryStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CloneStoragePath(baseDir,
- NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME),
- mDefaultStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Make a lazy thread for any IO we need (like clearing or enumerating the
- // contents of storage directories).
- mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
- NS_LITERAL_CSTRING("Storage I/O"),
- LazyIdleThread::ManualShutdown);
- // Make a timer here to avoid potential failures later. We don't actually
- // initialize the timer until shutdown.
- mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
- if (NS_WARN_IF(!mShutdownTimer)) {
- return NS_ERROR_FAILURE;
- }
- static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::DOMCACHE == 2 &&
- Client::TYPE_MAX == 3, "Fix the registration!");
- MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
- "Should be using an auto array with correct capacity!");
- // Register clients.
- mClients.AppendElement(indexedDB::CreateQuotaClient());
- mClients.AppendElement(asmjscache::CreateClient());
- mClients.AppendElement(cache::CreateQuotaClient());
- return NS_OK;
- }
- void
- QuotaManager::Shutdown()
- {
- AssertIsOnOwningThread();
- // Setting this flag prevents the service from being recreated and prevents
- // further storagess from being created.
- if (gShutdown.exchange(true)) {
- NS_ERROR("Shutdown more than once?!");
- }
- StopIdleMaintenance();
- // Kick off the shutdown timer.
- MOZ_ALWAYS_SUCCEEDS(
- mShutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback,
- this,
- DEFAULT_SHUTDOWN_TIMER_MS,
- nsITimer::TYPE_ONE_SHOT));
- // Each client will spin the event loop while we wait on all the threads
- // to close. Our timer may fire during that loop.
- for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
- mClients[index]->ShutdownWorkThreads();
- }
- // Cancel the timer regardless of whether it actually fired.
- if (NS_FAILED(mShutdownTimer->Cancel())) {
- NS_WARNING("Failed to cancel shutdown timer!");
- }
- // NB: It's very important that runnable is destroyed on this thread
- // (i.e. after we join the IO thread) because we can't release the
- // QuotaManager on the IO thread. This should probably use
- // NewNonOwningRunnableMethod ...
- RefPtr<Runnable> runnable =
- NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
- MOZ_ASSERT(runnable);
- // Give clients a chance to cleanup IO thread only objects.
- if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
- NS_WARNING("Failed to dispatch runnable!");
- }
- // Make sure to join with our IO thread.
- if (NS_FAILED(mIOThread->Shutdown())) {
- NS_WARNING("Failed to shutdown IO thread!");
- }
- for (RefPtr<DirectoryLockImpl>& lock : mPendingDirectoryLocks) {
- lock->Invalidate();
- }
- }
- void
- QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- uint64_t aUsageBytes,
- int64_t aAccessTime)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(IsTreatedAsTemporary(aPersistenceType, aIsApp));
- MutexAutoLock lock(mQuotaMutex);
- GroupInfoPair* pair;
- if (!mGroupInfoPairs.Get(aGroup, &pair)) {
- pair = new GroupInfoPair();
- mGroupInfoPairs.Put(aGroup, pair);
- // The hashtable is now responsible to delete the GroupInfoPair.
- }
- RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
- if (!groupInfo) {
- groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
- pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
- }
- RefPtr<OriginInfo> originInfo =
- new OriginInfo(groupInfo, aOrigin, aIsApp, aUsageBytes, aAccessTime);
- groupInfo->LockedAddOriginInfo(originInfo);
- }
- void
- QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- int64_t aSize)
- {
- MOZ_ASSERT(!NS_IsMainThread());
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- MutexAutoLock lock(mQuotaMutex);
- GroupInfoPair* pair;
- if (!mGroupInfoPairs.Get(aGroup, &pair)) {
- return;
- }
- RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
- if (!groupInfo) {
- return;
- }
- RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
- if (originInfo) {
- originInfo->LockedDecreaseUsage(aSize);
- }
- }
- void
- QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- MutexAutoLock lock(mQuotaMutex);
- GroupInfoPair* pair;
- if (!mGroupInfoPairs.Get(aGroup, &pair)) {
- return;
- }
- RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
- if (!groupInfo) {
- return;
- }
- RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
- if (originInfo) {
- int64_t timestamp = PR_Now();
- originInfo->LockedUpdateAccessTime(timestamp);
- MutexAutoUnlock autoUnlock(mQuotaMutex);
- RefPtr<SaveOriginAccessTimeOp> op =
- new SaveOriginAccessTimeOp(aPersistenceType, aOrigin, timestamp);
- op->RunImmediately();
- }
- }
- void
- QuotaManager::RemoveQuota()
- {
- MutexAutoLock lock(mQuotaMutex);
- for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
- nsAutoPtr<GroupInfoPair>& pair = iter.Data();
- MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
- MOZ_ASSERT(pair, "Null pointer!");
- RefPtr<GroupInfo> groupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
- if (groupInfo) {
- groupInfo->LockedRemoveOriginInfos();
- }
- groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
- if (groupInfo) {
- groupInfo->LockedRemoveOriginInfos();
- }
- iter.Remove();
- }
- NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
- }
- already_AddRefed<QuotaObject>
- QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- nsIFile* aFile)
- {
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
- return nullptr;
- }
- nsString path;
- nsresult rv = aFile->GetPath(path);
- NS_ENSURE_SUCCESS(rv, nullptr);
- int64_t fileSize;
- bool exists;
- rv = aFile->Exists(&exists);
- NS_ENSURE_SUCCESS(rv, nullptr);
- if (exists) {
- rv = aFile->GetFileSize(&fileSize);
- NS_ENSURE_SUCCESS(rv, nullptr);
- }
- else {
- fileSize = 0;
- }
- // Re-escape our parameters above to make sure we get the right quota group.
- nsAutoCString group;
- rv = NS_EscapeURL(aGroup, esc_Query, group, fallible);
- NS_ENSURE_SUCCESS(rv, nullptr);
- nsAutoCString origin;
- rv = NS_EscapeURL(aOrigin, esc_Query, origin, fallible);
- NS_ENSURE_SUCCESS(rv, nullptr);
- RefPtr<QuotaObject> result;
- {
- MutexAutoLock lock(mQuotaMutex);
- GroupInfoPair* pair;
- if (!mGroupInfoPairs.Get(group, &pair)) {
- return nullptr;
- }
- RefPtr<GroupInfo> groupInfo =
- pair->LockedGetGroupInfo(aPersistenceType);
- if (!groupInfo) {
- return nullptr;
- }
- RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(origin);
- if (!originInfo) {
- return nullptr;
- }
- // We need this extra raw pointer because we can't assign to the smart
- // pointer directly since QuotaObject::AddRef would try to acquire the same
- // mutex.
- QuotaObject* quotaObject;
- if (!originInfo->mQuotaObjects.Get(path, "aObject)) {
- // Create a new QuotaObject.
- quotaObject = new QuotaObject(originInfo, path, fileSize);
- // Put it to the hashtable. The hashtable is not responsible to delete
- // the QuotaObject.
- originInfo->mQuotaObjects.Put(path, quotaObject);
- }
- // Addref the QuotaObject and move the ownership to the result. This must
- // happen before we unlock!
- result = quotaObject->LockedAddRef();
- }
- // The caller becomes the owner of the QuotaObject, that is, the caller is
- // is responsible to delete it when the last reference is removed.
- return result.forget();
- }
- already_AddRefed<QuotaObject>
- QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- const nsAString& aPath)
- {
- nsresult rv;
- nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, nullptr);
- rv = file->InitWithPath(aPath);
- NS_ENSURE_SUCCESS(rv, nullptr);
- return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
- }
- void
- QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId)
- {
- AssertIsOnOwningThread();
- for (RefPtr<Client>& client : mClients) {
- client->AbortOperationsForProcess(aContentParentId);
- }
- }
- nsresult
- QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
- const nsACString& aASCIIOrigin,
- nsIFile** aDirectory) const
- {
- nsresult rv;
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString originSanitized(aASCIIOrigin);
- SanitizeOriginString(originSanitized);
- rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
- NS_ENSURE_SUCCESS(rv, rv);
- directory.forget(aDirectory);
- return NS_OK;
- }
- nsresult
- QuotaManager::RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(mStorageInitialized);
- RefPtr<RestoreDirectoryMetadata2Helper> helper =
- new RestoreDirectoryMetadata2Helper(aDirectory, aPersistent);
- nsresult rv = helper->RestoreMetadata2File();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
- int64_t* aTimestamp,
- nsACString& aSuffix,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aIsApp)
- {
- MOZ_ASSERT(!NS_IsMainThread());
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aTimestamp);
- MOZ_ASSERT(aIsApp);
- MOZ_ASSERT(mStorageInitialized);
- nsCOMPtr<nsIBinaryInputStream> binaryStream;
- nsresult rv = GetBinaryInputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
- getter_AddRefs(binaryStream));
- NS_ENSURE_SUCCESS(rv, rv);
- uint64_t timestamp;
- rv = binaryStream->Read64(×tamp);
- NS_ENSURE_SUCCESS(rv, rv);
- bool persisted;
- rv = binaryStream->ReadBoolean(&persisted);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint32_t reservedData1;
- rv = binaryStream->Read32(&reservedData1);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint32_t reservedData2;
- rv = binaryStream->Read32(&reservedData2);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString suffix;
- rv = binaryStream->ReadCString(suffix);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString group;
- rv = binaryStream->ReadCString(group);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCString origin;
- rv = binaryStream->ReadCString(origin);
- NS_ENSURE_SUCCESS(rv, rv);
- bool isApp;
- rv = binaryStream->ReadBoolean(&isApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aTimestamp = timestamp;
- aSuffix = suffix;
- aGroup = group;
- aOrigin = origin;
- *aIsApp = isApp;
- return NS_OK;
- }
- nsresult
- QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
- bool aPersistent,
- int64_t* aTimestamp,
- nsACString& aSuffix,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aIsApp)
- {
- nsresult rv = GetDirectoryMetadata2(aDirectory,
- aTimestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = GetDirectoryMetadata2(aDirectory,
- aTimestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aTimestamp);
- MOZ_ASSERT(mStorageInitialized);
- nsCOMPtr<nsIBinaryInputStream> binaryStream;
- nsresult rv = GetBinaryInputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
- getter_AddRefs(binaryStream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint64_t timestamp;
- rv = binaryStream->Read64(×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aTimestamp = timestamp;
- return NS_OK;
- }
- nsresult
- QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
- bool aPersistent,
- int64_t* aTimestamp)
- {
- nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
- {
- MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY ||
- aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
- nsresult rv;
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool created;
- rv = EnsureDirectory(directory, &created);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry);
- MOZ_ASSERT(childDirectory);
- bool isDirectory;
- rv = childDirectory->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- nsString leafName;
- rv = childDirectory->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- continue;
- }
- QM_WARNING("Something (%s) in the repository that doesn't belong!",
- NS_ConvertUTF16toUTF8(leafName).get());
- return NS_ERROR_UNEXPECTED;
- }
- int64_t timestamp;
- nsCString suffix;
- nsCString group;
- nsCString origin;
- bool isApp;
- rv = GetDirectoryMetadata2WithRestore(childDirectory,
- /* aPersistent */ false,
- ×tamp,
- suffix,
- group,
- origin,
- &isApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (IsTreatedAsPersistent(aPersistenceType, isApp)) {
- continue;
- }
- rv = InitializeOrigin(aPersistenceType, group, origin, isApp, timestamp,
- childDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- namespace {
- // The Cache API was creating top level morgue directories by accident for
- // a short time in nightly. This unfortunately prevents all storage from
- // working. So recover these profiles by removing these corrupt directories.
- // This should be removed at some point in the future.
- bool
- MaybeRemoveCorruptDirectory(const nsAString& aLeafName, nsIFile* aDir)
- {
- #ifdef NIGHTLY_BUILD
- MOZ_ASSERT(aDir);
- if (aLeafName != NS_LITERAL_STRING("morgue")) {
- return false;
- }
- NS_WARNING("QuotaManager removing corrupt morgue directory!");
- nsresult rv = aDir->Remove(true /* recursive */);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return false;
- }
- return true;
- #else
- return false;
- #endif // NIGHTLY_BUILD
- }
- } // namespace
- nsresult
- QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- int64_t aAccessTime,
- nsIFile* aDirectory)
- {
- AssertIsOnIOThread();
- nsresult rv;
- bool trackQuota = IsQuotaEnforced(aPersistenceType, aOrigin, aIsApp);
- // We need to initialize directories of all clients if they exists and also
- // get the total usage to initialize the quota.
- nsAutoPtr<UsageInfo> usageInfo;
- if (trackQuota) {
- usageInfo = new UsageInfo();
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- NS_ENSURE_TRUE(file, NS_NOINTERFACE);
- nsString leafName;
- rv = file->GetLeafName(leafName);
- NS_ENSURE_SUCCESS(rv, rv);
- if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
- leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
- leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- continue;
- }
- bool isDirectory;
- rv = file->IsDirectory(&isDirectory);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!isDirectory) {
- NS_WARNING("Unknown file found!");
- return NS_ERROR_UNEXPECTED;
- }
- if (MaybeRemoveCorruptDirectory(leafName, file)) {
- continue;
- }
- Client::Type clientType;
- rv = Client::TypeFromText(leafName, clientType);
- if (NS_FAILED(rv)) {
- NS_WARNING("Unknown directory found!");
- return NS_ERROR_UNEXPECTED;
- }
- Atomic<bool> dummy(false);
- rv = mClients[clientType]->InitOrigin(aPersistenceType,
- aGroup,
- aOrigin,
- /* aCanceled */ dummy,
- usageInfo);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (trackQuota) {
- InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, aIsApp,
- usageInfo->TotalUsage(), aAccessTime);
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::MaybeUpgradeIndexedDBDirectory()
- {
- AssertIsOnIOThread();
- nsresult rv;
- nsCOMPtr<nsIFile> indexedDBDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = indexedDBDir->InitWithPath(mIndexedDBPath);
- NS_ENSURE_SUCCESS(rv, rv);
- bool exists;
- rv = indexedDBDir->Exists(&exists);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!exists) {
- // Nothing to upgrade.
- return NS_OK;
- }
- bool isDirectory;
- rv = indexedDBDir->IsDirectory(&isDirectory);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!isDirectory) {
- NS_WARNING("indexedDB entry is not a directory!");
- return NS_OK;
- }
- nsCOMPtr<nsIFile> persistentStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = persistentStorageDir->InitWithPath(mStoragePath);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = persistentStorageDir->Exists(&exists);
- NS_ENSURE_SUCCESS(rv, rv);
- if (exists) {
- NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
- return NS_OK;
- }
- nsCOMPtr<nsIFile> storageDir;
- rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
- NS_ENSURE_SUCCESS(rv, rv);
- // MoveTo() is atomic if the move happens on the same volume which should
- // be our case, so even if we crash in the middle of the operation nothing
- // breaks next time we try to initialize.
- // However there's a theoretical possibility that the indexedDB directory
- // is on different volume, but it should be rare enough that we don't have
- // to worry about it.
- rv = indexedDBDir->MoveTo(storageDir, NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- QuotaManager::MaybeUpgradePersistentStorageDirectory()
- {
- AssertIsOnIOThread();
- nsresult rv;
- nsCOMPtr<nsIFile> persistentStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = persistentStorageDir->InitWithPath(mStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = persistentStorageDir->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- // Nothing to upgrade.
- return NS_OK;
- }
- bool isDirectory;
- rv = persistentStorageDir->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- NS_WARNING("persistent entry is not a directory!");
- return NS_OK;
- }
- nsCOMPtr<nsIFile> defaultStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = defaultStorageDir->InitWithPath(mDefaultStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = defaultStorageDir->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (exists) {
- NS_WARNING("storage/persistent shouldn't exist after the upgrade!");
- return NS_OK;
- }
- // Create real metadata files for origin directories in persistent storage.
- RefPtr<CreateOrUpgradeDirectoryMetadataHelper> helper =
- new CreateOrUpgradeDirectoryMetadataHelper(persistentStorageDir,
- /* aPersistent */ true);
- rv = helper->CreateOrUpgradeMetadataFiles();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Upgrade metadata files for origin directories in temporary storage.
- nsCOMPtr<nsIFile> temporaryStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = temporaryStorageDir->InitWithPath(mTemporaryStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = temporaryStorageDir->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (exists) {
- rv = temporaryStorageDir->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- NS_WARNING("temporary entry is not a directory!");
- return NS_OK;
- }
- helper =
- new CreateOrUpgradeDirectoryMetadataHelper(temporaryStorageDir,
- /* aPersistent */ false);
- rv = helper->CreateOrUpgradeMetadataFiles();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- // And finally rename persistent to default.
- rv = persistentStorageDir->RenameTo(nullptr, NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::MaybeRemoveOldDirectories()
- {
- AssertIsOnIOThread();
- nsresult rv;
- nsCOMPtr<nsIFile> indexedDBDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = indexedDBDir->InitWithPath(mIndexedDBPath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = indexedDBDir->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (exists) {
- QM_WARNING("Deleting old <profile>/indexedDB directory!");
- rv = indexedDBDir->Remove(/* aRecursive */ true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsCOMPtr<nsIFile> persistentStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = persistentStorageDir->InitWithPath(mStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = persistentStorageDir->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (exists) {
- QM_WARNING("Deleting old <profile>/storage/persistent directory!");
- rv = persistentStorageDir->Remove(/* aRecursive */ true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- QuotaManager::UpgradeStorageFrom0ToCurrent(mozIStorageConnection* aConnection)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aConnection);
- nsresult rv;
- for (const PersistenceType persistenceType : kAllPersistenceTypes) {
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = directory->InitWithPath(GetStoragePath(persistenceType));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
- RefPtr<UpgradeDirectoryMetadataFrom1To2Helper> helper =
- new UpgradeDirectoryMetadataFrom1To2Helper(directory, persistent);
- rv = helper->UpgradeMetadataFiles();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- #ifdef DEBUG
- {
- int32_t storageVersion;
- rv = aConnection->GetSchemaVersion(&storageVersion);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(storageVersion == 0);
- }
- #endif
- rv = aConnection->SetSchemaVersion(kStorageVersion);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- #if 0
- nsresult
- QuotaManager::UpgradeStorageFrom1To2(mozIStorageConnection* aConnection)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aConnection);
- nsresult rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- #endif
- nsresult
- QuotaManager::EnsureStorageIsInitialized()
- {
- AssertIsOnIOThread();
- if (mStorageInitialized) {
- return NS_OK;
- }
- nsresult rv;
- nsCOMPtr<nsIFile> storageFile =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = storageFile->InitWithPath(mBasePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<mozIStorageService> ss =
- do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<mozIStorageConnection> connection;
- rv = ss->OpenUnsharedDatabase(storageFile, getter_AddRefs(connection));
- if (rv == NS_ERROR_FILE_CORRUPTED) {
- // Nuke the database file.
- rv = storageFile->Remove(false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = ss->OpenUnsharedDatabase(storageFile, getter_AddRefs(connection));
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // We want extra durability for this important file.
- rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "PRAGMA synchronous = EXTRA;"
- ));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Check to make sure that the storage version is correct.
- int32_t storageVersion;
- rv = connection->GetSchemaVersion(&storageVersion);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (GetMajorStorageVersion(storageVersion) > kMajorStorageVersion) {
- NS_WARNING("Unable to initialize storage, version is too high!");
- return NS_ERROR_FAILURE;
- }
- if (storageVersion < kStorageVersion) {
- const bool newDatabase = !storageVersion;
- if (newDatabase) {
- // Set the page size first.
- if (kSQLitePageSizeOverride) {
- rv = connection->ExecuteSimpleSQL(
- nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
- );
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- }
- mozStorageTransaction transaction(connection, false,
- mozIStorageConnection::TRANSACTION_IMMEDIATE);
- if (newDatabase) {
- rv = MaybeUpgradeIndexedDBDirectory();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = MaybeUpgradePersistentStorageDirectory();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = MaybeRemoveOldDirectories();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = UpgradeStorageFrom0ToCurrent(connection);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&storageVersion)));
- MOZ_ASSERT(storageVersion == kStorageVersion);
- } else {
- // This logic needs to change next time we change the storage!
- static_assert(kStorageVersion == int32_t((1 << 16) + 0),
- "Upgrade function needed due to storage version increase.");
- while (storageVersion != kStorageVersion) {
- /* if (storageVersion == MakeStorageVersion(1, 0)) {
- rv = UpgradeStorageFrom1To2(connection);
- } else */ {
- NS_WARNING("Unable to initialize storage, no upgrade path is "
- "available!");
- return NS_ERROR_FAILURE;
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = connection->GetSchemaVersion(&storageVersion);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- MOZ_ASSERT(storageVersion == kStorageVersion);
- }
- rv = transaction.Commit();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- mStorageInitialized = true;
- return NS_OK;
- }
- void
- QuotaManager::OpenDirectory(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- Client::Type aClientType,
- bool aExclusive,
- OpenDirectoryListener* aOpenListener)
- {
- AssertIsOnOwningThread();
- RefPtr<DirectoryLockImpl> lock =
- CreateDirectoryLock(Nullable<PersistenceType>(aPersistenceType),
- aGroup,
- OriginScope::FromOrigin(aOrigin),
- Nullable<bool>(aIsApp),
- Nullable<Client::Type>(aClientType),
- aExclusive,
- false,
- aOpenListener);
- MOZ_ASSERT(lock);
- }
- void
- QuotaManager::OpenDirectoryInternal(Nullable<PersistenceType> aPersistenceType,
- const OriginScope& aOriginScope,
- Nullable<Client::Type> aClientType,
- bool aExclusive,
- OpenDirectoryListener* aOpenListener)
- {
- AssertIsOnOwningThread();
- RefPtr<DirectoryLockImpl> lock =
- CreateDirectoryLock(aPersistenceType,
- EmptyCString(),
- aOriginScope,
- Nullable<bool>(),
- Nullable<Client::Type>(aClientType),
- aExclusive,
- true,
- aOpenListener);
- MOZ_ASSERT(lock);
- if (!aExclusive) {
- return;
- }
- // All the locks that block this new exclusive lock need to be invalidated.
- // We also need to notify clients to abort operations for them.
- AutoTArray<nsAutoPtr<nsTHashtable<nsCStringHashKey>>,
- Client::TYPE_MAX> origins;
- origins.SetLength(Client::TYPE_MAX);
- const nsTArray<DirectoryLockImpl*>& blockedOnLocks =
- lock->GetBlockedOnLocks();
- for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) {
- blockedOnLock->Invalidate();
- if (!blockedOnLock->IsInternal()) {
- MOZ_ASSERT(!blockedOnLock->GetClientType().IsNull());
- Client::Type clientType = blockedOnLock->GetClientType().Value();
- MOZ_ASSERT(clientType < Client::TYPE_MAX);
- const OriginScope& originScope = blockedOnLock->GetOriginScope();
- MOZ_ASSERT(originScope.IsOrigin());
- MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
- nsAutoPtr<nsTHashtable<nsCStringHashKey>>& origin = origins[clientType];
- if (!origin) {
- origin = new nsTHashtable<nsCStringHashKey>();
- }
- origin->PutEntry(originScope.GetOrigin());
- }
- }
- for (uint32_t index : MakeRange(uint32_t(Client::TYPE_MAX))) {
- if (origins[index]) {
- for (auto iter = origins[index]->Iter(); !iter.Done(); iter.Next()) {
- MOZ_ASSERT(mClients[index]);
- mClients[index]->AbortOperations(iter.Get()->GetKey());
- }
- }
- }
- }
- nsresult
- QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
- const nsACString& aSuffix,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- nsIFile** aDirectory)
- {
- AssertIsOnIOThread();
- nsresult rv = EnsureStorageIsInitialized();
- NS_ENSURE_SUCCESS(rv, rv);
- // Get directory for this origin and persistence type.
- nsCOMPtr<nsIFile> directory;
- rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
- getter_AddRefs(directory));
- NS_ENSURE_SUCCESS(rv, rv);
- if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
- if (mInitializedOrigins.Contains(OriginKey(aPersistenceType, aOrigin))) {
- directory.forget(aDirectory);
- return NS_OK;
- }
- } else if (!mTemporaryStorageInitialized) {
- rv = InitializeRepository(aPersistenceType);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- // We have to cleanup partially initialized quota.
- RemoveQuota();
- return rv;
- }
- rv = InitializeRepository(ComplementaryPersistenceType(aPersistenceType));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- // We have to cleanup partially initialized quota.
- RemoveQuota();
- return rv;
- }
- if (gFixedLimitKB >= 0) {
- mTemporaryStorageLimit = static_cast<uint64_t>(gFixedLimitKB) * 1024;
- }
- else {
- nsCOMPtr<nsIFile> storageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = storageDir->InitWithPath(GetStoragePath());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = GetTemporaryStorageLimit(storageDir, mTemporaryStorageUsage,
- &mTemporaryStorageLimit);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- mTemporaryStorageInitialized = true;
- CheckTemporaryStorageLimits();
- }
- int64_t timestamp;
- bool created;
- rv = EnsureDirectory(directory, &created);
- NS_ENSURE_SUCCESS(rv, rv);
- if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
- if (created) {
- timestamp = PR_Now();
- rv = CreateDirectoryMetadata(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CreateDirectoryMetadata2(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
- rv = GetDirectoryMetadata2WithRestore(directory,
- persistent,
- ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(timestamp <= PR_Now());
- }
- rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
- directory);
- NS_ENSURE_SUCCESS(rv, rv);
- mInitializedOrigins.AppendElement(OriginKey(aPersistenceType, aOrigin));
- } else if (created) {
- timestamp = PR_Now();
- rv = CreateDirectoryMetadata(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = CreateDirectoryMetadata2(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin,
- aIsApp);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
- directory);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- directory.forget(aDirectory);
- return NS_OK;
- }
- void
- QuotaManager::OriginClearCompleted(PersistenceType aPersistenceType,
- const nsACString& aOrigin,
- bool aIsApp)
- {
- AssertIsOnIOThread();
- if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
- mInitializedOrigins.RemoveElement(OriginKey(aPersistenceType, aOrigin));
- }
- for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
- mClients[index]->OnOriginClearCompleted(aPersistenceType, aOrigin);
- }
- }
- void
- QuotaManager::ResetOrClearCompleted()
- {
- AssertIsOnIOThread();
- mInitializedOrigins.Clear();
- mTemporaryStorageInitialized = false;
- mStorageInitialized = false;
- ReleaseIOThreadObjects();
- }
- Client*
- QuotaManager::GetClient(Client::Type aClientType)
- {
- MOZ_ASSERT(aClientType >= Client::IDB);
- MOZ_ASSERT(aClientType < Client::TYPE_MAX);
- return mClients.ElementAt(aClientType);
- }
- uint64_t
- QuotaManager::GetGroupLimit() const
- {
- MOZ_ASSERT(mTemporaryStorageInitialized);
- // To avoid one group evicting all the rest, limit the amount any one group
- // can use to 20%. To prevent individual sites from using exorbitant amounts
- // of storage where there is a lot of free space, cap the group limit to 2GB.
- uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
- // In low-storage situations, make an exception (while not exceeding the total
- // storage limit).
- return std::min<uint64_t>(mTemporaryStorageLimit,
- std::max<uint64_t>(x, 10 MB));
- }
- void
- QuotaManager::GetGroupUsageAndLimit(const nsACString& aGroup,
- UsageInfo* aUsageInfo)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aUsageInfo);
- {
- MutexAutoLock lock(mQuotaMutex);
- aUsageInfo->SetLimit(GetGroupLimit());
- aUsageInfo->ResetUsage();
- GroupInfoPair* pair;
- if (!mGroupInfoPairs.Get(aGroup, &pair)) {
- return;
- }
- // Calculate temporary group usage
- RefPtr<GroupInfo> temporaryGroupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
- if (temporaryGroupInfo) {
- aUsageInfo->AppendToDatabaseUsage(temporaryGroupInfo->mUsage);
- }
- // Calculate default group usage
- RefPtr<GroupInfo> defaultGroupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
- if (defaultGroupInfo) {
- aUsageInfo->AppendToDatabaseUsage(defaultGroupInfo->mUsage);
- }
- }
- }
- // static
- void
- QuotaManager::GetStorageId(PersistenceType aPersistenceType,
- const nsACString& aOrigin,
- Client::Type aClientType,
- nsACString& aDatabaseId)
- {
- nsAutoCString str;
- str.AppendInt(aPersistenceType);
- str.Append('*');
- str.Append(aOrigin);
- str.Append('*');
- str.AppendInt(aClientType);
- aDatabaseId = str;
- }
- // static
- nsresult
- QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
- nsACString* aSuffix,
- nsACString* aGroup,
- nsACString* aOrigin,
- bool* aIsApp)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aPrincipal);
- if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
- GetInfoForChrome(aSuffix, aGroup, aOrigin, aIsApp);
- return NS_OK;
- }
- if (aPrincipal->GetIsNullPrincipal()) {
- NS_WARNING("IndexedDB not supported from this principal!");
- return NS_ERROR_FAILURE;
- }
- nsCString origin;
- nsresult rv = aPrincipal->GetOrigin(origin);
- NS_ENSURE_SUCCESS(rv, rv);
- if (origin.EqualsLiteral(kChromeOrigin)) {
- NS_WARNING("Non-chrome principal can't use chrome origin!");
- return NS_ERROR_FAILURE;
- }
- nsCString suffix;
- BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(suffix);
- if (aSuffix)
- {
- aSuffix->Assign(suffix);
- }
- if (aGroup) {
- nsCString baseDomain;
- rv = aPrincipal->GetBaseDomain(baseDomain);
- if (NS_FAILED(rv)) {
- // A hack for JetPack.
- nsCOMPtr<nsIURI> uri;
- rv = aPrincipal->GetURI(getter_AddRefs(uri));
- NS_ENSURE_SUCCESS(rv, rv);
- bool isIndexedDBURI = false;
- rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
- NS_ENSURE_SUCCESS(rv, rv);
- if (isIndexedDBURI) {
- rv = NS_OK;
- }
- }
- NS_ENSURE_SUCCESS(rv, rv);
- if (baseDomain.IsEmpty()) {
- aGroup->Assign(origin);
- } else {
- aGroup->Assign(baseDomain + suffix);
- }
- }
- if (aOrigin) {
- aOrigin->Assign(origin);
- }
- if (aIsApp) {
- *aIsApp = aPrincipal->GetAppStatus() !=
- nsIPrincipal::APP_STATUS_NOT_INSTALLED;
- }
- return NS_OK;
- }
- // static
- nsresult
- QuotaManager::GetInfoFromWindow(nsPIDOMWindowOuter* aWindow,
- nsACString* aSuffix,
- nsACString* aGroup,
- nsACString* aOrigin,
- bool* aIsApp)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aWindow);
- nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
- NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
- nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
- NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
- nsresult rv =
- GetInfoFromPrincipal(principal, aSuffix, aGroup, aOrigin, aIsApp);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- // static
- void
- QuotaManager::GetInfoForChrome(nsACString* aSuffix,
- nsACString* aGroup,
- nsACString* aOrigin,
- bool* aIsApp)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
- if (aSuffix) {
- aSuffix->Assign(EmptyCString());
- }
- if (aGroup) {
- ChromeOrigin(*aGroup);
- }
- if (aOrigin) {
- ChromeOrigin(*aOrigin);
- }
- if (aIsApp) {
- *aIsApp = false;
- }
- }
- // static
- bool
- QuotaManager::IsOriginInternal(const nsACString& aOrigin)
- {
- // The first prompt is not required for these origins.
- if (aOrigin.EqualsLiteral(kChromeOrigin) ||
- StringBeginsWith(aOrigin, nsDependentCString(kAboutHomeOriginPrefix)) ||
- StringBeginsWith(aOrigin, nsDependentCString(kIndexedDBOriginPrefix)) ||
- StringBeginsWith(aOrigin, nsDependentCString(kResourceOriginPrefix))) {
- return true;
- }
- return false;
- }
- // static
- bool
- QuotaManager::IsFirstPromptRequired(PersistenceType aPersistenceType,
- const nsACString& aOrigin,
- bool aIsApp)
- {
- if (IsTreatedAsTemporary(aPersistenceType, aIsApp)) {
- return false;
- }
- return !IsOriginInternal(aOrigin);
- }
- // static
- bool
- QuotaManager::IsQuotaEnforced(PersistenceType aPersistenceType,
- const nsACString& aOrigin,
- bool aIsApp)
- {
- return IsTreatedAsTemporary(aPersistenceType, aIsApp);
- }
- // static
- void
- QuotaManager::ChromeOrigin(nsACString& aOrigin)
- {
- aOrigin.AssignLiteral(kChromeOrigin);
- }
- uint64_t
- QuotaManager::LockedCollectOriginsForEviction(
- uint64_t aMinSizeToBeFreed,
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
- {
- mQuotaMutex.AssertCurrentThreadOwns();
- RefPtr<CollectOriginsHelper> helper =
- new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
- // Unlock while calling out to XPCOM (code behind the dispatch method needs
- // to acquire its own lock which can potentially lead to a deadlock and it
- // also calls an observer that can do various stuff like IO, so it's better
- // to not hold our mutex while that happens).
- {
- MutexAutoUnlock autoUnlock(mQuotaMutex);
- MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(helper, NS_DISPATCH_NORMAL));
- }
- return helper->BlockAndReturnOriginsForEviction(aLocks);
- }
- void
- QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin)
- {
- mQuotaMutex.AssertCurrentThreadOwns();
- MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
- GroupInfoPair* pair;
- mGroupInfoPairs.Get(aGroup, &pair);
- if (!pair) {
- return;
- }
- RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
- if (groupInfo) {
- groupInfo->LockedRemoveOriginInfo(aOrigin);
- if (!groupInfo->LockedHasOriginInfos()) {
- pair->LockedClearGroupInfo(aPersistenceType);
- if (!pair->LockedHasGroupInfos()) {
- mGroupInfoPairs.Remove(aGroup);
- }
- }
- }
- }
- void
- QuotaManager::CheckTemporaryStorageLimits()
- {
- AssertIsOnIOThread();
- nsTArray<OriginInfo*> doomedOriginInfos;
- {
- MutexAutoLock lock(mQuotaMutex);
- for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
- GroupInfoPair* pair = iter.UserData();
- MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
- MOZ_ASSERT(pair, "Null pointer!");
- uint64_t groupUsage = 0;
- RefPtr<GroupInfo> temporaryGroupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
- if (temporaryGroupInfo) {
- groupUsage += temporaryGroupInfo->mUsage;
- }
- RefPtr<GroupInfo> defaultGroupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
- if (defaultGroupInfo) {
- groupUsage += defaultGroupInfo->mUsage;
- }
- if (groupUsage > 0) {
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager, "Shouldn't be null!");
- if (groupUsage > quotaManager->GetGroupLimit()) {
- nsTArray<OriginInfo*> originInfos;
- if (temporaryGroupInfo) {
- originInfos.AppendElements(temporaryGroupInfo->mOriginInfos);
- }
- if (defaultGroupInfo) {
- originInfos.AppendElements(defaultGroupInfo->mOriginInfos);
- }
- originInfos.Sort(OriginInfoLRUComparator());
- for (uint32_t i = 0; i < originInfos.Length(); i++) {
- OriginInfo* originInfo = originInfos[i];
- doomedOriginInfos.AppendElement(originInfo);
- groupUsage -= originInfo->mUsage;
- if (groupUsage <= quotaManager->GetGroupLimit()) {
- break;
- }
- }
- }
- }
- }
- uint64_t usage = 0;
- for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
- usage += doomedOriginInfos[index]->mUsage;
- }
- if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
- nsTArray<OriginInfo*> originInfos;
- for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
- GroupInfoPair* pair = iter.UserData();
- MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
- MOZ_ASSERT(pair, "Null pointer!");
- RefPtr<GroupInfo> groupInfo =
- pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
- if (groupInfo) {
- originInfos.AppendElements(groupInfo->mOriginInfos);
- }
- groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
- if (groupInfo) {
- originInfos.AppendElements(groupInfo->mOriginInfos);
- }
- }
- for (uint32_t index = originInfos.Length(); index > 0; index--) {
- if (doomedOriginInfos.Contains(originInfos[index - 1])) {
- originInfos.RemoveElementAt(index - 1);
- }
- }
- originInfos.Sort(OriginInfoLRUComparator());
- for (uint32_t i = 0; i < originInfos.Length(); i++) {
- if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
- originInfos.TruncateLength(i);
- break;
- }
- usage += originInfos[i]->mUsage;
- }
- doomedOriginInfos.AppendElements(originInfos);
- }
- }
- for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
- OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
- DeleteFilesForOrigin(doomedOriginInfo->mGroupInfo->mPersistenceType,
- doomedOriginInfo->mOrigin);
- }
- nsTArray<OriginParams> doomedOrigins;
- {
- MutexAutoLock lock(mQuotaMutex);
- for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
- OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
- PersistenceType persistenceType =
- doomedOriginInfo->mGroupInfo->mPersistenceType;
- nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
- nsCString origin = doomedOriginInfo->mOrigin;
- bool isApp = doomedOriginInfo->mIsApp;
- LockedRemoveQuotaForOrigin(persistenceType, group, origin);
- #ifdef DEBUG
- doomedOriginInfos[index] = nullptr;
- #endif
- doomedOrigins.AppendElement(OriginParams(persistenceType, origin, isApp));
- }
- }
- for (const OriginParams& doomedOrigin : doomedOrigins) {
- OriginClearCompleted(doomedOrigin.mPersistenceType,
- doomedOrigin.mOrigin,
- doomedOrigin.mIsApp);
- }
- }
- void
- QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType,
- const nsACString& aOrigin)
- {
- nsCOMPtr<nsIFile> directory;
- nsresult rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
- getter_AddRefs(directory));
- NS_ENSURE_SUCCESS_VOID(rv);
- rv = directory->Remove(true);
- if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
- rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
- // This should never fail if we've closed all storage connections
- // correctly...
- NS_ERROR("Failed to remove directory!");
- }
- }
- void
- QuotaManager::FinalizeOriginEviction(
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
- {
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- RefPtr<FinalizeOriginEvictionOp> op =
- new FinalizeOriginEvictionOp(mOwningThread, aLocks);
- if (IsOnIOThread()) {
- op->RunOnIOThreadImmediately();
- } else {
- op->Dispatch();
- }
- }
- void
- QuotaManager::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
- {
- AssertIsOnBackgroundThread();
- auto quotaManager = static_cast<QuotaManager*>(aClosure);
- MOZ_ASSERT(quotaManager);
- NS_WARNING("Some storage operations are taking longer than expected "
- "during shutdown and will be aborted!");
- // Abort all operations.
- for (RefPtr<Client>& client : quotaManager->mClients) {
- client->AbortOperations(NullCString());
- }
- }
- auto
- QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType)
- -> DirectoryLockTable&
- {
- switch (aPersistenceType) {
- case PERSISTENCE_TYPE_TEMPORARY:
- return mTemporaryDirectoryLockTable;
- case PERSISTENCE_TYPE_DEFAULT:
- return mDefaultDirectoryLockTable;
- case PERSISTENCE_TYPE_PERSISTENT:
- case PERSISTENCE_TYPE_INVALID:
- default:
- MOZ_CRASH("Bad persistence type value!");
- }
- }
- /*******************************************************************************
- * Local class implementations
- ******************************************************************************/
- void
- OriginInfo::LockedDecreaseUsage(int64_t aSize)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- AssertNoUnderflow(mUsage, aSize);
- mUsage -= aSize;
- AssertNoUnderflow(mGroupInfo->mUsage, aSize);
- mGroupInfo->mUsage -= aSize;
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, aSize);
- quotaManager->mTemporaryStorageUsage -= aSize;
- }
- already_AddRefed<OriginInfo>
- GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- for (RefPtr<OriginInfo>& originInfo : mOriginInfos) {
- if (originInfo->mOrigin == aOrigin) {
- RefPtr<OriginInfo> result = originInfo;
- return result.forget();
- }
- }
- return nullptr;
- }
- void
- GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo),
- "Replacing an existing entry!");
- mOriginInfos.AppendElement(aOriginInfo);
- AssertNoOverflow(mUsage, aOriginInfo->mUsage);
- mUsage += aOriginInfo->mUsage;
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- AssertNoOverflow(quotaManager->mTemporaryStorageUsage, aOriginInfo->mUsage);
- quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage;
- }
- void
- GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin)
- {
- AssertCurrentThreadOwnsQuotaMutex();
- for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
- if (mOriginInfos[index]->mOrigin == aOrigin) {
- AssertNoUnderflow(mUsage, mOriginInfos[index]->mUsage);
- mUsage -= mOriginInfos[index]->mUsage;
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- AssertNoUnderflow(quotaManager->mTemporaryStorageUsage,
- mOriginInfos[index]->mUsage);
- quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage;
- mOriginInfos.RemoveElementAt(index);
- return;
- }
- }
- }
- void
- GroupInfo::LockedRemoveOriginInfos()
- {
- AssertCurrentThreadOwnsQuotaMutex();
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
- OriginInfo* originInfo = mOriginInfos[index - 1];
- AssertNoUnderflow(mUsage, originInfo->mUsage);
- mUsage -= originInfo->mUsage;
- AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, originInfo->mUsage);
- quotaManager->mTemporaryStorageUsage -= originInfo->mUsage;
- mOriginInfos.RemoveElementAt(index - 1);
- }
- }
- RefPtr<GroupInfo>&
- GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType)
- {
- switch (aPersistenceType) {
- case PERSISTENCE_TYPE_TEMPORARY:
- return mTemporaryStorageGroupInfo;
- case PERSISTENCE_TYPE_DEFAULT:
- return mDefaultStorageGroupInfo;
- case PERSISTENCE_TYPE_PERSISTENT:
- case PERSISTENCE_TYPE_INVALID:
- default:
- MOZ_CRASH("Bad persistence type value!");
- }
- }
- CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
- uint64_t aMinSizeToBeFreed)
- : mMinSizeToBeFreed(aMinSizeToBeFreed),
- mMutex(aMutex),
- mCondVar(aMutex, "CollectOriginsHelper::mCondVar"),
- mSizeToBeFreed(0),
- mWaiting(true)
- {
- MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
- mMutex.AssertCurrentThreadOwns();
- }
- int64_t
- CollectOriginsHelper::BlockAndReturnOriginsForEviction(
- nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
- {
- MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
- mMutex.AssertCurrentThreadOwns();
- while (mWaiting) {
- mCondVar.Wait();
- }
- mLocks.SwapElements(aLocks);
- return mSizeToBeFreed;
- }
- NS_IMETHODIMP
- CollectOriginsHelper::Run()
- {
- AssertIsOnBackgroundThread();
- QuotaManager* quotaManager = QuotaManager::Get();
- NS_ASSERTION(quotaManager, "Shouldn't be null!");
- // We use extra stack vars here to avoid race detector warnings (the same
- // memory accessed with and without the lock held).
- nsTArray<RefPtr<DirectoryLockImpl>> locks;
- uint64_t sizeToBeFreed =
- quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, locks);
- MutexAutoLock lock(mMutex);
- NS_ASSERTION(mWaiting, "Huh?!");
- mLocks.SwapElements(locks);
- mSizeToBeFreed = sizeToBeFreed;
- mWaiting = false;
- mCondVar.Notify();
- return NS_OK;
- }
- /*******************************************************************************
- * OriginOperationBase
- ******************************************************************************/
- NS_IMETHODIMP
- OriginOperationBase::Run()
- {
- nsresult rv;
- switch (mState) {
- case State_Initial: {
- rv = Init();
- break;
- }
- case State_Initializing: {
- rv = InitOnMainThread();
- break;
- }
- case State_FinishingInit: {
- rv = FinishInit();
- break;
- }
- case State_CreatingQuotaManager: {
- rv = QuotaManagerOpen();
- break;
- }
- case State_DirectoryOpenPending: {
- rv = DirectoryOpen();
- break;
- }
- case State_DirectoryWorkOpen: {
- rv = DirectoryWork();
- break;
- }
- case State_UnblockingOpen: {
- UnblockOpen();
- return NS_OK;
- }
- default:
- MOZ_CRASH("Bad state!");
- }
- if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) {
- Finish(rv);
- }
- return NS_OK;
- }
- nsresult
- OriginOperationBase::DirectoryOpen()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State_DirectoryOpenPending);
- QuotaManager* quotaManager = QuotaManager::Get();
- if (NS_WARN_IF(!quotaManager)) {
- return NS_ERROR_FAILURE;
- }
- // Must set this before dispatching otherwise we will race with the IO thread.
- AdvanceState();
- nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- void
- OriginOperationBase::Finish(nsresult aResult)
- {
- if (NS_SUCCEEDED(mResultCode)) {
- mResultCode = aResult;
- }
- // Must set mState before dispatching otherwise we will race with the main
- // thread.
- mState = State_UnblockingOpen;
- MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
- }
- nsresult
- OriginOperationBase::Init()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State_Initial);
- AdvanceState();
- if (mNeedsMainThreadInit) {
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
- } else {
- AdvanceState();
- MOZ_ALWAYS_SUCCEEDS(Run());
- }
- return NS_OK;
- }
- nsresult
- OriginOperationBase::InitOnMainThread()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mState == State_Initializing);
- nsresult rv = DoInitOnMainThread();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- AdvanceState();
- MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
- return NS_OK;
- }
- nsresult
- OriginOperationBase::FinishInit()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State_FinishingInit);
- if (QuotaManager::IsShuttingDown()) {
- return NS_ERROR_FAILURE;
- }
- AdvanceState();
- if (mNeedsQuotaManagerInit && !QuotaManager::Get()) {
- QuotaManager::GetOrCreate(this);
- } else {
- Open();
- }
- return NS_OK;
- }
- nsresult
- OriginOperationBase::QuotaManagerOpen()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(mState == State_CreatingQuotaManager);
- if (NS_WARN_IF(!QuotaManager::Get())) {
- return NS_ERROR_FAILURE;
- }
- Open();
- return NS_OK;
- }
- nsresult
- OriginOperationBase::DirectoryWork()
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(mState == State_DirectoryWorkOpen);
- QuotaManager* quotaManager = QuotaManager::Get();
- if (NS_WARN_IF(!quotaManager)) {
- return NS_ERROR_FAILURE;
- }
- nsresult rv;
- if (mNeedsQuotaManagerInit) {
- rv = quotaManager->EnsureStorageIsInitialized();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- rv = DoDirectoryWork(quotaManager);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Must set mState before dispatching otherwise we will race with the owning
- // thread.
- AdvanceState();
- MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
- return NS_OK;
- }
- void
- FinalizeOriginEvictionOp::Dispatch()
- {
- MOZ_ASSERT(!NS_IsMainThread());
- MOZ_ASSERT(GetState() == State_Initial);
- SetState(State_DirectoryOpenPending);
- MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
- }
- void
- FinalizeOriginEvictionOp::RunOnIOThreadImmediately()
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(GetState() == State_Initial);
- SetState(State_DirectoryWorkOpen);
- MOZ_ALWAYS_SUCCEEDS(this->Run());
- }
- void
- FinalizeOriginEvictionOp::Open()
- {
- MOZ_CRASH("Shouldn't get here!");
- }
- nsresult
- FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
- aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
- lock->GetOriginScope().GetOrigin(),
- lock->GetIsApp().Value());
- }
- return NS_OK;
- }
- void
- FinalizeOriginEvictionOp::UnblockOpen()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(GetState() == State_UnblockingOpen);
- #ifdef DEBUG
- NoteActorDestroyed();
- #endif
- mLocks.Clear();
- AdvanceState();
- }
- NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, Runnable)
- void
- NormalOriginOperationBase::Open()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
- MOZ_ASSERT(QuotaManager::Get());
- AdvanceState();
- QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType,
- mOriginScope,
- Nullable<Client::Type>(),
- mExclusive,
- this);
- }
- void
- NormalOriginOperationBase::UnblockOpen()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(GetState() == State_UnblockingOpen);
- SendResults();
- mDirectoryLock = nullptr;
- AdvanceState();
- }
- void
- NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aLock);
- MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
- MOZ_ASSERT(!mDirectoryLock);
- mDirectoryLock = aLock;
- nsresult rv = DirectoryOpen();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Finish(rv);
- return;
- }
- }
- void
- NormalOriginOperationBase::DirectoryLockFailed()
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
- MOZ_ASSERT(!mDirectoryLock);
- Finish(NS_ERROR_FAILURE);
- }
- nsresult
- SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(!mPersistenceType.IsNull());
- MOZ_ASSERT(mOriginScope.IsOrigin());
- PROFILER_LABEL("Quota", "SaveOriginAccessTimeOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- nsCOMPtr<nsIFile> directory;
- nsresult rv =
- aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
- mOriginScope.GetOrigin(),
- getter_AddRefs(directory));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIBinaryOutputStream> stream;
- rv = GetBinaryOutputStream(directory,
- NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
- kUpdateFileFlag,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // The origin directory may not exist anymore.
- if (stream) {
- rv = stream->Write64(mTimestamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- void
- SaveOriginAccessTimeOp::SendResults()
- {
- #ifdef DEBUG
- NoteActorDestroyed();
- #endif
- }
- /*******************************************************************************
- * Quota
- ******************************************************************************/
- Quota::Quota()
- #ifdef DEBUG
- : mActorDestroyed(false)
- #endif
- {
- }
- Quota::~Quota()
- {
- MOZ_ASSERT(mActorDestroyed);
- }
- void
- Quota::StartIdleMaintenance()
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(!QuotaManager::IsShuttingDown());
- QuotaManager* quotaManager = QuotaManager::Get();
- if (NS_WARN_IF(!quotaManager)) {
- return;
- }
- quotaManager->StartIdleMaintenance();
- }
- void
- Quota::ActorDestroy(ActorDestroyReason aWhy)
- {
- AssertIsOnBackgroundThread();
- #ifdef DEBUG
- MOZ_ASSERT(!mActorDestroyed);
- mActorDestroyed = true;
- #endif
- }
- PQuotaUsageRequestParent*
- Quota::AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
- RefPtr<QuotaUsageRequestBase> actor;
- switch (aParams.type()) {
- case UsageRequestParams::TAllUsageParams:
- actor = new GetUsageOp(aParams);
- break;
- case UsageRequestParams::TOriginUsageParams:
- actor = new GetOriginUsageOp(aParams);
- break;
- default:
- MOZ_CRASH("Should never get here!");
- }
- MOZ_ASSERT(actor);
- // Transfer ownership to IPDL.
- return actor.forget().take();
- }
- bool
- Quota::RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
- const UsageRequestParams& aParams)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aActor);
- MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
- auto* op = static_cast<QuotaUsageRequestBase*>(aActor);
- if (NS_WARN_IF(!op->Init(this))) {
- return false;
- }
- op->RunImmediately();
- return true;
- }
- bool
- Quota::DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aActor);
- // Transfer ownership back from IPDL.
- RefPtr<QuotaUsageRequestBase> actor =
- dont_AddRef(static_cast<QuotaUsageRequestBase*>(aActor));
- return true;
- }
- PQuotaRequestParent*
- Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aParams.type() != RequestParams::T__None);
- if (aParams.type() == RequestParams::TClearOriginsParams) {
- PBackgroundParent* actor = Manager();
- MOZ_ASSERT(actor);
- if (BackgroundParent::IsOtherProcessActor(actor)) {
- ASSERT_UNLESS_FUZZING();
- return nullptr;
- }
- }
- RefPtr<QuotaRequestBase> actor;
- switch (aParams.type()) {
- case RequestParams::TClearOriginParams:
- case RequestParams::TClearOriginsParams:
- actor = new OriginClearOp(aParams);
- break;
- case RequestParams::TClearAllParams:
- actor = new ResetOrClearOp(/* aClear */ true);
- break;
- case RequestParams::TResetAllParams:
- actor = new ResetOrClearOp(/* aClear */ false);
- break;
- default:
- MOZ_CRASH("Should never get here!");
- }
- MOZ_ASSERT(actor);
- // Transfer ownership to IPDL.
- return actor.forget().take();
- }
- bool
- Quota::RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
- const RequestParams& aParams)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aActor);
- MOZ_ASSERT(aParams.type() != RequestParams::T__None);
- auto* op = static_cast<QuotaRequestBase*>(aActor);
- if (NS_WARN_IF(!op->Init(this))) {
- return false;
- }
- op->RunImmediately();
- return true;
- }
- bool
- Quota::DeallocPQuotaRequestParent(PQuotaRequestParent* aActor)
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(aActor);
- // Transfer ownership back from IPDL.
- RefPtr<QuotaRequestBase> actor =
- dont_AddRef(static_cast<QuotaRequestBase*>(aActor));
- return true;
- }
- bool
- Quota::RecvStartIdleMaintenance()
- {
- AssertIsOnBackgroundThread();
- PBackgroundParent* actor = Manager();
- MOZ_ASSERT(actor);
- if (BackgroundParent::IsOtherProcessActor(actor)) {
- ASSERT_UNLESS_FUZZING();
- return false;
- }
- if (QuotaManager::IsShuttingDown()) {
- return true;
- }
- QuotaManager* quotaManager = QuotaManager::Get();
- if (!quotaManager) {
- nsCOMPtr<nsIRunnable> callback =
- NewRunnableMethod(this, &Quota::StartIdleMaintenance);
- QuotaManager::GetOrCreate(callback);
- return true;
- }
- quotaManager->StartIdleMaintenance();
- return true;
- }
- bool
- Quota::RecvStopIdleMaintenance()
- {
- AssertIsOnBackgroundThread();
- PBackgroundParent* actor = Manager();
- MOZ_ASSERT(actor);
- if (BackgroundParent::IsOtherProcessActor(actor)) {
- ASSERT_UNLESS_FUZZING();
- return false;
- }
- if (QuotaManager::IsShuttingDown()) {
- return true;
- }
- QuotaManager* quotaManager = QuotaManager::Get();
- if (!quotaManager) {
- return true;
- }
- quotaManager->StopIdleMaintenance();
- return true;
- }
- bool
- QuotaUsageRequestBase::Init(Quota* aQuota)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aQuota);
- mNeedsQuotaManagerInit = true;
- return true;
- }
- nsresult
- QuotaUsageRequestBase::GetUsageForOrigin(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType,
- const nsACString& aGroup,
- const nsACString& aOrigin,
- bool aIsApp,
- UsageInfo* aUsageInfo)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aQuotaManager);
- MOZ_ASSERT(aUsageInfo);
- MOZ_ASSERT(aUsageInfo->TotalUsage() == 0);
- nsCOMPtr<nsIFile> directory;
- nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType,
- aOrigin,
- getter_AddRefs(directory));
- NS_ENSURE_SUCCESS(rv, rv);
- bool exists;
- rv = directory->Exists(&exists);
- NS_ENSURE_SUCCESS(rv, rv);
- // If the directory exists then enumerate all the files inside, adding up
- // the sizes to get the final usage statistic.
- if (exists && !mCanceled) {
- bool initialized;
- if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
- nsCString originKey = OriginKey(aPersistenceType, aOrigin);
- initialized = aQuotaManager->IsOriginInitialized(originKey);
- } else {
- initialized = aQuotaManager->IsTemporaryStorageInitialized();
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
- hasMore && !mCanceled) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- NS_ENSURE_TRUE(file, NS_NOINTERFACE);
- nsString leafName;
- rv = file->GetLeafName(leafName);
- NS_ENSURE_SUCCESS(rv, rv);
- if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
- leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
- leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- continue;
- }
- if (!initialized) {
- bool isDirectory;
- rv = file->IsDirectory(&isDirectory);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!isDirectory) {
- NS_WARNING("Unknown file found!");
- return NS_ERROR_UNEXPECTED;
- }
- }
- if (MaybeRemoveCorruptDirectory(leafName, file)) {
- continue;
- }
- Client::Type clientType;
- rv = Client::TypeFromText(leafName, clientType);
- if (NS_FAILED(rv)) {
- NS_WARNING("Unknown directory found!");
- if (!initialized) {
- return NS_ERROR_UNEXPECTED;
- }
- continue;
- }
- Client* client = aQuotaManager->GetClient(clientType);
- MOZ_ASSERT(client);
- if (initialized) {
- rv = client->GetUsageForOrigin(aPersistenceType,
- aGroup,
- aOrigin,
- mCanceled,
- aUsageInfo);
- }
- else {
- rv = client->InitOrigin(aPersistenceType,
- aGroup,
- aOrigin,
- mCanceled,
- aUsageInfo);
- }
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- return NS_OK;
- }
- void
- QuotaUsageRequestBase::SendResults()
- {
- AssertIsOnOwningThread();
- if (IsActorDestroyed()) {
- if (NS_SUCCEEDED(mResultCode)) {
- mResultCode = NS_ERROR_FAILURE;
- }
- } else {
- if (mCanceled) {
- mResultCode = NS_ERROR_FAILURE;
- }
- UsageRequestResponse response;
- if (NS_SUCCEEDED(mResultCode)) {
- GetResponse(response);
- } else {
- response = mResultCode;
- }
- Unused << PQuotaUsageRequestParent::Send__delete__(this, response);
- }
- }
- void
- QuotaUsageRequestBase::ActorDestroy(ActorDestroyReason aWhy)
- {
- AssertIsOnOwningThread();
- NoteActorDestroyed();
- }
- bool
- QuotaUsageRequestBase::RecvCancel()
- {
- AssertIsOnOwningThread();
- if (mCanceled.exchange(true)) {
- NS_WARNING("Canceled more than once?!");
- return false;
- }
- return true;
- }
- GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
- : mGetAll(aParams.get_AllUsageParams().getAll())
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams);
- }
- nsresult
- GetUsageOp::TraverseRepository(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aQuotaManager);
- nsresult rv;
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = directory->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- return NS_OK;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
- hasMore && !mCanceled) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
- MOZ_ASSERT(originDir);
- bool isDirectory;
- rv = originDir->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- nsString leafName;
- rv = originDir->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- QM_WARNING("Something (%s) in the repository that doesn't belong!",
- NS_ConvertUTF16toUTF8(leafName).get());
- }
- continue;
- }
- int64_t timestamp;
- nsCString suffix;
- nsCString group;
- nsCString origin;
- bool isApp;
- rv = aQuotaManager->GetDirectoryMetadata2WithRestore(originDir,
- persistent,
- ×tamp,
- suffix,
- group,
- origin,
- &isApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!mGetAll && aQuotaManager->IsOriginInternal(origin)) {
- continue;
- }
- OriginUsage* originUsage;
- // We can't store pointers to OriginUsage objects in the hashtable
- // since AppendElement() reallocates its internal array buffer as number
- // of elements grows.
- uint32_t index;
- if (mOriginUsagesIndex.Get(origin, &index)) {
- originUsage = &mOriginUsages[index];
- } else {
- index = mOriginUsages.Length();
- originUsage = mOriginUsages.AppendElement();
- originUsage->origin() = origin;
- originUsage->persisted() = false;
- originUsage->usage() = 0;
- mOriginUsagesIndex.Put(origin, index);
- }
- UsageInfo usageInfo;
- rv = GetUsageForOrigin(aQuotaManager,
- aPersistenceType,
- group,
- origin,
- isApp,
- &usageInfo);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- originUsage->usage() = originUsage->usage() + usageInfo.TotalUsage();
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- PROFILER_LABEL("Quota", "GetUsageOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- nsresult rv;
- for (const PersistenceType type : kAllPersistenceTypes) {
- rv = TraverseRepository(aQuotaManager, type);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- void
- GetUsageOp::GetResponse(UsageRequestResponse& aResponse)
- {
- AssertIsOnOwningThread();
- aResponse = AllUsageResponse();
- if (!mOriginUsages.IsEmpty()) {
- nsTArray<OriginUsage>& originUsages =
- aResponse.get_AllUsageResponse().originUsages();
- mOriginUsages.SwapElements(originUsages);
- }
- }
- GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams)
- : mParams(aParams.get_OriginUsageParams())
- , mGetGroupUsage(aParams.get_OriginUsageParams().getGroupUsage())
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams);
- }
- bool
- GetOriginUsageOp::Init(Quota* aQuota)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aQuota);
- if (NS_WARN_IF(!QuotaUsageRequestBase::Init(aQuota))) {
- return false;
- }
- mNeedsMainThreadInit = true;
- return true;
- }
- nsresult
- GetOriginUsageOp::DoInitOnMainThread()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(GetState() == State_Initializing);
- MOZ_ASSERT(mNeedsMainThreadInit);
- const PrincipalInfo& principalInfo = mParams.principalInfo();
- nsresult rv;
- nsCOMPtr<nsIPrincipal> principal =
- PrincipalInfoToPrincipal(principalInfo, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Figure out which origin we're dealing with.
- nsCString origin;
- rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
- &origin, &mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- mOriginScope.SetFromOrigin(origin);
- return NS_OK;
- }
- nsresult
- GetOriginUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
- PROFILER_LABEL("Quota", "GetOriginUsageOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- nsresult rv;
- if (mGetGroupUsage) {
- nsCOMPtr<nsIFile> directory;
- // Ensure origin is initialized first. It will initialize all origins for
- // temporary storage including origins belonging to our group.
- rv = aQuotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_TEMPORARY,
- mSuffix, mGroup,
- mOriginScope.GetOrigin(),
- mIsApp,
- getter_AddRefs(directory));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Get cached usage and limit (the method doesn't have to stat any files).
- aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
- return NS_OK;
- }
- // Add all the persistent/temporary/default storage files we care about.
- for (const PersistenceType type : kAllPersistenceTypes) {
- UsageInfo usageInfo;
- rv = GetUsageForOrigin(aQuotaManager,
- type,
- mGroup,
- mOriginScope.GetOrigin(),
- mIsApp,
- &usageInfo);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- mUsageInfo.Append(usageInfo);
- }
- return NS_OK;
- }
- void
- GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse)
- {
- AssertIsOnOwningThread();
- OriginUsageResponse usageResponse;
- // We'll get the group usage when mGetGroupUsage is true and get the
- // origin usage when mGetGroupUsage is false.
- usageResponse.usage() = mUsageInfo.TotalUsage();
- if (mGetGroupUsage) {
- usageResponse.limit() = mUsageInfo.Limit();
- } else {
- usageResponse.fileUsage() = mUsageInfo.FileUsage();
- }
- aResponse = usageResponse;
- }
- bool
- QuotaRequestBase::Init(Quota* aQuota)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aQuota);
- mNeedsQuotaManagerInit = true;
- return true;
- }
- void
- QuotaRequestBase::SendResults()
- {
- AssertIsOnOwningThread();
- if (IsActorDestroyed()) {
- if (NS_SUCCEEDED(mResultCode)) {
- mResultCode = NS_ERROR_FAILURE;
- }
- } else {
- RequestResponse response;
- if (NS_SUCCEEDED(mResultCode)) {
- GetResponse(response);
- } else {
- response = mResultCode;
- }
- Unused << PQuotaRequestParent::Send__delete__(this, response);
- }
- }
- void
- QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy)
- {
- AssertIsOnOwningThread();
- NoteActorDestroyed();
- }
- void
- ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aQuotaManager);
- nsresult rv;
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = directory->InitWithPath(aQuotaManager->GetStoragePath());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = directory->Remove(true);
- if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
- rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
- // This should never fail if we've closed all storage connections
- // correctly...
- MOZ_ASSERT(false, "Failed to remove storage directory!");
- }
- nsCOMPtr<nsIFile> storageFile =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = storageFile->InitWithPath(aQuotaManager->GetBasePath());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = storageFile->Remove(true);
- if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
- rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
- // This should never fail if we've closed the storage connection
- // correctly...
- MOZ_ASSERT(false, "Failed to remove storage file!");
- }
- }
- nsresult
- ResetOrClearOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- PROFILER_LABEL("Quota", "ResetOrClearOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- if (mClear) {
- DeleteFiles(aQuotaManager);
- }
- aQuotaManager->RemoveQuota();
- aQuotaManager->ResetOrClearCompleted();
- return NS_OK;
- }
- void
- ResetOrClearOp::GetResponse(RequestResponse& aResponse)
- {
- AssertIsOnOwningThread();
- if (mClear) {
- aResponse = ClearAllResponse();
- } else {
- aResponse = ResetAllResponse();
- }
- }
- OriginClearOp::OriginClearOp(const RequestParams& aParams)
- : QuotaRequestBase(/* aExclusive */ true)
- , mParams(aParams)
- , mMultiple(aParams.type() == RequestParams::TClearOriginsParams)
- {
- MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams ||
- aParams.type() == RequestParams::TClearOriginsParams);
- }
- bool
- OriginClearOp::Init(Quota* aQuota)
- {
- AssertIsOnOwningThread();
- MOZ_ASSERT(aQuota);
- if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
- return false;
- }
- if (!mMultiple) {
- const ClearOriginParams& params = mParams.get_ClearOriginParams();
- if (params.persistenceTypeIsExplicit()) {
- MOZ_ASSERT(params.persistenceType() != PERSISTENCE_TYPE_INVALID);
- mPersistenceType.SetValue(params.persistenceType());
- }
- }
- mNeedsMainThreadInit = true;
- return true;
- }
- nsresult
- OriginClearOp::DoInitOnMainThread()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(GetState() == State_Initializing);
- MOZ_ASSERT(mNeedsMainThreadInit);
- if (mMultiple) {
- const ClearOriginsParams& params = mParams.get_ClearOriginsParams();
- mOriginScope.SetFromJSONPattern(params.pattern());
- } else {
- const ClearOriginParams& params = mParams.get_ClearOriginParams();
- const PrincipalInfo& principalInfo = params.principalInfo();
- nsresult rv;
- nsCOMPtr<nsIPrincipal> principal =
- PrincipalInfoToPrincipal(principalInfo, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Figure out which origin we're dealing with.
- nsCString origin;
- rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr, &origin,
- nullptr);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (params.clearAll()) {
- mOriginScope.SetFromPrefix(origin);
- } else {
- mOriginScope.SetFromOrigin(origin);
- }
- }
- return NS_OK;
- }
- void
- OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager,
- PersistenceType aPersistenceType)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aQuotaManager);
- nsresult rv;
- nsCOMPtr<nsIFile> directory =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- if (NS_WARN_IF(NS_FAILED(
- directory->GetDirectoryEntries(getter_AddRefs(entries)))) || !entries) {
- return;
- }
- OriginScope originScope = mOriginScope.Clone();
- if (originScope.IsOrigin()) {
- nsCString originSanitized(originScope.GetOrigin());
- SanitizeOriginString(originSanitized);
- originScope.SetOrigin(originSanitized);
- } else if (originScope.IsPrefix()) {
- nsCString prefixSanitized(originScope.GetPrefix());
- SanitizeOriginString(prefixSanitized);
- originScope.SetPrefix(prefixSanitized);
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- MOZ_ASSERT(file);
- bool isDirectory;
- rv = file->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- nsString leafName;
- rv = file->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- if (!isDirectory) {
- if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- QM_WARNING("Something (%s) in the repository that doesn't belong!",
- NS_ConvertUTF16toUTF8(leafName).get());
- }
- continue;
- }
- // Skip the origin directory if it doesn't match the pattern.
- if (!originScope.MatchesOrigin(OriginScope::FromOrigin(
- NS_ConvertUTF16toUTF8(leafName)))) {
- continue;
- }
- bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
- int64_t timestamp;
- nsCString suffix;
- nsCString group;
- nsCString origin;
- bool isApp;
- rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
- persistent,
- ×tamp,
- suffix,
- group,
- origin,
- &isApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- for (uint32_t index = 0; index < 10; index++) {
- // We can't guarantee that this will always succeed on Windows...
- if (NS_SUCCEEDED((rv = file->Remove(true)))) {
- break;
- }
- NS_WARNING("Failed to remove directory, retrying after a short delay.");
- PR_Sleep(PR_MillisecondsToInterval(200));
- }
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to remove directory, giving up!");
- }
- if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
- aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin);
- }
- aQuotaManager->OriginClearCompleted(aPersistenceType, origin, isApp);
- }
- }
- nsresult
- OriginClearOp::DoDirectoryWork(QuotaManager* aQuotaManager)
- {
- AssertIsOnIOThread();
- PROFILER_LABEL("Quota", "OriginClearOp::DoDirectoryWork",
- js::ProfileEntry::Category::OTHER);
- if (mPersistenceType.IsNull()) {
- for (const PersistenceType type : kAllPersistenceTypes) {
- DeleteFiles(aQuotaManager, type);
- }
- } else {
- DeleteFiles(aQuotaManager, mPersistenceType.Value());
- }
- return NS_OK;
- }
- void
- OriginClearOp::GetResponse(RequestResponse& aResponse)
- {
- AssertIsOnOwningThread();
- if (mMultiple) {
- aResponse = ClearOriginsResponse();
- } else {
- aResponse = ClearOriginResponse();
- }
- }
- nsresult
- StorageDirectoryHelper::AddOriginDirectory(nsIFile* aDirectory,
- OriginProps** aOriginProps)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- OriginProps* originProps;
- nsString leafName;
- nsresult rv = aDirectory->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (leafName.EqualsLiteral(kChromeOrigin)) {
- originProps = mOriginProps.AppendElement();
- originProps->mDirectory = aDirectory;
- originProps->mSpec = kChromeOrigin;
- originProps->mType = OriginProps::eChrome;
- } else {
- nsCString spec;
- PrincipalOriginAttributes attrs;
- bool result = OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
- spec, &attrs);
- if (NS_WARN_IF(!result)) {
- return NS_ERROR_FAILURE;
- }
- originProps = mOriginProps.AppendElement();
- originProps->mDirectory = aDirectory;
- originProps->mSpec = spec;
- originProps->mAttrs = attrs;
- originProps->mType = OriginProps::eContent;
- }
- if (aOriginProps) {
- *aOriginProps = originProps;
- }
- return NS_OK;
- }
- nsresult
- StorageDirectoryHelper::ProcessOriginDirectories()
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(!mOriginProps.IsEmpty());
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
- {
- mozilla::MutexAutoLock autolock(mMutex);
- while (mWaiting) {
- mCondVar.Wait();
- }
- }
- if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
- return mMainThreadResultCode;
- }
- // Verify that the bounce to the main thread didn't start the shutdown
- // sequence.
- if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
- return NS_ERROR_FAILURE;
- }
- nsresult rv = DoProcessOriginDirectories();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- StorageDirectoryHelper::RunOnMainThread()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!mOriginProps.IsEmpty());
- nsresult rv;
- nsCOMPtr<nsIScriptSecurityManager> secMan =
- do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- for (uint32_t count = mOriginProps.Length(), index = 0;
- index < count;
- index++) {
- OriginProps& originProps = mOriginProps[index];
- switch (originProps.mType) {
- case OriginProps::eChrome: {
- QuotaManager::GetInfoForChrome(&originProps.mSuffix,
- &originProps.mGroup,
- &originProps.mOrigin,
- &originProps.mIsApp);
- break;
- }
- case OriginProps::eContent: {
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), originProps.mSpec);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIPrincipal> principal =
- BasePrincipal::CreateCodebasePrincipal(uri, originProps.mAttrs);
- if (NS_WARN_IF(!principal)) {
- return NS_ERROR_FAILURE;
- }
- rv = QuotaManager::GetInfoFromPrincipal(principal,
- &originProps.mSuffix,
- &originProps.mGroup,
- &originProps.mOrigin,
- &originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- break;
- }
- default:
- MOZ_CRASH("Bad type!");
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- StorageDirectoryHelper::Run()
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsresult rv = RunOnMainThread();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- mMainThreadResultCode = rv;
- }
- MutexAutoLock lock(mMutex);
- MOZ_ASSERT(mWaiting);
- mWaiting = false;
- mCondVar.Notify();
- return NS_OK;
- }
- // static
- bool
- OriginParser::ParseOrigin(const nsACString& aOrigin,
- nsCString& aSpec,
- PrincipalOriginAttributes* aAttrs)
- {
- MOZ_ASSERT(!aOrigin.IsEmpty());
- MOZ_ASSERT(aAttrs);
- PrincipalOriginAttributes originAttributes;
- nsCString originNoSuffix;
- bool ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
- if (!ok) {
- return false;
- }
- OriginParser parser(originNoSuffix, originAttributes);
- return parser.Parse(aSpec, aAttrs);
- }
- bool
- OriginParser::Parse(nsACString& aSpec, PrincipalOriginAttributes* aAttrs)
- {
- MOZ_ASSERT(aAttrs);
- while (mTokenizer.hasMoreTokens()) {
- const nsDependentCSubstring& token = mTokenizer.nextToken();
- HandleToken(token);
- if (mError) {
- break;
- }
- if (!mHandledTokens.IsEmpty()) {
- mHandledTokens.Append(NS_LITERAL_CSTRING(", "));
- }
- mHandledTokens.Append('\'');
- mHandledTokens.Append(token);
- mHandledTokens.Append('\'');
- }
- if (!mError && mTokenizer.separatorAfterCurrentToken()) {
- HandleTrailingSeparator();
- }
- if (mError) {
- QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin.get(),
- mHandledTokens.get());
- return false;
- }
- MOZ_ASSERT(mState == eComplete || mState == eHandledTrailingSeparator);
- if (mAppId == kNoAppId) {
- *aAttrs = mOriginAttributes;
- } else {
- MOZ_ASSERT(mOriginAttributes.mAppId == kNoAppId);
- *aAttrs = PrincipalOriginAttributes(mAppId, mInIsolatedMozBrowser);
- }
- nsAutoCString spec(mSchema);
- if (mSchemaType == eFile) {
- spec.AppendLiteral("://");
- for (uint32_t count = mPathnameComponents.Length(), index = 0;
- index < count;
- index++) {
- spec.Append('/');
- spec.Append(mPathnameComponents[index]);
- }
- aSpec = spec;
- return true;
- }
- if (mSchemaType == eAbout) {
- spec.Append(':');
- } else {
- spec.AppendLiteral("://");
- }
- spec.Append(mHost);
- if (!mPort.IsNull()) {
- spec.Append(':');
- spec.AppendInt(mPort.Value());
- }
- aSpec = spec;
- return true;
- }
- void
- OriginParser::HandleSchema(const nsDependentCSubstring& aToken)
- {
- MOZ_ASSERT(!aToken.IsEmpty());
- MOZ_ASSERT(mState == eExpectingAppIdOrSchema || mState == eExpectingSchema);
- bool isAbout = false;
- bool isFile = false;
- if (aToken.EqualsLiteral("http") ||
- aToken.EqualsLiteral("https") ||
- (isAbout = aToken.EqualsLiteral("about") ||
- aToken.EqualsLiteral("moz-safe-about")) ||
- aToken.EqualsLiteral("indexeddb") ||
- (isFile = aToken.EqualsLiteral("file")) ||
- aToken.EqualsLiteral("app") ||
- aToken.EqualsLiteral("resource")) {
- mSchema = aToken;
- if (isAbout) {
- mSchemaType = eAbout;
- mState = eExpectingHost;
- } else {
- if (isFile) {
- mSchemaType = eFile;
- }
- mState = eExpectingEmptyToken1;
- }
- return;
- }
- QM_WARNING("'%s' is not a valid schema!", nsCString(aToken).get());
- mError = true;
- }
- void
- OriginParser::HandlePathnameComponent(const nsDependentCSubstring& aToken)
- {
- MOZ_ASSERT(!aToken.IsEmpty());
- MOZ_ASSERT(mState == eExpectingEmptyTokenOrDriveLetterOrPathnameComponent ||
- mState == eExpectingEmptyTokenOrPathnameComponent);
- MOZ_ASSERT(mSchemaType == eFile);
- mPathnameComponents.AppendElement(aToken);
- mState = mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
- : eComplete;
- }
- void
- OriginParser::HandleToken(const nsDependentCSubstring& aToken)
- {
- switch (mState) {
- case eExpectingAppIdOrSchema: {
- if (aToken.IsEmpty()) {
- QM_WARNING("Expected an app id or schema (not an empty string)!");
- mError = true;
- return;
- }
- if (NS_IsAsciiDigit(aToken.First())) {
- // nsDependentCSubstring doesn't provice ToInteger()
- nsCString token(aToken);
- nsresult rv;
- uint32_t appId = token.ToInteger(&rv);
- if (NS_SUCCEEDED(rv)) {
- mAppId = appId;
- mState = eExpectingInMozBrowser;
- return;
- }
- }
- HandleSchema(aToken);
- return;
- }
- case eExpectingInMozBrowser: {
- if (aToken.Length() != 1) {
- QM_WARNING("'%d' is not a valid length for the inMozBrowser flag!",
- aToken.Length());
- mError = true;
- return;
- }
- if (aToken.First() == 't') {
- mInIsolatedMozBrowser = true;
- } else if (aToken.First() == 'f') {
- mInIsolatedMozBrowser = false;
- } else {
- QM_WARNING("'%s' is not a valid value for the inMozBrowser flag!",
- nsCString(aToken).get());
- mError = true;
- return;
- }
- mState = eExpectingSchema;
- return;
- }
- case eExpectingSchema: {
- if (aToken.IsEmpty()) {
- QM_WARNING("Expected a schema (not an empty string)!");
- mError = true;
- return;
- }
- HandleSchema(aToken);
- return;
- }
- case eExpectingEmptyToken1: {
- if (!aToken.IsEmpty()) {
- QM_WARNING("Expected the first empty token!");
- mError = true;
- return;
- }
- mState = eExpectingEmptyToken2;
- return;
- }
- case eExpectingEmptyToken2: {
- if (!aToken.IsEmpty()) {
- QM_WARNING("Expected the second empty token!");
- mError = true;
- return;
- }
- if (mSchemaType == eFile) {
- mState = eExpectingEmptyToken3;
- } else {
- mState = eExpectingHost;
- }
- return;
- }
- case eExpectingEmptyToken3: {
- MOZ_ASSERT(mSchemaType == eFile);
- if (!aToken.IsEmpty()) {
- QM_WARNING("Expected the third empty token!");
- mError = true;
- return;
- }
- mState = mTokenizer.hasMoreTokens()
- ? eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
- : eComplete;
- return;
- }
- case eExpectingHost: {
- if (aToken.IsEmpty()) {
- QM_WARNING("Expected a host (not an empty string)!");
- mError = true;
- return;
- }
- mHost = aToken;
- mState = mTokenizer.hasMoreTokens() ? eExpectingPort : eComplete;
- return;
- }
- case eExpectingPort: {
- MOZ_ASSERT(mSchemaType == eNone);
- if (aToken.IsEmpty()) {
- QM_WARNING("Expected a port (not an empty string)!");
- mError = true;
- return;
- }
- // nsDependentCSubstring doesn't provice ToInteger()
- nsCString token(aToken);
- nsresult rv;
- uint32_t port = token.ToInteger(&rv);
- if (NS_SUCCEEDED(rv)) {
- mPort.SetValue() = port;
- } else {
- QM_WARNING("'%s' is not a valid port number!", token.get());
- mError = true;
- return;
- }
- mState = eComplete;
- return;
- }
- case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent: {
- MOZ_ASSERT(mSchemaType == eFile);
- if (aToken.IsEmpty()) {
- mPathnameComponents.AppendElement(EmptyCString());
- mState =
- mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
- : eComplete;
- return;
- }
- if (aToken.Length() == 1 && NS_IsAsciiAlpha(aToken.First())) {
- mMaybeDriveLetter = true;
- mPathnameComponents.AppendElement(aToken);
- mState =
- mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
- : eComplete;
- return;
- }
- HandlePathnameComponent(aToken);
- return;
- }
- case eExpectingEmptyTokenOrPathnameComponent: {
- MOZ_ASSERT(mSchemaType == eFile);
- if (aToken.IsEmpty()) {
- if (mMaybeDriveLetter) {
- MOZ_ASSERT(mPathnameComponents.Length() == 1);
- nsCString& pathnameComponent = mPathnameComponents[0];
- pathnameComponent.Append(':');
- mMaybeDriveLetter = false;
- } else {
- mPathnameComponents.AppendElement(EmptyCString());
- }
- mState =
- mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
- : eComplete;
- return;
- }
- HandlePathnameComponent(aToken);
- return;
- }
- default:
- MOZ_CRASH("Should never get here!");
- }
- }
- void
- OriginParser::HandleTrailingSeparator()
- {
- MOZ_ASSERT(mState == eComplete);
- MOZ_ASSERT(mSchemaType == eFile);
- mPathnameComponents.AppendElement(EmptyCString());
- mState = eHandledTrailingSeparator;
- }
- nsresult
- CreateOrUpgradeDirectoryMetadataHelper::CreateOrUpgradeMetadataFiles()
- {
- AssertIsOnIOThread();
- bool exists;
- nsresult rv = mDirectory->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- return NS_OK;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
- MOZ_ASSERT(originDir);
- nsString leafName;
- rv = originDir->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool isDirectory;
- rv = originDir->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (isDirectory) {
- if (leafName.EqualsLiteral("moz-safe-about+++home")) {
- // This directory was accidentally created by a buggy nightly and can
- // be safely removed.
- QM_WARNING("Deleting accidental moz-safe-about+++home directory!");
- rv = originDir->Remove(/* aRecursive */ true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- continue;
- }
- } else {
- if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- QM_WARNING("Something (%s) in the storage directory that doesn't belong!",
- NS_ConvertUTF16toUTF8(leafName).get());
- }
- continue;
- }
- if (mPersistent) {
- rv = MaybeUpgradeOriginDirectory(originDir);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- OriginProps* originProps;
- rv = AddOriginDirectory(originDir, &originProps);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!mPersistent) {
- int64_t timestamp;
- nsCString group;
- nsCString origin;
- bool hasIsApp;
- rv = GetDirectoryMetadata(originDir,
- ×tamp,
- group,
- origin,
- &hasIsApp);
- if (NS_FAILED(rv)) {
- timestamp = INT64_MIN;
- rv = GetLastModifiedTime(originDir, ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- originProps->mTimestamp = timestamp;
- originProps->mNeedsRestore = true;
- } else if (hasIsApp) {
- originProps->mIgnore = true;
- }
- }
- else if (!QuotaManager::IsOriginInternal(originProps->mSpec)) {
- int64_t timestamp = INT64_MIN;
- rv = GetLastModifiedTime(originDir, ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- originProps->mTimestamp = timestamp;
- }
- }
- if (mOriginProps.IsEmpty()) {
- return NS_OK;
- }
- rv = ProcessOriginDirectories();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- CreateOrUpgradeDirectoryMetadataHelper::MaybeUpgradeOriginDirectory(
- nsIFile* aDirectory)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- nsCOMPtr<nsIFile> metadataFile;
- nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = metadataFile->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- // Directory structure upgrade needed.
- // Move all files to IDB specific directory.
- nsString idbDirectoryName;
- rv = Client::TypeToText(Client::IDB, idbDirectoryName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> idbDirectory;
- rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = idbDirectory->Append(idbDirectoryName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
- if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
- NS_WARNING("IDB directory already exists!");
- bool isDirectory;
- rv = idbDirectory->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (NS_WARN_IF(!isDirectory)) {
- return NS_ERROR_UNEXPECTED;
- }
- }
- else {
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
- if (NS_WARN_IF(!file)) {
- return rv;
- }
- nsString leafName;
- rv = file->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!leafName.Equals(idbDirectoryName)) {
- rv = file->MoveTo(idbDirectory, EmptyString());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- }
- rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- CreateOrUpgradeDirectoryMetadataHelper::GetDirectoryMetadata(
- nsIFile* aDirectory,
- int64_t* aTimestamp,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aHasIsApp)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aTimestamp);
- MOZ_ASSERT(aHasIsApp);
- MOZ_ASSERT(!mPersistent);
- nsCOMPtr<nsIBinaryInputStream> binaryStream;
- nsresult rv = GetBinaryInputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_FILE_NAME),
- getter_AddRefs(binaryStream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint64_t timestamp;
- rv = binaryStream->Read64(×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString group;
- rv = binaryStream->ReadCString(group);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString origin;
- rv = binaryStream->ReadCString(origin);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool dummyIsApp;
- bool hasIsApp = NS_SUCCEEDED(binaryStream->ReadBoolean(&dummyIsApp));
- *aTimestamp = timestamp;
- aGroup = group;
- aOrigin = origin;
- *aHasIsApp = hasIsApp;
- return NS_OK;
- }
- nsresult
- CreateOrUpgradeDirectoryMetadataHelper::DoProcessOriginDirectories()
- {
- AssertIsOnIOThread();
- nsresult rv;
- nsCOMPtr<nsIFile> permanentStorageDir;
- for (uint32_t count = mOriginProps.Length(), index = 0;
- index < count;
- index++) {
- OriginProps& originProps = mOriginProps[index];
- if (mPersistent) {
- rv = CreateDirectoryMetadata(originProps.mDirectory,
- originProps.mTimestamp,
- originProps.mSuffix,
- originProps.mGroup,
- originProps.mOrigin,
- originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // Move internal origins to new persistent storage.
- if (QuotaManager::IsOriginInternal(originProps.mSpec)) {
- if (!permanentStorageDir) {
- permanentStorageDir =
- do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- QuotaManager* quotaManager = QuotaManager::Get();
- MOZ_ASSERT(quotaManager);
- const nsString& permanentStoragePath =
- quotaManager->GetStoragePath(PERSISTENCE_TYPE_PERSISTENT);
- rv = permanentStorageDir->InitWithPath(permanentStoragePath);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsString leafName;
- rv = originProps.mDirectory->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> newDirectory;
- rv = permanentStorageDir->Clone(getter_AddRefs(newDirectory));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = newDirectory->Append(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = newDirectory->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (exists) {
- QM_WARNING("Found %s in storage/persistent and storage/permanent !",
- NS_ConvertUTF16toUTF8(leafName).get());
- rv = originProps.mDirectory->Remove(/* recursive */ true);
- } else {
- rv = originProps.mDirectory->MoveTo(permanentStorageDir, EmptyString());
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- } else if (originProps.mNeedsRestore) {
- rv = CreateDirectoryMetadata(originProps.mDirectory,
- originProps.mTimestamp,
- originProps.mSuffix,
- originProps.mGroup,
- originProps.mOrigin,
- originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- } else if (!originProps.mIgnore) {
- nsCOMPtr<nsIBinaryOutputStream> stream;
- rv = GetBinaryOutputStream(originProps.mDirectory,
- NS_LITERAL_STRING(METADATA_FILE_NAME),
- kAppendFileFlag,
- getter_AddRefs(stream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- MOZ_ASSERT(stream);
- rv = stream->WriteBoolean(originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- }
- return NS_OK;
- }
- nsresult
- UpgradeDirectoryMetadataFrom1To2Helper::UpgradeMetadataFiles()
- {
- AssertIsOnIOThread();
- bool exists;
- nsresult rv = mDirectory->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- return NS_OK;
- }
- nsCOMPtr<nsISimpleEnumerator> entries;
- rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool hasMore;
- while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
- nsCOMPtr<nsISupports> entry;
- rv = entries->GetNext(getter_AddRefs(entry));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
- MOZ_ASSERT(originDir);
- bool isDirectory;
- rv = originDir->IsDirectory(&isDirectory);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isDirectory) {
- nsString leafName;
- rv = originDir->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
- QM_WARNING("Something (%s) in the storage directory that doesn't belong!",
- NS_ConvertUTF16toUTF8(leafName).get());
- }
- continue;
- }
- OriginProps* originProps;
- rv = AddOriginDirectory(originDir, &originProps);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- int64_t timestamp;
- nsCString group;
- nsCString origin;
- bool isApp;
- nsresult rv = GetDirectoryMetadata(originDir,
- ×tamp,
- group,
- origin,
- &isApp);
- if (NS_FAILED(rv)) {
- if (!mPersistent) {
- rv = GetLastModifiedTime(originDir, ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- originProps->mTimestamp = timestamp;
- }
- originProps->mNeedsRestore = true;
- } else {
- originProps->mTimestamp = timestamp;
- }
- }
- if (mOriginProps.IsEmpty()) {
- return NS_OK;
- }
- rv = ProcessOriginDirectories();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- UpgradeDirectoryMetadataFrom1To2Helper::GetDirectoryMetadata(
- nsIFile* aDirectory,
- int64_t* aTimestamp,
- nsACString& aGroup,
- nsACString& aOrigin,
- bool* aIsApp)
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aTimestamp);
- MOZ_ASSERT(aIsApp);
- nsCOMPtr<nsIBinaryInputStream> binaryStream;
- nsresult rv = GetBinaryInputStream(aDirectory,
- NS_LITERAL_STRING(METADATA_FILE_NAME),
- getter_AddRefs(binaryStream));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- uint64_t timestamp;
- rv = binaryStream->Read64(×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString group;
- rv = binaryStream->ReadCString(group);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCString origin;
- rv = binaryStream->ReadCString(origin);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool isApp;
- rv = binaryStream->ReadBoolean(&isApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aTimestamp = timestamp;
- aGroup = group;
- aOrigin = origin;
- *aIsApp = isApp;
- return NS_OK;
- }
- nsresult
- UpgradeDirectoryMetadataFrom1To2Helper::DoProcessOriginDirectories()
- {
- AssertIsOnIOThread();
- for (uint32_t count = mOriginProps.Length(), index = 0;
- index < count;
- index++) {
- OriginProps& originProps = mOriginProps[index];
- nsresult rv;
- if (originProps.mNeedsRestore) {
- rv = CreateDirectoryMetadata(originProps.mDirectory,
- originProps.mTimestamp,
- originProps.mSuffix,
- originProps.mGroup,
- originProps.mOrigin,
- originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- rv = CreateDirectoryMetadata2(originProps.mDirectory,
- originProps.mTimestamp,
- originProps.mSuffix,
- originProps.mGroup,
- originProps.mOrigin,
- originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsString oldName;
- rv = originProps.mDirectory->GetLeafName(oldName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsAutoCString originSanitized(originProps.mOrigin);
- SanitizeOriginString(originSanitized);
- NS_ConvertASCIItoUTF16 newName(originSanitized);
- if (!oldName.Equals(newName)) {
- rv = originProps.mDirectory->RenameTo(nullptr, newName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- }
- return NS_OK;
- }
- nsresult
- RestoreDirectoryMetadata2Helper::RestoreMetadata2File()
- {
- AssertIsOnIOThread();
- nsresult rv;
- OriginProps* originProps;
- rv = AddOriginDirectory(mDirectory, &originProps);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!mPersistent) {
- int64_t timestamp = INT64_MIN;
- rv = GetLastModifiedTime(mDirectory, ×tamp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- originProps->mTimestamp = timestamp;
- }
- rv = ProcessOriginDirectories();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- RestoreDirectoryMetadata2Helper::DoProcessOriginDirectories()
- {
- AssertIsOnIOThread();
- MOZ_ASSERT(mOriginProps.Length() == 1);
- OriginProps& originProps = mOriginProps[0];
- nsresult rv = CreateDirectoryMetadata2(originProps.mDirectory,
- originProps.mTimestamp,
- originProps.mSuffix,
- originProps.mGroup,
- originProps.mOrigin,
- originProps.mIsApp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- } // namespace quota
- } // namespace dom
- } // namespace mozilla
|