12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645 |
- From: uazo <uazo@users.noreply.github.com>
- Date: Fri, 13 Aug 2021 17:10:47 +0000
- Subject: Experimental user scripts support
- Activate the user scripts functionality for Android,
- as it is available in the Desktop version.
- It is possible to add user scripts in two ways: by
- selecting files from the picker in the settings or
- downloading the scripts and opening them from downloads
- (only if such files end with '.user.js').
- New imported scripts are disabled by default: they
- can be activated via the UI.
- Parsed user script headers are: name, version, description,
- include, exclude, match, exclude_match (only http and
- https), run-at (document-start, document-end,
- document-idle), homepage, url_source
- The UI also allows you to see the source of the script.
- See also: components/user_scripts/README.md
- Need: Adds-support-for-writing-URIs.patch
- ---
- chrome/android/BUILD.gn | 5 +
- .../android/java/res/xml/main_preferences.xml | 5 +
- .../browser/download/DownloadUtils.java | 6 +
- .../init/ProcessInitializationHandler.java | 3 +
- chrome/android/java_sources.gni | 3 +
- chrome/browser/BUILD.gn | 5 +
- chrome/browser/about_flags.cc | 5 +
- .../browser/chrome_content_browser_client.cc | 3 +-
- chrome/browser/flag_descriptions.cc | 5 +
- chrome/browser/flag_descriptions.h | 3 +
- chrome/browser/prefs/browser_prefs.cc | 2 +
- chrome/browser/profiles/BUILD.gn | 1 +
- ...hrome_browser_main_extra_parts_profiles.cc | 3 +
- chrome/browser/profiles/profile_manager.cc | 9 +
- chrome/browser/profiles/renderer_updater.cc | 10 +-
- chrome/browser/profiles/renderer_updater.h | 1 +
- .../webui/chrome_web_ui_controller_factory.cc | 3 +
- chrome/chrome_paks.gni | 2 +
- chrome/common/renderer_configuration.mojom | 1 +
- chrome/renderer/BUILD.gn | 1 +
- .../chrome_content_renderer_client.cc | 37 +
- .../renderer/chrome_render_thread_observer.cc | 3 +
- components/components_strings.grd | 1 +
- components/user_scripts/README.md | 150 ++++
- components/user_scripts/android/BUILD.gn | 80 ++
- .../java/res/layout/accept_script_item.xml | 160 ++++
- .../java/res/layout/accept_script_list.xml | 10 +
- .../java/res/layout/scripts_preference.xml | 40 +
- .../android/java/res/values/dimens.xml | 11 +
- .../java/res/xml/userscripts_preferences.xml | 34 +
- .../user_scripts/UserScriptsUtils.java | 87 ++
- .../user_scripts/FragmentWindowAndroid.java | 90 ++
- .../user_scripts/IUserScriptsUtils.java | 22 +
- .../components/user_scripts/ScriptInfo.java | 37 +
- .../user_scripts/ScriptListBaseAdapter.java | 163 ++++
- .../user_scripts/ScriptListPreference.java | 171 ++++
- .../user_scripts/UserScriptsBridge.java | 212 +++++
- .../user_scripts/UserScriptsPreferences.java | 116 +++
- .../user_scripts/android/java_sources.gni | 18 +
- .../android/user_scripts_bridge.cc | 173 ++++
- .../android/user_scripts_bridge.h | 31 +
- components/user_scripts/browser/BUILD.gn | 82 ++
- .../user_scripts/browser/file_task_runner.cc | 40 +
- .../user_scripts/browser/file_task_runner.h | 34 +
- .../browser/resources/browser_resources.grd | 14 +
- .../browser/resources/user-script-ui/BUILD.gn | 12 +
- .../user-script-ui/user-scripts-ui.html | 14 +
- .../user-script-ui/user-scripts-ui.js | 9 +
- .../browser/ui/user_scripts_ui.cc | 147 ++++
- .../user_scripts/browser/ui/user_scripts_ui.h | 37 +
- .../browser/user_script_loader.cc | 716 ++++++++++++++++
- .../user_scripts/browser/user_script_loader.h | 169 ++++
- .../browser/user_script_pref_info.cc | 34 +
- .../browser/user_script_pref_info.h | 72 ++
- .../user_scripts/browser/user_script_prefs.cc | 278 ++++++
- .../user_scripts/browser/user_script_prefs.h | 62 ++
- .../browser/userscripts_browser_client.cc | 78 ++
- .../browser/userscripts_browser_client.h | 62 ++
- components/user_scripts/common/BUILD.gn | 49 ++
- components/user_scripts/common/constants.h | 21 +
- components/user_scripts/common/error_utils.cc | 54 ++
- components/user_scripts/common/error_utils.h | 24 +
- .../common/extension_message_generator.cc | 29 +
- .../common/extension_message_generator.h | 11 +
- .../user_scripts/common/extension_messages.cc | 40 +
- .../user_scripts/common/extension_messages.h | 70 ++
- components/user_scripts/common/host_id.cc | 31 +
- components/user_scripts/common/host_id.h | 35 +
- .../user_scripts/common/script_constants.h | 33 +
- components/user_scripts/common/url_pattern.cc | 803 ++++++++++++++++++
- components/user_scripts/common/url_pattern.h | 302 +++++++
- .../user_scripts/common/url_pattern_set.cc | 335 ++++++++
- .../user_scripts/common/url_pattern_set.h | 160 ++++
- components/user_scripts/common/user_script.cc | 329 +++++++
- components/user_scripts/common/user_script.h | 403 +++++++++
- .../common/user_scripts_features.cc | 32 +
- .../common/user_scripts_features.h | 34 +
- components/user_scripts/common/view_type.cc | 39 +
- components/user_scripts/common/view_type.h | 48 ++
- components/user_scripts/renderer/BUILD.gn | 67 ++
- .../renderer/extension_frame_helper.cc | 96 +++
- .../renderer/extension_frame_helper.h | 91 ++
- .../user_scripts/renderer/injection_host.cc | 12 +
- .../user_scripts/renderer/injection_host.h | 41 +
- .../renderer/resources/greasemonkey_api.js | 82 ++
- .../user_scripts_renderer_resources.grd | 14 +
- .../user_scripts/renderer/script_context.cc | 215 +++++
- .../user_scripts/renderer/script_context.h | 69 ++
- .../user_scripts/renderer/script_injection.cc | 343 ++++++++
- .../user_scripts/renderer/script_injection.h | 154 ++++
- .../renderer/script_injection_callback.cc | 25 +
- .../renderer/script_injection_callback.h | 38 +
- .../renderer/script_injection_manager.cc | 417 +++++++++
- .../renderer/script_injection_manager.h | 101 +++
- .../user_scripts/renderer/script_injector.h | 96 +++
- .../user_scripts/renderer/scripts_run_info.cc | 31 +
- .../user_scripts/renderer/scripts_run_info.h | 69 ++
- .../renderer/user_script_injector.cc | 228 +++++
- .../renderer/user_script_injector.h | 86 ++
- .../user_scripts/renderer/user_script_set.cc | 262 ++++++
- .../user_scripts/renderer/user_script_set.h | 101 +++
- .../renderer/user_script_set_manager.cc | 77 ++
- .../renderer/user_script_set_manager.h | 61 ++
- .../renderer/user_scripts_dispatcher.cc | 36 +
- .../renderer/user_scripts_dispatcher.h | 49 ++
- .../renderer/user_scripts_renderer_client.cc | 105 +++
- .../renderer/user_scripts_renderer_client.h | 37 +
- .../renderer/web_ui_injection_host.cc | 40 +
- .../renderer/web_ui_injection_host.h | 27 +
- .../strings/userscripts_strings.grdp | 57 ++
- tools/gritsettings/resource_ids.spec | 6 +
- 111 files changed, 9595 insertions(+), 2 deletions(-)
- create mode 100644 components/user_scripts/README.md
- create mode 100755 components/user_scripts/android/BUILD.gn
- create mode 100644 components/user_scripts/android/java/res/layout/accept_script_item.xml
- create mode 100644 components/user_scripts/android/java/res/layout/accept_script_list.xml
- create mode 100644 components/user_scripts/android/java/res/layout/scripts_preference.xml
- create mode 100755 components/user_scripts/android/java/res/values/dimens.xml
- create mode 100644 components/user_scripts/android/java/res/xml/userscripts_preferences.xml
- create mode 100755 components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
- create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
- create mode 100755 components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
- create mode 100644 components/user_scripts/android/java_sources.gni
- create mode 100644 components/user_scripts/android/user_scripts_bridge.cc
- create mode 100644 components/user_scripts/android/user_scripts_bridge.h
- create mode 100755 components/user_scripts/browser/BUILD.gn
- create mode 100755 components/user_scripts/browser/file_task_runner.cc
- create mode 100755 components/user_scripts/browser/file_task_runner.h
- create mode 100644 components/user_scripts/browser/resources/browser_resources.grd
- create mode 100644 components/user_scripts/browser/resources/user-script-ui/BUILD.gn
- create mode 100644 components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
- create mode 100644 components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
- create mode 100644 components/user_scripts/browser/ui/user_scripts_ui.cc
- create mode 100644 components/user_scripts/browser/ui/user_scripts_ui.h
- create mode 100755 components/user_scripts/browser/user_script_loader.cc
- create mode 100755 components/user_scripts/browser/user_script_loader.h
- create mode 100644 components/user_scripts/browser/user_script_pref_info.cc
- create mode 100644 components/user_scripts/browser/user_script_pref_info.h
- create mode 100644 components/user_scripts/browser/user_script_prefs.cc
- create mode 100644 components/user_scripts/browser/user_script_prefs.h
- create mode 100755 components/user_scripts/browser/userscripts_browser_client.cc
- create mode 100755 components/user_scripts/browser/userscripts_browser_client.h
- create mode 100755 components/user_scripts/common/BUILD.gn
- create mode 100755 components/user_scripts/common/constants.h
- create mode 100755 components/user_scripts/common/error_utils.cc
- create mode 100755 components/user_scripts/common/error_utils.h
- create mode 100755 components/user_scripts/common/extension_message_generator.cc
- create mode 100755 components/user_scripts/common/extension_message_generator.h
- create mode 100755 components/user_scripts/common/extension_messages.cc
- create mode 100755 components/user_scripts/common/extension_messages.h
- create mode 100755 components/user_scripts/common/host_id.cc
- create mode 100755 components/user_scripts/common/host_id.h
- create mode 100755 components/user_scripts/common/script_constants.h
- create mode 100755 components/user_scripts/common/url_pattern.cc
- create mode 100755 components/user_scripts/common/url_pattern.h
- create mode 100755 components/user_scripts/common/url_pattern_set.cc
- create mode 100755 components/user_scripts/common/url_pattern_set.h
- create mode 100755 components/user_scripts/common/user_script.cc
- create mode 100755 components/user_scripts/common/user_script.h
- create mode 100644 components/user_scripts/common/user_scripts_features.cc
- create mode 100644 components/user_scripts/common/user_scripts_features.h
- create mode 100755 components/user_scripts/common/view_type.cc
- create mode 100755 components/user_scripts/common/view_type.h
- create mode 100755 components/user_scripts/renderer/BUILD.gn
- create mode 100755 components/user_scripts/renderer/extension_frame_helper.cc
- create mode 100755 components/user_scripts/renderer/extension_frame_helper.h
- create mode 100755 components/user_scripts/renderer/injection_host.cc
- create mode 100755 components/user_scripts/renderer/injection_host.h
- create mode 100755 components/user_scripts/renderer/resources/greasemonkey_api.js
- create mode 100755 components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
- create mode 100755 components/user_scripts/renderer/script_context.cc
- create mode 100755 components/user_scripts/renderer/script_context.h
- create mode 100755 components/user_scripts/renderer/script_injection.cc
- create mode 100755 components/user_scripts/renderer/script_injection.h
- create mode 100755 components/user_scripts/renderer/script_injection_callback.cc
- create mode 100755 components/user_scripts/renderer/script_injection_callback.h
- create mode 100755 components/user_scripts/renderer/script_injection_manager.cc
- create mode 100755 components/user_scripts/renderer/script_injection_manager.h
- create mode 100755 components/user_scripts/renderer/script_injector.h
- create mode 100755 components/user_scripts/renderer/scripts_run_info.cc
- create mode 100755 components/user_scripts/renderer/scripts_run_info.h
- create mode 100755 components/user_scripts/renderer/user_script_injector.cc
- create mode 100755 components/user_scripts/renderer/user_script_injector.h
- create mode 100755 components/user_scripts/renderer/user_script_set.cc
- create mode 100755 components/user_scripts/renderer/user_script_set.h
- create mode 100755 components/user_scripts/renderer/user_script_set_manager.cc
- create mode 100755 components/user_scripts/renderer/user_script_set_manager.h
- create mode 100755 components/user_scripts/renderer/user_scripts_dispatcher.cc
- create mode 100755 components/user_scripts/renderer/user_scripts_dispatcher.h
- create mode 100755 components/user_scripts/renderer/user_scripts_renderer_client.cc
- create mode 100755 components/user_scripts/renderer/user_scripts_renderer_client.h
- create mode 100755 components/user_scripts/renderer/web_ui_injection_host.cc
- create mode 100755 components/user_scripts/renderer/web_ui_injection_host.h
- create mode 100755 components/user_scripts/strings/userscripts_strings.grdp
- diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
- --- a/chrome/android/BUILD.gn
- +++ b/chrome/android/BUILD.gn
- @@ -267,6 +267,10 @@ android_resources("chrome_app_java_resources") {
- "//third_party/androidx:androidx_preference_preference_java",
- "//third_party/androidx:androidx_recyclerview_recyclerview_java",
- ]
- +
- + # this need to be into android_resources("chrome_app_java_resources") section because
- + # android:java_resources are packed *_percent.pak and placed in the executable folder
- + deps += [ "//components/user_scripts/android:java_resources" ]
- }
-
- if (enable_vr) {
- @@ -557,6 +561,7 @@ android_library("chrome_java") {
- "//components/ukm/android:java",
- "//components/url_formatter/android:url_formatter_java",
- "//components/user_prefs/android:java",
- + "//components/user_scripts/android:java",
- "//components/variations/android:variations_java",
- "//components/version_info/android:version_constants_java",
- "//components/viz/common:common_java",
- diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
- --- a/chrome/android/java/res/xml/main_preferences.xml
- +++ b/chrome/android/java/res/xml/main_preferences.xml
- @@ -86,6 +86,11 @@
- android:key="useragent_settings"
- android:order="20"
- android:title="@string/prefs_useragent_settings"/>
- + <Preference
- + android:fragment="org.chromium.components.user_scripts.UserScriptsPreferences"
- + android:key="userscripts_settings"
- + android:order="20"
- + android:title="@string/prefs_userscripts_settings"/>
- <Preference
- android:fragment="org.chromium.chrome.browser.language.settings.LanguageSettings"
- android:key="languages"
- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
- --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
- +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
- @@ -70,6 +70,7 @@ import org.chromium.content_public.browser.BrowserStartupController;
- import org.chromium.content_public.browser.LoadUrlParams;
- import org.chromium.ui.base.DeviceFormFactor;
- import org.chromium.ui.widget.Toast;
- +import org.chromium.chrome.browser.user_scripts.UserScriptsUtils;
-
- import java.io.File;
-
- @@ -420,6 +421,11 @@ public class DownloadUtils {
- public static boolean openFile(String filePath, String mimeType, String downloadGuid,
- OTRProfileID otrProfileID, String originalUrl, String referrer,
- @DownloadOpenSource int source, Context context) {
- + if (UserScriptsUtils.getInstance().openFile(filePath, mimeType, downloadGuid,
- + originalUrl, referrer,
- + getUriForItem(filePath))) {
- + return true;
- + }
- DownloadMetrics.recordDownloadOpen(source, mimeType);
- DownloadManagerService service = DownloadManagerService.getDownloadManagerService();
-
- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
- --- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
- +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
- @@ -122,6 +122,8 @@ import java.util.Date;
- import java.util.List;
- import java.util.Locale;
-
- +import org.chromium.chrome.browser.user_scripts.UserScriptsUtils;
- +
- /**
- * Handles the initialization dependences of the browser process. This is meant to handle the
- * initialization that is not tied to any particular Activity, and the logic that should only be
- @@ -322,6 +324,7 @@ public class ProcessInitializationHandler {
-
- DefaultBrowserInfo.initBrowserFetcher();
-
- + UserScriptsUtils.Initialize();
- AfterStartupTaskUtils.setStartupComplete();
-
- PartnerBrowserCustomizations.getInstance().setOnInitializeAsyncFinished(
- diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
- --- a/chrome/android/java_sources.gni
- +++ b/chrome/android/java_sources.gni
- @@ -22,6 +22,7 @@ import("//components/feed/features.gni")
- import("//components/offline_pages/buildflags/features.gni")
- import("//components/omnibox/browser/test_java_sources.gni")
- import("//device/vr/buildflags/buildflags.gni")
- +import("//components/user_scripts/android/java_sources.gni")
-
- chrome_java_sources += public_autofill_assistant_java_sources
-
- @@ -59,3 +60,5 @@ if (enable_arcore) {
- "java/src/org/chromium/chrome/browser/vr/ArDelegateImpl.java",
- ]
- }
- +
- +chrome_java_sources += userscripts_java_sources
- diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
- --- a/chrome/browser/BUILD.gn
- +++ b/chrome/browser/BUILD.gn
- @@ -3494,6 +3494,11 @@ static_library("browser") {
- ]
- deps += [ "//chrome/android/modules/dev_ui/provider:native" ]
- }
- + deps += [
- + "//components/user_scripts/common",
- + "//components/user_scripts/browser",
- + "//components/user_scripts/android",
- + ]
- } else {
- #!is_android
- if (!is_chromeos_lacros) {
- diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
- --- a/chrome/browser/about_flags.cc
- +++ b/chrome/browser/about_flags.cc
- @@ -150,6 +150,7 @@
- #include "components/translate/core/browser/translate_ranker_impl.h"
- #include "components/translate/core/common/translate_util.h"
- #include "components/ui_devtools/switches.h"
- +#include "components/user_scripts/common/user_scripts_features.h"
- #include "components/version_info/version_info.h"
- #include "components/viz/common/features.h"
- #include "components/viz/common/switches.h"
- @@ -6713,6 +6714,10 @@ const FeatureEntry kFeatureEntries[] = {
- FEATURE_VALUE_TYPE(chromeos::features::kClipboardHistoryScreenshotNudge)},
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
- + {"enable-userscripts-log", flag_descriptions::kEnableLoggingUserScriptsName,
- + flag_descriptions::kEnableLoggingUserScriptsDescription, kOsDesktop | kOsAndroid,
- + FEATURE_VALUE_TYPE(user_scripts::features::kEnableLoggingUserScripts)},
- +
- #if BUILDFLAG(IS_WIN)
- {"enable-media-foundation-video-capture",
- flag_descriptions::kEnableMediaFoundationVideoCaptureName,
- diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
- --- a/chrome/browser/chrome_content_browser_client.cc
- +++ b/chrome/browser/chrome_content_browser_client.cc
- @@ -4682,7 +4682,8 @@ ChromeContentBrowserClient::CreateURLLoaderThrottles(
- chrome::mojom::DynamicParams dynamic_params = {
- profile->GetPrefs()->GetBoolean(prefs::kForceGoogleSafeSearch),
- profile->GetPrefs()->GetInteger(prefs::kForceYouTubeRestrict),
- - profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps)};
- + profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps),
- + false /*-> allow_userscript, don't care */};
- result.push_back(std::make_unique<GoogleURLLoaderThrottle>(
- #if BUILDFLAG(IS_ANDROID)
- client_data_header, is_tab_large_enough,
- diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
- --- a/chrome/browser/flag_descriptions.cc
- +++ b/chrome/browser/flag_descriptions.cc
- @@ -5602,6 +5602,11 @@ const char kDesktopDetailedLanguageSettingsDescription[] =
- #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
- // BUILDFLAG(IS_FUCHSIA)
-
- +const char kEnableLoggingUserScriptsName[] = "Enable logging user scripts component";
- +const char kEnableLoggingUserScriptsDescription[] =
- + "Enables logging for troubleshooting feature. "
- + "Enabling logs may make browsing slower.";
- +
- #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
- const char kWebShareName[] = "Web Share";
- const char kWebShareDescription[] =
- diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
- --- a/chrome/browser/flag_descriptions.h
- +++ b/chrome/browser/flag_descriptions.h
- @@ -3244,6 +3244,9 @@ extern const char kDesktopDetailedLanguageSettingsDescription[];
- #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
- // defined (OS_FUCHSIA)
-
- +extern const char kEnableLoggingUserScriptsName[];
- +extern const char kEnableLoggingUserScriptsDescription[];
- +
- #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
- extern const char kWebShareName[];
- extern const char kWebShareDescription[];
- diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
- --- a/chrome/browser/prefs/browser_prefs.cc
- +++ b/chrome/browser/prefs/browser_prefs.cc
- @@ -236,6 +236,7 @@
- #include "components/ntp_tiles/popular_sites_impl.h"
- #include "components/permissions/contexts/geolocation_permission_context_android.h"
- #include "components/query_tiles/tile_service_prefs.h"
- +#include "components/user_scripts/browser/user_script_prefs.h"
- #else // BUILDFLAG(IS_ANDROID)
- #include "chrome/browser/cart/cart_service.h"
- #include "chrome/browser/device_api/device_service_impl.h"
- @@ -1275,6 +1276,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
- translate::TranslatePrefs::RegisterProfilePrefs(registry);
- omnibox::RegisterProfilePrefs(registry);
- ZeroSuggestProvider::RegisterProfilePrefs(registry);
- + user_scripts::UserScriptsPrefs::RegisterProfilePrefs(registry);
-
- #if BUILDFLAG(ENABLE_SESSION_SERVICE)
- RegisterSessionServiceLogProfilePrefs(registry);
- diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
- --- a/chrome/browser/profiles/BUILD.gn
- +++ b/chrome/browser/profiles/BUILD.gn
- @@ -45,6 +45,7 @@ source_set("profile") {
- "//components/profile_metrics",
- "//components/sync/driver",
- "//components/variations",
- + "//components/user_scripts/browser",
- "//content/public/browser",
- "//extensions/buildflags",
- ]
- diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
- --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
- +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
- @@ -235,6 +235,8 @@
- #include "chrome/browser/ui/cocoa/screentime/screentime_features.h"
- #endif
-
- +#include "components/user_scripts/browser/userscripts_browser_client.h"
- +
- namespace chrome {
-
- void AddProfilesExtraParts(ChromeBrowserMainParts* main_parts) {
- @@ -556,6 +558,7 @@ void ChromeBrowserMainExtraPartsProfiles::
- #endif
- WebDataServiceFactory::GetInstance();
- webrtc_event_logging::WebRtcEventLogManagerKeyedServiceFactory::GetInstance();
- + user_scripts::UserScriptsBrowserClient::GetInstance();
- }
-
- void ChromeBrowserMainExtraPartsProfiles::PreProfileInit() {
- diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
- --- a/chrome/browser/profiles/profile_manager.cc
- +++ b/chrome/browser/profiles/profile_manager.cc
- @@ -113,6 +113,8 @@
- #include "extensions/common/manifest.h"
- #endif
-
- +#include "components/user_scripts/browser/userscripts_browser_client.h"
- +
- #if BUILDFLAG(ENABLE_SESSION_SERVICE)
- #include "chrome/browser/sessions/app_session_service_factory.h"
- #include "chrome/browser/sessions/session_service_factory.h"
- @@ -1635,6 +1637,13 @@ void ProfileManager::DoFinalInitForServices(Profile* profile,
- }
-
- #endif
- +
- + user_scripts::UserScriptsBrowserClient* userscript_client =
- + user_scripts::UserScriptsBrowserClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->SetProfile(profile);
- + }
- +
- #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
- // Initialization needs to happen after extension system initialization (for
- // extension::ManagementPolicy) and InitProfileUserPrefs (for setting the
- diff --git a/chrome/browser/profiles/renderer_updater.cc b/chrome/browser/profiles/renderer_updater.cc
- --- a/chrome/browser/profiles/renderer_updater.cc
- +++ b/chrome/browser/profiles/renderer_updater.cc
- @@ -29,6 +29,8 @@
- #include "chrome/browser/ash/login/signin/oauth2_login_manager_factory.h"
- #endif
-
- +#include "components/user_scripts/browser/user_script_prefs.h"
- +
- namespace {
-
- #if BUILDFLAG(ENABLE_EXTENSIONS)
- @@ -75,6 +77,7 @@ RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
- force_google_safesearch_.Init(prefs::kForceGoogleSafeSearch, pref_service);
- force_youtube_restrict_.Init(prefs::kForceYouTubeRestrict, pref_service);
- allowed_domains_for_apps_.Init(prefs::kAllowedDomainsForApps, pref_service);
- + activate_userscripts_.Init(user_scripts::prefs::kUserScriptsEnabled, pref_service);
-
- pref_change_registrar_.Init(pref_service);
- pref_change_registrar_.Add(
- @@ -89,6 +92,10 @@ RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
- prefs::kAllowedDomainsForApps,
- base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
- base::Unretained(this)));
- + pref_change_registrar_.Add(
- + user_scripts::prefs::kUserScriptsEnabled,
- + base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
- + base::Unretained(this)));
- }
-
- RendererUpdater::~RendererUpdater() {
- @@ -236,5 +243,6 @@ void RendererUpdater::UpdateRenderer(
- ->SetConfiguration(chrome::mojom::DynamicParams::New(
- force_google_safesearch_.GetValue(),
- force_youtube_restrict_.GetValue(),
- - allowed_domains_for_apps_.GetValue()));
- + allowed_domains_for_apps_.GetValue(),
- + activate_userscripts_.GetValue()));
- }
- diff --git a/chrome/browser/profiles/renderer_updater.h b/chrome/browser/profiles/renderer_updater.h
- --- a/chrome/browser/profiles/renderer_updater.h
- +++ b/chrome/browser/profiles/renderer_updater.h
- @@ -83,6 +83,7 @@ class RendererUpdater : public KeyedService,
-
- // Prefs that we sync to the renderers.
- BooleanPrefMember force_google_safesearch_;
- + BooleanPrefMember activate_userscripts_;
- IntegerPrefMember force_youtube_restrict_;
- StringPrefMember allowed_domains_for_apps_;
-
- diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
- --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
- +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
- @@ -88,6 +88,7 @@
- #include "components/security_interstitials/content/urls.h"
- #include "components/signin/public/base/signin_buildflags.h"
- #include "components/site_engagement/content/site_engagement_service.h"
- +#include "components/user_scripts/browser/ui/user_scripts_ui.h"
- #include "content/public/browser/web_contents.h"
- #include "content/public/browser/web_ui.h"
- #include "content/public/common/content_client.h"
- @@ -773,6 +774,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
- return &NewWebUI<UserActionsUI>;
- if (url.host_piece() == chrome::kChromeUIVersionHost)
- return &NewWebUI<VersionUI>;
- + if (url.host_piece() == user_scripts::kChromeUIUserScriptsHost)
- + return &NewWebUI<user_scripts::UserScriptsUI>;
-
- #if !BUILDFLAG(IS_ANDROID)
- #if !BUILDFLAG(IS_CHROMEOS)
- diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
- --- a/chrome/chrome_paks.gni
- +++ b/chrome/chrome_paks.gni
- @@ -110,6 +110,7 @@ template("chrome_extra_paks") {
- "$root_gen_dir/third_party/blink/public/resources/inspector_overlay_resources.pak",
- "$root_gen_dir/ui/resources/webui_generated_resources.pak",
- "$root_gen_dir/ui/resources/webui_resources.pak",
- + "$root_gen_dir/chrome/userscripts_browser_resources.pak",
- ]
- deps = [
- "//base/tracing/protos:chrome_track_event_resources",
- @@ -128,6 +129,7 @@ template("chrome_extra_paks") {
- "//third_party/blink/public:devtools_inspector_resources",
- "//third_party/blink/public:resources",
- "//ui/resources",
- + "//components/user_scripts/browser:userscripts_browser_resources_grit",
- ]
- if (defined(invoker.deps)) {
- deps += invoker.deps
- diff --git a/chrome/common/renderer_configuration.mojom b/chrome/common/renderer_configuration.mojom
- --- a/chrome/common/renderer_configuration.mojom
- +++ b/chrome/common/renderer_configuration.mojom
- @@ -12,6 +12,7 @@ struct DynamicParams {
- bool force_safe_search = true;
- int32 youtube_restrict = 0;
- string allowed_domains_for_apps;
- + bool allow_userscript = false;
- };
-
- interface ChromeOSListener {
- diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
- --- a/chrome/renderer/BUILD.gn
- +++ b/chrome/renderer/BUILD.gn
- @@ -142,6 +142,7 @@ static_library("renderer") {
- "//components/feed:feature_list",
- "//components/feed/content/renderer:feed_renderer",
- "//components/history_clusters/core",
- + "//components/user_scripts/renderer",
- "//components/network_hints/renderer",
- "//components/no_state_prefetch/common",
- "//components/no_state_prefetch/renderer",
- diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
- --- a/chrome/renderer/chrome_content_renderer_client.cc
- +++ b/chrome/renderer/chrome_content_renderer_client.cc
- @@ -243,6 +243,9 @@
- #include "chrome/renderer/supervised_user/supervised_user_error_page_controller_delegate_impl.h"
- #endif
-
- +#include "components/user_scripts/common/user_scripts_features.h"
- +#include "components/user_scripts/renderer/user_scripts_renderer_client.h"
- +
- using autofill::AutofillAgent;
- using autofill::PasswordAutofillAgent;
- using autofill::PasswordGenerationAgent;
- @@ -415,6 +418,12 @@ void ChromeContentRendererClient::RenderThreadStarted() {
- WebString::FromASCII(extensions::kExtensionScheme));
- #endif
-
- + user_scripts::UserScriptsRendererClient* userscript_client =
- + user_scripts::UserScriptsRendererClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->RenderThreadStarted();
- + }
- +
- #if BUILDFLAG(ENABLE_SPELLCHECK)
- if (!spellcheck_)
- InitSpellCheck();
- @@ -551,6 +560,13 @@ void ChromeContentRendererClient::RenderFrameCreated(
- render_frame, registry);
- #endif
-
- + user_scripts::UserScriptsRendererClient* userscript_client =
- + user_scripts::UserScriptsRendererClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->RenderFrameCreated(
- + render_frame, registry);
- + }
- +
- #if BUILDFLAG(ENABLE_PLUGINS)
- new PepperHelper(render_frame);
- #endif
- @@ -1532,7 +1548,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentStart(
- ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentStart(
- render_frame);
- // |render_frame| might be dead by now.
- + static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
- #endif
- + user_scripts::UserScriptsRendererClient* userscript_client =
- + user_scripts::UserScriptsRendererClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->RunScriptsAtDocumentStart(
- + render_frame);
- + }
- }
-
- void ChromeContentRendererClient::RunScriptsAtDocumentEnd(
- @@ -1541,7 +1564,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentEnd(
- ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentEnd(
- render_frame);
- // |render_frame| might be dead by now.
- + static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
- #endif
- + user_scripts::UserScriptsRendererClient* userscript_client =
- + user_scripts::UserScriptsRendererClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->RunScriptsAtDocumentEnd(
- + render_frame);
- + }
- }
-
- void ChromeContentRendererClient::RunScriptsAtDocumentIdle(
- @@ -1550,7 +1580,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentIdle(
- ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentIdle(
- render_frame);
- // |render_frame| might be dead by now.
- + static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
- #endif
- + user_scripts::UserScriptsRendererClient* userscript_client =
- + user_scripts::UserScriptsRendererClient::GetInstance();
- + if (userscript_client) {
- + userscript_client->RunScriptsAtDocumentIdle(
- + render_frame);
- + }
- }
-
- void ChromeContentRendererClient::
- diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
- --- a/chrome/renderer/chrome_render_thread_observer.cc
- +++ b/chrome/renderer/chrome_render_thread_observer.cc
- @@ -58,6 +58,8 @@
- #include "third_party/blink/public/web/web_security_policy.h"
- #include "third_party/blink/public/web/web_view.h"
-
- +#include "components/user_scripts/renderer/user_scripts_renderer_client.h"
- +
- #if BUILDFLAG(ENABLE_EXTENSIONS)
- #include "chrome/renderer/extensions/extension_localization_peer.h"
- #endif
- @@ -255,6 +257,7 @@ void ChromeRenderThreadObserver::SetInitialConfiguration(
- void ChromeRenderThreadObserver::SetConfiguration(
- chrome::mojom::DynamicParamsPtr params) {
- *GetDynamicConfigParams() = std::move(*params);
- + user_scripts::UserScriptsRendererClient::GetInstance()->ConfigurationUpdated();
- }
-
- void ChromeRenderThreadObserver::SetContentSettingRules(
- diff --git a/components/components_strings.grd b/components/components_strings.grd
- --- a/components/components_strings.grd
- +++ b/components/components_strings.grd
- @@ -338,6 +338,7 @@
- <part file="undo_strings.grdp" />
- <part file="version_ui_strings.grdp" />
- <part file="webapps_strings.grdp" />
- + <part file="user_scripts/strings/userscripts_strings.grdp" />
-
- <if expr="not is_ios">
- <part file="management_strings.grdp" />
- diff --git a/components/user_scripts/README.md b/components/user_scripts/README.md
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/README.md
- @@ -0,0 +1,150 @@
- +# Userscripts support for Bromite
- +
- +UserScript support is under user setting currently disabled by default: when disabled, no code that can impact navigation safety is active.
- +
- +Activation allows the use of userscripts in Bromite. It is possible to add them in two ways:
- +- by selecting files from the file picker in the settings
- +- downloading the scripts and opening it from downloads (only if ends with .user.js)
- +The new imported scripts are disabled by default: they can be activated via the menu visible on the ui.
- +
- +Userscript support is currently the one provided by the desktop version. The enabled headers are:
- +
- +- `@name`
- +- `@version`
- +- `@description`
- +- `@url` or `@homepage`
- +- `@include`, `@exclude`, `@match`, `@exclude_match` for the url pattern (only http e https)
- +- `@run-at`
- + - `document-start`
- + Start the script after the documentElement is created, but before anything else happens
- + - `document-end`
- + Start the script after the entire document is parsed. Same as DOMContentLoaded
- + - `document-idle`
- + Start the script sometime after DOMContentLoaded, as soon as the document is "idle". Currently this uses the simple heuristic of: min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no particular injection point is guaranteed
- +
- +The url-patterns are so defined:
- +```
- +// <url-pattern> := <scheme>://<host><port><path> | '<all_urls>'
- +// <scheme> := '*' | 'http' | 'https'
- +// <host> := '*' | <IPv4 address> | [<IPv6 address>] |
- +// '*.' <anychar except '/' and '*'>+
- +// <port> := [':' ('*' | <port number between 0 and 65535>)]
- +// <path> := '/' <any chars>
- +//
- +// * Host is not used when the scheme is 'file'.
- +// * The path can have embedded '*' characters which act as glob wildcards.
- +// * '<all_urls>' is a special pattern that matches any valid URL that contains
- +// a valid scheme (as specified by valid_schemes_).
- +// * The '*' scheme pattern excludes file URLs.
- +//
- +// Examples of valid patterns:
- +// - http://*/*
- +// - http://*/foo*
- +// - https://*.google.com/foo*bar
- +// - file://monkey*
- +// - http://127.0.0.1/*
- +// - http://[2607:f8b0:4005:805::200e]/*
- +//
- +// Examples of invalid patterns:
- +// - http://* -- path not specified
- +// - http://*foo/bar -- * not allowed as substring of host component
- +// - http://foo.*.bar/baz -- * must be first component
- +// - http:/bar -- scheme separator not found
- +// - foo://* -- invalid scheme
- +// - chrome:// -- we don't support chrome internal URLs
- +```
- +
- +---
- +## **Beware of the scripts you enter: they can be a source of security problems, you are injecting code into your navigation**.
- +---
- +## Technical aspects
- +
- +`user_scripts/common` and `user_scripts/renderer` is the closest to the current chromium code: few changes there, mostly eliminated the superfluous extension related code.
- +
- +In `user_scripts/browser` you find the actual management (in the browser process) and in `android` basically the settings ui.
- +
- +At startup it tries to read all files in the `userscripts folder` in `/data/user/0/org.bromite.bromite/app_chrome/userscripts`: this could be a critical process because a crash would prevent the browser from opening, and that's why there is a crash counter that automatically disables the feature after three attempts if it encounters a problem during startup.
- +
- +The java ui allows userscript management: addition, deletion and activation/deactivation. Any errors while reading the scripts are presented to the user: scripts with errors cannot be activated. There is also the visualization of the script source and the open of its homepage (if foreseen) in incognito browsing.
- +
- +There is also support for an on-line help at https://github.com/bromite/bromite/wiki/UserScripts.
- +
- +
- +Entry points are `components/user_scripts/browser/userscripts_browser_client.cc` and `components/user_scripts/renderer/user_scripts_renderer_client.cc`: the two attach to the browser and the renderer process.
- +
- +for userscripts_browser_client.cc
- +- `chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc`
- +builds the browser side. also gpu process passes here, but the call is avoided.
- +
- +- `chrome/browser/profiles/profile_manager.cc`
- +set the profile
- +
- +for renderer/user_scripts_renderer_client.cc
- +- `chrome/renderer/chrome_content_renderer_client.cc`
- +at the renderer side
- +
- +the two sides have a life of their own and can communicate only via ipc, the renderer does not have access to the disk while the browser does only via its own task runner file (`components/user_scripts/browser/file_task_runner.cc`).
- +
- +## BROWSER PROCESS
- +Once the profile is set, it istance the `components/user_scripts/browser/user_script_loader.cc` and starts it.
- +This loads all the files in the folder into the runner file and interprets them. Control then passes to
- +`components/user_scripts/browser/user_script_prefs.cc` which verifies through the default profile what the user wants active.
- +At that point it passes through IPC to the renderer only the list of active scripts.
- +
- +## RENDERER PROCESS
- +Each time a frame is created, the script pattern is checked and it is injected into the three stages (START, IDLE, END).
- +The logic is all in `components/user_scripts/renderer/user_script_set.cc`.
- +
- +## Simple example
- +Here you find a working example that eliminates the google popup, useful in always incognito:
- +```
- +// ==UserScript==
- +// @name Remove Google Consent
- +// @namespace google
- +// @version 0.0.1
- +// @description Autohide Accepts Cookies
- +// @author uazo
- +// @match https://*.google.com/search?*
- +// @grant none
- +// @run-at document-start
- +// ==/UserScript==
- +
- +(function() {
- + 'use strict';
- +
- + var prepareStyleSheet = function() {
- + var style = document.createElement('style');
- + //style.setAttribute('media', 'screen');
- + style.appendChild(document.createTextNode(''));
- + document.head.appendChild(style);
- + style.sheet.insertRule('body { overflow:scroll !important;position:unset !important }');
- + };
- +
- + var hideConsent = function() {
- + document.getElementById("lb").style.display = "none";
- + };
- +
- + var checkElementThenRun = function(selector, func) {
- + var el = document.querySelector(selector);
- + if ( el == null ) {
- + if (window.requestAnimationFrame != undefined) {
- + window.requestAnimationFrame(function(){ checkElementThenRun(selector, func)});
- + } else {
- + document.addEventListener('readystatechange', function(e) {
- + if (document.readyState == 'complete') {
- + func();
- + }
- + });
- + }
- + } else {
- + func();
- + }
- + }
- +
- + document.cookie = 'CONSENT=YES+IT.it+V13+BX;domain=.google.com';
- + checkElementThenRun('head', prepareStyleSheet);
- + checkElementThenRun('#lb', hideConsent);
- +})();
- +```
- +
- +See also: https://github.com/bromite/bromite/pull/857
- diff --git a/components/user_scripts/android/BUILD.gn b/components/user_scripts/android/BUILD.gn
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/android/BUILD.gn
- @@ -0,0 +1,80 @@
- +# This file is part of Bromite.
- +
- +# Bromite is free software: you can redistribute it and/or modify
- +# it under the terms of the GNU General Public License as published by
- +# the Free Software Foundation, either version 3 of the License, or
- +# (at your option) any later version.
- +
- +# Bromite is distributed in the hope that it will be useful,
- +# but WITHOUT ANY WARRANTY; without even the implied warranty of
- +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- +# GNU General Public License for more details.
- +
- +# You should have received a copy of the GNU General Public License
- +# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +
- +import("//build/config/android/rules.gni")
- +
- +generate_jni("user_scripts_jni_headers") {
- + sources = [ "java/src/org/chromium/components/user_scripts/UserScriptsBridge.java" ]
- +}
- +
- +android_resources("java_resources") {
- + sources = [
- + "java/res/xml/userscripts_preferences.xml",
- + "java/res/layout/accept_script_item.xml",
- + "java/res/layout/accept_script_list.xml",
- + "java/res/layout/scripts_preference.xml",
- + "java/res/values/dimens.xml"
- + ]
- +
- + deps = [
- + "//components/browser_ui/strings/android:browser_ui_strings_grd",
- + "//components/browser_ui/styles/android:java_resources",
- + "//components/strings:components_strings_grd",
- + "//ui/android:ui_java_resources",
- + ]
- +}
- +
- +android_library("java") {
- + sources = [
- + "java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java",
- + "java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java",
- + "java/src/org/chromium/components/user_scripts/UserScriptsBridge.java",
- + "java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java",
- + "java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java",
- + "java/src/org/chromium/components/user_scripts/ScriptListPreference.java",
- + "java/src/org/chromium/components/user_scripts/ScriptInfo.java",
- + ]
- + deps = [
- + ":java_resources",
- + "//base:base_java",
- + "//base:jni_java",
- + "//components/embedder_support/android:browser_context_java",
- + "//components/browser_ui/settings/android:java",
- + "//components/browser_ui/widget/android:java",
- + "//content/public/android:content_java",
- + "//components/prefs/android:java",
- + "//third_party/android_deps:android_support_v7_appcompat_java",
- + "//third_party/androidx:androidx_annotation_annotation_java",
- + "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
- + "//third_party/androidx:androidx_preference_preference_java",
- + "//ui/android:ui_java",
- + ]
- + resources_package = "org.chromium.components.user_scripts"
- + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
- +}
- +
- +source_set("android") {
- + sources = [
- + "user_scripts_bridge.cc",
- + "user_scripts_bridge.h",
- + ]
- + deps = [
- + ":user_scripts_jni_headers",
- + "//base",
- + "//components/user_scripts/browser",
- + "//components/permissions",
- + "//content/public/browser",
- + ]
- +}
- diff --git a/components/user_scripts/android/java/res/layout/accept_script_item.xml b/components/user_scripts/android/java/res/layout/accept_script_item.xml
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/res/layout/accept_script_item.xml
- @@ -0,0 +1,160 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<!-- Copyright 2017 The Chromium Authors. All rights reserved.
- + Use of this source code is governed by a BSD-style license that can be
- + found in the LICENSE file. -->
- +
- +<LinearLayout
- + xmlns:android="http://schemas.android.com/apk/res/android"
- + xmlns:app="http://schemas.android.com/apk/res-auto"
- + xmlns:tools="http://schemas.android.com/tools"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:orientation="vertical"
- + android:layout_gravity="center_vertical" >
- +
- + <LinearLayout
- + android:layout_marginStart="0dp"
- + android:paddingStart="@dimen/draggable_list_item_padding"
- + android:paddingEnd="@dimen/draggable_list_item_padding"
- + style="@style/ListItemContainer">
- +
- + <Switch
- + android:id="@+id/switch_widget"
- + android:layout_width="wrap_content"
- + android:layout_height="wrap_content"
- + android:paddingEnd="15dp"
- + android:focusable="false"
- + android:background="@null" />
- +
- + <LinearLayout
- + android:layout_width="0dp"
- + android:layout_height="wrap_content"
- + android:layout_weight="1"
- + android:orientation="vertical"
- + android:layout_gravity="center_vertical" >
- +
- + <TextView
- + android:id="@+id/title"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + style="@style/PreferenceTitle" />
- +
- + <TextView
- + android:id="@+id/description"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + style="@style/PreferenceSummary" />
- +
- + <LinearLayout
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:layout_weight="1"
- + android:paddingTop="5dp"
- + android:layout_gravity="center_vertical" >
- +
- + <TextView
- + android:layout_width="wrap_content"
- + android:layout_height="wrap_content"
- + android:paddingEnd="5dp"
- + style="@style/PreferenceSummary"
- + android:text="@string/scripts_item_version"
- + android:textStyle="bold" />
- +
- + <TextView
- + android:id="@+id/version"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + style="@style/PreferenceSummary" />
- +
- + </LinearLayout>
- +
- + <LinearLayout
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:layout_weight="1"
- + android:layout_gravity="center_vertical" >
- +
- + <TextView
- + android:layout_width="wrap_content"
- + android:layout_height="wrap_content"
- + android:paddingEnd="5dp"
- + style="@style/PreferenceSummary"
- + android:text="@string/scripts_item_filename"
- + android:textStyle="bold" />
- +
- + <TextView
- + android:id="@+id/file"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + style="@style/PreferenceSummary" />
- +
- + </LinearLayout>
- +
- + <LinearLayout
- + android:id="@+id/url_container"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:layout_weight="1"
- + android:layout_gravity="center_vertical" >
- +
- + <TextView
- + android:layout_width="wrap_content"
- + android:layout_height="wrap_content"
- + android:paddingEnd="5dp"
- + style="@style/PreferenceSummary"
- + android:text="@string/scripts_item_url"
- + android:textStyle="bold" />
- +
- + <TextView
- + android:id="@+id/url"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:autoLink="web"
- + android:focusable="true"
- + android:linksClickable="true" />
- +
- + </LinearLayout>
- +
- + </LinearLayout>
- +
- + <org.chromium.components.browser_ui.widget.listmenu.ListMenuButton
- + android:id="@+id/more"
- + android:layout_width="wrap_content"
- + android:layout_height="match_parent"
- + android:paddingStart="@dimen/default_list_row_padding"
- + android:paddingEnd="@dimen/default_list_row_padding"
- + android:background="@null"
- + android:src="@drawable/ic_more_vert_24dp"
- + app:menuMaxWidth="@dimen/pref_scripts_item_popup_width"
- + app:tint="@color/default_icon_color_tint_list"
- + tools:ignore="ContentDescription" />
- +
- + </LinearLayout>
- +
- + <LinearLayout
- + android:id="@+id/error_layout"
- + android:layout_marginStart="0dp"
- + android:paddingStart="@dimen/draggable_list_item_padding"
- + android:paddingEnd="2dp"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + style="@style/ListItemContainer">
- +
- + <org.chromium.ui.widget.ChromeImageView
- + android:id="@+id/icon"
- + android:layout_width="40dp"
- + android:layout_height="wrap_content"
- + android:paddingEnd="15dp"
- + android:adjustViewBounds="true"
- + android:importantForAccessibility="no"
- + app:srcCompat="@drawable/ic_error_outline_red_24dp"/>
- +
- + <TextView
- + android:id="@+id/error"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:textColor="#F00"
- + style="@style/PreferenceSummary" />
- +
- + </LinearLayout>
- +</LinearLayout>
- \ No newline at end of file
- diff --git a/components/user_scripts/android/java/res/layout/accept_script_list.xml b/components/user_scripts/android/java/res/layout/accept_script_list.xml
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/res/layout/accept_script_list.xml
- @@ -0,0 +1,10 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<!-- Copyright 2017 The Chromium Authors. All rights reserved.
- + Use of this source code is governed by a BSD-style license that can be
- + found in the LICENSE file. -->
- +
- +<androidx.recyclerview.widget.RecyclerView
- + xmlns:android="http://schemas.android.com/apk/res/android"
- + android:id="@+id/script_list"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content" />
- \ No newline at end of file
- diff --git a/components/user_scripts/android/java/res/layout/scripts_preference.xml b/components/user_scripts/android/java/res/layout/scripts_preference.xml
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/res/layout/scripts_preference.xml
- @@ -0,0 +1,40 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<!-- Copyright 2017 The Chromium Authors. All rights reserved.
- + Use of this source code is governed by a BSD-style license that can be
- + found in the LICENSE file. -->
- +
- +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- + android:id="@+id/accept_scripts_list_container"
- + style="@style/PreferenceLayout"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:paddingStart="0dp"
- + android:paddingEnd="0dp"
- + android:padding="0dp"
- + android:orientation="vertical" >
- +
- + <TextView
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:padding="@dimen/draggable_list_item_padding"
- + android:text="@string/scripts_list_description" />
- +
- + <FrameLayout
- + android:id="@android:id/widget_frame"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content" />
- +
- + <TextView
- + android:id="@+id/add_script"
- + android:layout_width="match_parent"
- + android:layout_height="wrap_content"
- + android:background="?attr/selectableItemBackground"
- + android:clickable="true"
- + android:gravity="center_vertical"
- + android:padding="@dimen/draggable_list_item_padding"
- + android:paddingStart="@dimen/pref_scripts_add_button_padding"
- + android:drawablePadding="@dimen/pref_scripts_add_button_padding"
- + android:text="@string/add_script"
- + style="@style/PreferenceTitle" />
- +
- +</LinearLayout>
- \ No newline at end of file
- diff --git a/components/user_scripts/android/java/res/values/dimens.xml b/components/user_scripts/android/java/res/values/dimens.xml
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/android/java/res/values/dimens.xml
- @@ -0,0 +1,11 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<!-- Copyright 2014 The Chromium Authors. All rights reserved.
- + Use of this source code is governed by a BSD-style license that can be
- + found in the LICENSE file. -->
- +
- +<resources xmlns:tools="http://schemas.android.com/tools">
- +
- + <dimen name="pref_scripts_add_button_padding">24dp</dimen>
- + <dimen name="pref_scripts_item_popup_width">260dp</dimen>
- +
- +</resources>
- diff --git a/components/user_scripts/android/java/res/xml/userscripts_preferences.xml b/components/user_scripts/android/java/res/xml/userscripts_preferences.xml
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/res/xml/userscripts_preferences.xml
- @@ -0,0 +1,34 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<!--
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +-->
- +
- +<PreferenceScreen
- + xmlns:android="http://schemas.android.com/apk/res/android"
- + xmlns:app="http://schemas.android.com/apk/res-auto">
- +
- + <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
- + android:key="enabled_switch"
- + android:title="@string/option_userscript_flag"
- + android:summaryOn="@string/option_userscript_flag_on"
- + android:summaryOff="@string/option_userscript_flag_off" />
- +
- + <org.chromium.components.user_scripts.ScriptListPreference
- + android:key="script_list"
- + android:layout="@layout/scripts_preference"
- + android:widgetLayout="@layout/accept_script_list" />
- +
- +</PreferenceScreen>
- diff --git a/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java b/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
- @@ -0,0 +1,87 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.chrome.browser.user_scripts;
- +
- +import android.content.Context;
- +import android.content.Intent;
- +import android.provider.Browser;
- +import android.provider.MediaStore;
- +import android.net.Uri;
- +
- +import org.chromium.base.ContextUtils;
- +import org.chromium.base.ContentUriUtils;
- +import org.chromium.base.IntentUtils;
- +
- +import org.chromium.chrome.browser.IntentHandler;
- +import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
- +import org.chromium.components.browser_ui.settings.SettingsLauncher;
- +
- +import org.chromium.components.user_scripts.UserScriptsPreferences;
- +import org.chromium.components.user_scripts.UserScriptsBridge;
- +import org.chromium.components.user_scripts.IUserScriptsUtils;
- +
- +public class UserScriptsUtils implements IUserScriptsUtils
- +{
- + private static UserScriptsUtils instance;
- +
- + private UserScriptsUtils() {}
- +
- + public static void Initialize() {
- + instance = new UserScriptsUtils();
- + UserScriptsBridge.registerUtils(instance);
- + }
- +
- + public static UserScriptsUtils getInstance() {
- + if (instance == null) Initialize();
- + return instance;
- + }
- +
- + public boolean openFile(String filePath, String mimeType, String downloadGuid,
- + String originalUrl, String referrer, Uri contentUri) {
- + if (UserScriptsBridge.isEnabled() == false) return false;
- +
- + Context context = ContextUtils.getApplicationContext();
- +
- + String visibleName = filePath;
- + if (ContentUriUtils.isContentUri(visibleName)) {
- + visibleName = ContentUriUtils.getDisplayName(contentUri, context,
- + MediaStore.MediaColumns.DISPLAY_NAME);
- + }
- +
- + if (visibleName.toUpperCase().endsWith(".USER.JS") == false) return false;
- +
- + SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
- + Intent intent = settingsLauncher.createSettingsActivityIntent(
- + context, UserScriptsPreferences.class.getName(),
- + UserScriptsPreferences.createFragmentArgsForInstall(filePath));
- + IntentUtils.safeStartActivity(context, intent);
- +
- + return true;
- + }
- +
- + public void openSourceFile(String scriptKey) {
- + Context context = ContextUtils.getApplicationContext();
- +
- + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("chrome://user-scripts/?key=" + scriptKey));
- + intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
- + intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
- + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- + intent.setPackage(context.getPackageName());
- + IntentHandler.startChromeLauncherActivityForTrustedIntent(intent);
- + }
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
- @@ -0,0 +1,90 @@
- +// Copyright 2019 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +package org.chromium.components.user_scripts;
- +
- +import android.annotation.TargetApi;
- +import android.app.Activity;
- +import android.content.Context;
- +import android.content.Intent;
- +import android.content.IntentSender;
- +import android.os.Build;
- +import android.view.View;
- +
- +import androidx.fragment.app.Fragment;
- +
- +import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
- +import org.chromium.ui.base.ImmutableWeakReference;
- +import org.chromium.ui.base.IntentRequestTracker;
- +import org.chromium.ui.base.IntentRequestTracker.Delegate;
- +import org.chromium.ui.base.WindowAndroid;
- +
- +import java.lang.ref.WeakReference;
- +
- +import org.chromium.ui.permissions.ActivityAndroidPermissionDelegate;
- +
- +/**
- + * Implements intent sending for a fragment based window. This should be created when
- + * onAttach() is called on the fragment, and destroyed when onDetach() is called.
- + */
- +public class FragmentWindowAndroid extends WindowAndroid {
- + private Fragment mFragment;
- +
- + private static class TrackerDelegateImpl implements Delegate {
- + private final Fragment mFragment;
- + // This WeakReference is purely to avoid gc churn of creating a new WeakReference in
- + // every getActivity call. It is not needed for correctness.
- + private ImmutableWeakReference<Activity> mActivityWeakRefHolder;
- +
- + /**
- + * Create an instance of delegate for the given fragment that will own the
- + * IntentRequestTracker.
- + * @param fragment The fragment that owns the IntentRequestTracker.
- + */
- + private TrackerDelegateImpl(Fragment fragment) {
- + mFragment = fragment;
- + }
- +
- + @Override
- + public boolean startActivityForResult(Intent intent, int requestCode) {
- + mFragment.startActivityForResult(intent, requestCode, null);
- + return true;
- + }
- +
- + @Override
- + public boolean startIntentSenderForResult(IntentSender intentSender, int requestCode) {
- + try {
- + mFragment.startIntentSenderForResult(
- + intentSender, requestCode, new Intent(), 0, 0, 0, null);
- + } catch (IntentSender.SendIntentException e) {
- + return false;
- + }
- + return true;
- + }
- +
- + @Override
- + public void finishActivity(int requestCode) {
- + Activity activity = getActivity().get();
- + if (activity == null) return;
- + activity.finishActivity(requestCode);
- + }
- +
- + @Override
- + public final WeakReference<Activity> getActivity() {
- + if (mActivityWeakRefHolder == null
- + || mActivityWeakRefHolder.get() != mFragment.getActivity()) {
- + mActivityWeakRefHolder = new ImmutableWeakReference<>(mFragment.getActivity());
- + }
- + return mActivityWeakRefHolder;
- + }
- + }
- +
- + FragmentWindowAndroid(Context context, Fragment fragment) {
- + super(context, IntentRequestTracker.createFromDelegate(new TrackerDelegateImpl(fragment)));
- + mFragment = fragment;
- +
- + setKeyboardDelegate(new ActivityKeyboardVisibilityDelegate(getActivity()));
- + setAndroidPermissionDelegate(new ActivityAndroidPermissionDelegate(getActivity()));
- + }
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
- @@ -0,0 +1,22 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +public interface IUserScriptsUtils {
- + public void openSourceFile(String scriptKey);
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
- @@ -0,0 +1,37 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +import java.time.LocalDateTime;
- +
- +public class ScriptInfo {
- + public String Key;
- + public String Name;
- + public String Description;
- + public String Version;
- + public String FilePath;
- + public String UrlSource;
- +
- + public boolean Enabled;
- + public LocalDateTime InstallTime;
- +
- + public String ParserError;
- + public boolean ForceDisabled;
- +
- + public ScriptInfo() {}
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
- @@ -0,0 +1,163 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +import android.content.Context;
- +import android.view.LayoutInflater;
- +import android.view.MotionEvent;
- +import android.view.View;
- +import android.view.ViewGroup;
- +import android.view.accessibility.AccessibilityManager;
- +import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
- +import android.widget.ImageView;
- +import android.widget.TextView;
- +import android.widget.Switch;
- +import android.widget.CompoundButton;
- +import android.widget.LinearLayout;
- +
- +import androidx.annotation.DrawableRes;
- +import androidx.annotation.NonNull;
- +import androidx.core.view.ViewCompat;
- +import androidx.recyclerview.widget.RecyclerView.ViewHolder;
- +
- +import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter;
- +import org.chromium.components.browser_ui.widget.dragreorder.DragStateDelegate;
- +import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton;
- +import org.chromium.components.browser_ui.widget.listmenu.ListMenuButtonDelegate;
- +import org.chromium.ui.widget.ChromeImageView;
- +
- +import java.util.ArrayList;
- +import java.util.List;
- +
- +public class ScriptListBaseAdapter extends DragReorderableListAdapter<ScriptInfo> {
- +
- + class ItemClickListener {
- + void onScriptOnOff(boolean Enabled) {}
- + void onScriptClicked() {}
- + }
- +
- + static class ScriptInfoRowViewHolder extends ViewHolder {
- + private TextView mTitle;
- + private TextView mDescription;
- + private TextView mVersion;
- + private TextView mFile;
- + private TextView mUrl;
- + private TextView mError;
- + private Switch mSwitch;
- + private ChromeImageView mIcon;
- + private LinearLayout mErrorLayout;
- + private LinearLayout mUrlContainer;
- +
- + private ListMenuButton mMoreButton;
- +
- + private CompoundButton.OnCheckedChangeListener mOnOffListener;
- +
- + ScriptInfoRowViewHolder(View view) {
- + super(view);
- +
- + mSwitch = view.findViewById(R.id.switch_widget);
- + mTitle = view.findViewById(R.id.title);
- + mDescription = view.findViewById(R.id.description);
- + mVersion = view.findViewById(R.id.version);
- + mFile = view.findViewById(R.id.file);
- + mUrl = view.findViewById(R.id.url);
- + mUrlContainer = view.findViewById(R.id.url_container);
- + mError = view.findViewById(R.id.error);
- + mIcon = view.findViewById(R.id.icon);
- + mErrorLayout = view.findViewById(R.id.error_layout);
- +
- + mMoreButton = view.findViewById(R.id.more);
- + }
- +
- + protected void updateScriptInfo(ScriptInfo item) {
- + mSwitch.setOnCheckedChangeListener(null);
- + mSwitch.setChecked(item.Enabled);
- + mSwitch.setOnCheckedChangeListener(mOnOffListener);
- +
- + mSwitch.setEnabled(true);
- + if (item.ForceDisabled) {
- + mSwitch.setEnabled(false);
- + }
- +
- + mTitle.setText(item.Name);
- + mDescription.setText(item.Description);
- + mVersion.setText(item.Version);
- + mFile.setText(item.Key);
- + mUrl.setText(item.UrlSource);
- + mError.setText(item.ParserError);
- +
- + mUrl.setVisibility(View.VISIBLE);
- + if (item.UrlSource == null || item.UrlSource.isEmpty()) {
- + mUrlContainer.setVisibility(View.GONE);
- + }
- + mErrorLayout.setVisibility(View.VISIBLE);
- + if (item.ParserError == null || item.ParserError.isEmpty()) {
- + mErrorLayout.setVisibility(View.GONE);
- + }
- + }
- +
- + void setMenuButtonDelegate(@NonNull ListMenuButtonDelegate delegate) {
- + mMoreButton.setVisibility(View.VISIBLE);
- + mMoreButton.setDelegate(delegate);
- + // Set item row end padding 0 when MenuButton is visible.
- + ViewCompat.setPaddingRelative(itemView, ViewCompat.getPaddingStart(itemView),
- + itemView.getPaddingTop(), 0, itemView.getPaddingBottom());
- + }
- +
- + void setItemListener(@NonNull ItemClickListener listener) {
- + mOnOffListener = (buttonView, isChecked) -> listener.onScriptOnOff(isChecked);
- + mSwitch.setOnCheckedChangeListener(mOnOffListener);
- + itemView.setOnClickListener(view -> listener.onScriptClicked());
- + }
- + }
- +
- + ScriptListBaseAdapter(Context context) {
- + super(context);
- + }
- +
- + @Override
- + public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
- + View row = LayoutInflater.from(viewGroup.getContext())
- + .inflate(R.layout.accept_script_item, viewGroup, false);
- + return new ScriptInfoRowViewHolder(row);
- + }
- +
- + @Override
- + public void onBindViewHolder(ViewHolder viewHolder, int i) {
- + ((ScriptInfoRowViewHolder) viewHolder).updateScriptInfo(mElements.get(i));
- + }
- +
- + void setDisplayedScriptInfo(List<ScriptInfo> values) {
- + mElements = new ArrayList<>(values);
- + notifyDataSetChanged();
- + }
- +
- + @Override
- + protected void setOrder(List<ScriptInfo> order) {
- + }
- +
- + @Override
- + protected boolean isActivelyDraggable(ViewHolder viewHolder) {
- + return isPassivelyDraggable(viewHolder);
- + }
- +
- + @Override
- + protected boolean isPassivelyDraggable(ViewHolder viewHolder) {
- + return viewHolder instanceof ScriptInfoRowViewHolder;
- + }
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
- @@ -0,0 +1,171 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItem;
- +import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItemWithEndIcon;
- +
- +import android.content.Context;
- +import android.content.Intent;
- +import android.provider.Browser;
- +import android.net.Uri;
- +import android.util.AttributeSet;
- +import android.widget.TextView;
- +import android.widget.Toast;
- +
- +import androidx.preference.Preference;
- +import androidx.preference.PreferenceViewHolder;
- +import androidx.recyclerview.widget.DividerItemDecoration;
- +import androidx.recyclerview.widget.LinearLayoutManager;
- +import androidx.recyclerview.widget.RecyclerView;
- +import androidx.recyclerview.widget.RecyclerView.ViewHolder;
- +
- +import org.chromium.ui.base.WindowAndroid;
- +import org.chromium.ui.base.ActivityWindowAndroid;
- +
- +import org.chromium.base.ApplicationStatus;
- +import org.chromium.base.ContextUtils;
- +import org.chromium.components.browser_ui.widget.TintedDrawable;
- +import org.chromium.components.browser_ui.widget.listmenu.BasicListMenu;
- +import org.chromium.components.browser_ui.widget.listmenu.ListMenu;
- +import org.chromium.components.browser_ui.widget.listmenu.ListMenuItemProperties;
- +import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
- +
- +import org.chromium.components.user_scripts.ScriptListBaseAdapter;
- +import org.chromium.components.user_scripts.FragmentWindowAndroid;
- +
- +import java.util.List;
- +
- +public class ScriptListPreference extends Preference {
- + private static class ScriptListAdapter
- + extends ScriptListBaseAdapter {
- + private final Context mContext;
- +
- + ScriptListAdapter(Context context) {
- + super(context);
- + mContext = context;
- + }
- +
- + @Override
- + public void onBindViewHolder(ViewHolder holder, int position) {
- + super.onBindViewHolder(holder, position);
- +
- + final ScriptInfo info = getItemByPosition(position);
- +
- + ModelList menuItems = new ModelList();
- +
- + menuItems.add(buildMenuListItem(R.string.remove, 0, 0, true));
- + menuItems.add(buildMenuListItem(R.string.scripts_view_source, 0, 0,
- + info.ParserError == null || info.ParserError.isEmpty()));
- +
- + ListMenu.Delegate delegate = (model) -> {
- + int textId = model.get(ListMenuItemProperties.TITLE_ID);
- + if (textId == R.string.remove) {
- + UserScriptsBridge.RemoveScript(info.Key);
- + } else if (textId == R.string.scripts_view_source) {
- + UserScriptsBridge.getUtils().openSourceFile(info.Key);
- + }
- + };
- + ((ScriptInfoRowViewHolder) holder)
- + .setMenuButtonDelegate(() -> new BasicListMenu(mContext, menuItems, delegate));
- + ((ScriptInfoRowViewHolder) holder)
- + .setItemListener(new ScriptListBaseAdapter.ItemClickListener() {
- + @Override
- + public void onScriptOnOff(boolean Enabled) {
- + UserScriptsBridge.SetScriptEnabled(info.Key, Enabled);
- + }
- +
- + @Override
- + public void onScriptClicked() {}
- + });
- + }
- +
- + // @Override
- + public void onDataUpdated(boolean enabled) {
- + List<ScriptInfo> list = UserScriptsBridge.getUserScriptItems();
- + if (enabled == false) {
- + for (ScriptInfo script : list) {
- + script.ForceDisabled = true;
- + }
- + }
- + setDisplayedScriptInfo(list);
- + }
- + }
- +
- + private TextView mAddButton;
- + private RecyclerView mRecyclerView;
- + private ScriptListAdapter mAdapter;
- + private FragmentWindowAndroid mWindowAndroid;
- +
- + public ScriptListPreference(Context context, AttributeSet attrs) {
- + super(context, attrs);
- + mAdapter = new ScriptListAdapter(context);
- + }
- +
- + public void setWindowAndroid(FragmentWindowAndroid windowAndroid) {
- + mWindowAndroid = windowAndroid;
- + }
- +
- + @Override
- + public void onBindViewHolder(PreferenceViewHolder holder) {
- + super.onBindViewHolder(holder);
- +
- + mAddButton = (TextView) holder.findViewById(R.id.add_script);
- + mAddButton.setCompoundDrawablesRelativeWithIntrinsicBounds(
- + TintedDrawable.constructTintedDrawable(
- + getContext(), R.drawable.plus, R.color.default_control_color_active_baseline),
- + null, null, null);
- + mAddButton.setOnClickListener(view -> {
- + UserScriptsBridge.SelectAndAddScriptFromFile(mWindowAndroid);
- + });
- +
- + mRecyclerView = (RecyclerView) holder.findViewById(R.id.script_list);
- + LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
- + mRecyclerView.setLayoutManager(layoutManager);
- + mRecyclerView.addItemDecoration(
- + new DividerItemDecoration(getContext(), layoutManager.getOrientation()));
- + mRecyclerView.setEnabled(this.isEnabled());
- + UserScriptsBridge.RegisterLoadCallback(this);
- +
- + // We do not want the RecyclerView to be announced by screen readers every time
- + // the view is bound.
- + if (mRecyclerView.getAdapter() != mAdapter) {
- + mRecyclerView.setAdapter(mAdapter);
- + // Initialize script list.
- + mAdapter.onDataUpdated(this.isEnabled());
- + }
- + }
- +
- + @Override
- + public void setEnabled (boolean enabled) {
- + super.setEnabled(enabled);
- + if (mRecyclerView != null) mRecyclerView.setEnabled(enabled);
- + NotifyScriptsChanged();
- + }
- +
- + public void NotifyScriptsChanged() {
- + mAdapter.onDataUpdated(this.isEnabled());
- + }
- +
- + public void OnUserScriptLoaded(boolean result, String error) {
- + if (result == false) {
- + Toast toast = Toast.makeText(getContext(), error, Toast.LENGTH_LONG);
- + toast.show();
- + }
- + }
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
- @@ -0,0 +1,212 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +import java.util.ArrayList;
- +import java.util.List;
- +import java.lang.ref.WeakReference;
- +
- +import org.json.JSONArray;
- +import org.json.JSONException;
- +import org.json.JSONObject;
- +
- +import android.content.Context;
- +import android.content.Intent;
- +import android.net.Uri;
- +import android.provider.MediaStore;
- +import androidx.annotation.Nullable;
- +import android.app.AlertDialog;
- +import android.content.DialogInterface;
- +
- +import org.chromium.base.annotations.CalledByNative;
- +import org.chromium.base.annotations.JNINamespace;
- +import org.chromium.base.annotations.NativeMethods;
- +import org.chromium.base.ContentUriUtils;
- +import org.chromium.base.Log;
- +import org.chromium.ui.base.WindowAndroid;
- +
- +import org.chromium.components.user_scripts.ScriptListPreference;
- +import org.chromium.components.user_scripts.IUserScriptsUtils;
- +
- +@JNINamespace("user_scripts")
- +public class UserScriptsBridge {
- + private static final String TAG = "UserScript";
- +
- + static WeakReference<ScriptListPreference> observer;
- +
- + private static IUserScriptsUtils utilInstance;
- +
- + public static void registerUtils(IUserScriptsUtils instance) {
- + utilInstance = instance;
- + }
- +
- + public static IUserScriptsUtils getUtils() {
- + return utilInstance;
- + }
- +
- + public static boolean isEnabled() {
- + return UserScriptsBridgeJni.get().isEnabled();
- + }
- +
- + public static void setEnabled(boolean enabled) {
- + UserScriptsBridgeJni.get().setEnabled(enabled);
- + }
- +
- + public static void RemoveScript(String key) {
- + UserScriptsBridgeJni.get().removeScript(key);
- + }
- +
- + public static void SetScriptEnabled(String key,
- + boolean enabled) {
- + UserScriptsBridgeJni.get().setScriptEnabled(key, enabled);
- + }
- +
- + public static void Reload() {
- + UserScriptsBridgeJni.get().reload();
- + }
- +
- + public static void SelectAndAddScriptFromFile(WindowAndroid window) {
- + Context context = window.getContext().get();
- +
- + Intent fileSelector = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- + fileSelector.addCategory(Intent.CATEGORY_OPENABLE);
- + fileSelector.setType("*/*");
- + fileSelector.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- +
- + window.showIntent(fileSelector,
- + new WindowAndroid.IntentCallback() {
- + @Override
- + public void onIntentCompleted(int resultCode, Intent data) {
- + if (data == null) return;
- + Uri filePath = data.getData();
- + TryToInstall(context, filePath.toString());
- + }
- + },
- + null);
- + }
- +
- + public static void TryToInstall(Context context, String ScriptFullPath) {
- + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
- + @Override
- + public void onClick(DialogInterface dialog, int which) {
- + switch (which){
- + case DialogInterface.BUTTON_POSITIVE:
- + UserScriptsBridgeJni.get().tryToInstall(ScriptFullPath);
- + break;
- +
- + case DialogInterface.BUTTON_NEGATIVE:
- + break;
- + }
- + }
- + };
- +
- + String scriptName = ScriptFullPath;
- + if (ContentUriUtils.isContentUri(scriptName)) {
- + scriptName = ContentUriUtils.getFilePathFromContentUri(Uri.parse(scriptName));
- + if (scriptName == null) {
- + // fallback to content uri name if fail
- + scriptName = ContentUriUtils.getDisplayName(Uri.parse(ScriptFullPath), context,
- + MediaStore.MediaColumns.DISPLAY_NAME);
- + }
- + }
- +
- + String message = context.getString(R.string.ask_to_install, scriptName);
- +
- + AlertDialog.Builder builder = new AlertDialog.Builder(context);
- + builder.setMessage(message)
- + .setPositiveButton(context.getString(R.string.yes), dialogClickListener)
- + .setNegativeButton(context.getString(R.string.no), dialogClickListener)
- + .show();
- + }
- +
- + public static List<ScriptInfo> getUserScriptItems() {
- + List<ScriptInfo> list = new ArrayList<>();
- + try {
- + String json = UserScriptsBridgeJni.get().getScriptsInfo();
- +
- + JSONObject jsonObject = new JSONObject(json);
- +
- + JSONArray scripts = jsonObject.names();
- + if (scripts != null) {
- + Log.i(TAG, "User Scripts Loaded: %s", json);
- + Log.i(TAG, "Totals scripts: %s", Integer.toString(scripts.length()));
- + for (int i = 0; i < scripts.length(); i++) {
- + String key = (String) scripts.get(i);
- + JSONObject script = jsonObject.getJSONObject(key);
- +
- + ScriptInfo si = new ScriptInfo();
- + si.Key = key;
- + list.add(si);
- +
- + if(script.has("name")) si.Name = script.getString("name");
- + if(script.has("description")) si.Description = script.getString("description");
- + if(script.has("version")) si.Version = script.getString("version");
- + if(script.has("file_path")) si.FilePath = script.getString("file_path");
- + if(script.has("url_source")) si.UrlSource = script.getString("url_source");
- + if(script.has("parser_error")) si.ParserError = script.getString("parser_error");
- + if(script.has("force_disabled")) si.ForceDisabled = script.getBoolean("force_disabled");;
- + si.Enabled = script.getBoolean("enabled");
- + }
- + } else {
- + Log.i(TAG, "User Scripts list empty");
- + }
- + } catch (Exception e) {
- + Log.e(TAG, "User Scripts Load Error", e.toString());
- + }
- + return list;
- + }
- +
- + public static void RegisterLoadCallback(ScriptListPreference caller) {
- + UserScriptsBridgeJni.get().registerLoadCallback();
- + observer = new WeakReference<ScriptListPreference>(caller);
- + }
- +
- + @CalledByNative
- + private static void shouldRefreshUserScriptList() {
- + ScriptListPreference reference = observer.get();
- + if (reference != null) {
- + reference.NotifyScriptsChanged();
- + }
- + }
- +
- + @CalledByNative
- + private static void onUserScriptLoaded(boolean result, String error) {
- + ScriptListPreference reference = observer.get();
- + if (reference != null) {
- + reference.OnUserScriptLoaded(result, error);
- + }
- + }
- +
- + @NativeMethods
- + interface Natives {
- + boolean isEnabled();
- + void setEnabled(boolean enabled);
- +
- + String getScriptsInfo();
- +
- + void removeScript(String scriptKey);
- + void setScriptEnabled(String scriptKey, boolean enabled);
- +
- + void reload();
- + void selectAndAddScriptFromFile(WindowAndroid window);
- + void tryToInstall(String scriptFullPath);
- +
- + void registerLoadCallback();
- + }
- +
- +}
- diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
- @@ -0,0 +1,116 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +package org.chromium.components.user_scripts;
- +
- +import android.app.Activity;
- +import android.content.Context;
- +import android.content.Intent;
- +import android.os.Bundle;
- +import android.provider.Browser;
- +import android.net.Uri;
- +import android.view.MenuItem;
- +import android.view.View;
- +
- +import androidx.preference.Preference;
- +import androidx.preference.PreferenceFragmentCompat;
- +
- +import org.chromium.base.Log;
- +import org.chromium.ui.base.WindowAndroid;
- +import org.chromium.ui.base.ActivityWindowAndroid;
- +
- +import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
- +import org.chromium.components.browser_ui.settings.SettingsUtils;
- +import org.chromium.components.browser_ui.settings.TextMessagePreference;
- +
- +import org.chromium.components.user_scripts.UserScriptsBridge;
- +import org.chromium.components.user_scripts.FragmentWindowAndroid;
- +
- +public class UserScriptsPreferences
- + extends PreferenceFragmentCompat
- + implements SettingsUtils.ISupportHelpAndFeedback {
- +
- + private static final String PREF_ENABLED_SWITCH = "enabled_switch";
- + private static final String PREF_SCRIPTLISTPREFERENCE = "script_list";
- +
- + public static final String EXTRA_SCRIPT_FILE = "org.chromium.chrome.preferences.script_file";
- +
- + private FragmentWindowAndroid mWindowAndroid;
- +
- + @Override
- + public void onDestroy() {
- + if (mWindowAndroid != null) mWindowAndroid.destroy();
- + super.onDestroy();
- + }
- +
- + @Override
- + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- + getActivity().setTitle(R.string.prefs_userscripts_settings);
- + SettingsUtils.addPreferencesFromResource(this, R.xml.userscripts_preferences);
- +
- + ChromeSwitchPreference enabledSwitch =
- + (ChromeSwitchPreference) findPreference(PREF_ENABLED_SWITCH);
- + ScriptListPreference listPreference =
- + (ScriptListPreference) findPreference(PREF_SCRIPTLISTPREFERENCE);
- +
- + boolean enabled = UserScriptsBridge.isEnabled();
- + enabledSwitch.setChecked(enabled);
- + listPreference.setEnabled(enabled);
- + enabledSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
- + UserScriptsBridge.setEnabled((boolean) newValue);
- + listPreference.setEnabled((boolean) newValue);
- + return true;
- + });
- +
- + mWindowAndroid = new FragmentWindowAndroid(getContext(), this);
- + listPreference.setWindowAndroid(mWindowAndroid);
- + }
- +
- + @Override
- + public void onActivityResult(int requestCode, int resultCode, Intent data) {
- + // handle picker callback from SelectFileDialog
- + mWindowAndroid.getIntentRequestTracker().onActivityResult(requestCode, resultCode, data);
- + }
- +
- + public void onHelpAndFeebackPressed() {
- + Context context = getContext();
- +
- + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/bromite/bromite/wiki/UserScripts"));
- + // Let Chromium know that this intent is from Chromium, so that it does not close the app when
- + // the user presses 'back' button.
- + intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
- + intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
- + intent.setPackage(context.getPackageName());
- + context.startActivity(intent);
- + }
- +
- + public static Bundle createFragmentArgsForInstall(String filePath) {
- + Bundle fragmentArgs = new Bundle();
- + fragmentArgs.putSerializable(EXTRA_SCRIPT_FILE, filePath);
- + return fragmentArgs;
- + }
- +
- + @Override
- + public void onActivityCreated(Bundle savedInstanceState) {
- + super.onActivityCreated(savedInstanceState);
- +
- + String scriptToInstall = (String)getArguments().getSerializable(EXTRA_SCRIPT_FILE);
- + if (scriptToInstall != null) {
- + UserScriptsBridge.TryToInstall(getContext(), scriptToInstall);
- + }
- + }
- +}
- diff --git a/components/user_scripts/android/java_sources.gni b/components/user_scripts/android/java_sources.gni
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/java_sources.gni
- @@ -0,0 +1,18 @@
- +# This file is part of Bromite.
- +
- +# Bromite is free software: you can redistribute it and/or modify
- +# it under the terms of the GNU General Public License as published by
- +# the Free Software Foundation, either version 3 of the License, or
- +# (at your option) any later version.
- +
- +# Bromite is distributed in the hope that it will be useful,
- +# but WITHOUT ANY WARRANTY; without even the implied warranty of
- +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- +# GNU General Public License for more details.
- +
- +# You should have received a copy of the GNU General Public License
- +# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +
- +userscripts_java_sources = [
- + "//components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java",
- +]
- diff --git a/components/user_scripts/android/user_scripts_bridge.cc b/components/user_scripts/android/user_scripts_bridge.cc
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/user_scripts_bridge.cc
- @@ -0,0 +1,173 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include <jni.h>
- +#include <algorithm>
- +#include <string>
- +#include <vector>
- +#include <sstream>
- +#include <iterator>
- +
- +#include "base/android/callback_android.h"
- +#include "base/android/jni_android.h"
- +#include "base/android/jni_array.h"
- +#include "base/android/jni_string.h"
- +#include "base/android/scoped_java_ref.h"
- +#include "ui/android/window_android.h"
- +
- +#include "components/user_scripts/android/user_scripts_jni_headers/UserScriptsBridge_jni.h"
- +#include "../browser/userscripts_browser_client.h"
- +#include "user_scripts_bridge.h"
- +
- +using base::android::AttachCurrentThread;
- +using base::android::ConvertJavaStringToUTF8;
- +using base::android::ConvertUTF16ToJavaString;
- +using base::android::ConvertUTF8ToJavaString;
- +using base::android::JavaParamRef;
- +using base::android::JavaRef;
- +using base::android::ScopedJavaGlobalRef;
- +using base::android::ScopedJavaLocalRef;
- +using content::BrowserContext;
- +
- +namespace {
- +
- +user_scripts::UserScriptsBrowserClient* GetUserScriptsBrowserClient() {
- + return user_scripts::UserScriptsBrowserClient::GetInstance();
- +}
- +
- +class CallbackObserver : public user_scripts::UserScriptLoader::Observer {
- + private:
- + void OnScriptsLoaded(user_scripts::UserScriptLoader* loader,
- + content::BrowserContext* browser_context) override {
- + user_scripts::ShouldRefreshUserScriptList(base::android::AttachCurrentThread());
- + }
- +
- + void OnUserScriptLoaded(user_scripts::UserScriptLoader* loader,
- + bool result, const std::string& error) override {
- + user_scripts::OnUserScriptLoaded(base::android::AttachCurrentThread(),
- + result, error);
- + }
- +
- + void OnUserScriptLoaderDestroyed(user_scripts::UserScriptLoader* loader) override {}
- +};
- +
- +CallbackObserver* g_userscripts_loader_observer = NULL;
- +
- +}
- +
- +namespace user_scripts {
- +
- +static jboolean JNI_UserScriptsBridge_IsEnabled(
- + JNIEnv* env) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return false;
- + return client->GetPrefs()->IsEnabled();
- +}
- +
- +static void JNI_UserScriptsBridge_SetEnabled(
- + JNIEnv* env,
- + jboolean is_enabled) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- + client->GetPrefs()->SetEnabled(is_enabled);
- + client->GetLoader()->StartLoad();
- +}
- +
- +static base::android::ScopedJavaLocalRef<jstring> JNI_UserScriptsBridge_GetScriptsInfo(
- + JNIEnv* env) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return ConvertUTF8ToJavaString(env, {});
- +
- + std::string json = client->GetPrefs()->GetScriptsInfo();
- + return ConvertUTF8ToJavaString(env, json);
- +}
- +
- +static void JNI_UserScriptsBridge_RemoveScript(
- + JNIEnv* env,
- + const JavaParamRef<jstring>& jscript_key) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + std::string script_key = base::android::ConvertJavaStringToUTF8(jscript_key);
- + client->GetLoader()->RemoveScript(script_key);
- +}
- +
- +static void JNI_UserScriptsBridge_SetScriptEnabled(
- + JNIEnv* env,
- + const JavaParamRef<jstring>& jscript_key,
- + jboolean is_enabled) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + std::string script_key = base::android::ConvertJavaStringToUTF8(jscript_key);
- + client->GetLoader()->SetScriptEnabled(script_key, is_enabled);
- +}
- +
- +static void JNI_UserScriptsBridge_Reload(
- + JNIEnv* env) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + client->GetLoader()->StartLoad();
- + user_scripts::ShouldRefreshUserScriptList(env);
- +}
- +
- +static void JNI_UserScriptsBridge_SelectAndAddScriptFromFile(
- + JNIEnv* env,
- + const JavaParamRef<jobject>& jwindow_android) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + client->GetLoader()->SelectAndAddScriptFromFile(
- + ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android));
- +}
- +
- +static void JNI_UserScriptsBridge_TryToInstall(JNIEnv* env,
- + const JavaParamRef<jstring>& jscript_path) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + std::string script_path = base::android::ConvertJavaStringToUTF8(jscript_path);
- + base::FilePath path(script_path);
- +
- + client->GetLoader()->TryToInstall(path);
- +}
- +
- +static void JNI_UserScriptsBridge_RegisterLoadCallback(
- + JNIEnv* env) {
- + user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
- + if (client == NULL) return;
- +
- + if (g_userscripts_loader_observer == NULL) {
- + g_userscripts_loader_observer = new CallbackObserver();
- + client->GetLoader()->AddObserver(g_userscripts_loader_observer);
- + }
- +}
- +
- +static void ShouldRefreshUserScriptList(JNIEnv* env) {
- + Java_UserScriptsBridge_shouldRefreshUserScriptList(env);
- +}
- +
- +static void OnUserScriptLoaded(JNIEnv* env,
- + bool result, const std::string& error) {
- + base::android::ScopedJavaLocalRef<jstring> j_error =
- + base::android::ConvertUTF8ToJavaString(env, error);
- +
- + Java_UserScriptsBridge_onUserScriptLoaded(env, result, j_error);
- +}
- +
- +}
- diff --git a/components/user_scripts/android/user_scripts_bridge.h b/components/user_scripts/android/user_scripts_bridge.h
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/android/user_scripts_bridge.h
- @@ -0,0 +1,31 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef COMPONENTS_USERSCRIPS_HELPER_ANDROID_H_
- +#define COMPONENTS_USERSCRIPS_HELPER_ANDROID_H_
- +
- +#include <jni.h>
- +
- +namespace user_scripts {
- +
- +static void ShouldRefreshUserScriptList(JNIEnv* env);
- +static void OnUserScriptLoaded(JNIEnv* env,
- + bool result, const std::string& error);
- +
- +} // namespace user_scripts
- +
- +#endif
- \ No newline at end of file
- diff --git a/components/user_scripts/browser/BUILD.gn b/components/user_scripts/browser/BUILD.gn
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/BUILD.gn
- @@ -0,0 +1,82 @@
- +# This file is part of Bromite.
- +
- +# Bromite is free software: you can redistribute it and/or modify
- +# it under the terms of the GNU General Public License as published by
- +# the Free Software Foundation, either version 3 of the License, or
- +# (at your option) any later version.
- +
- +# Bromite is distributed in the hope that it will be useful,
- +# but WITHOUT ANY WARRANTY; without even the implied warranty of
- +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- +# GNU General Public License for more details.
- +
- +# You should have received a copy of the GNU General Public License
- +# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +
- +import("//build/config/features.gni")
- +import("//tools/grit/grit_rule.gni")
- +
- +group("browser") {
- + public_deps = [
- + "//components/user_scripts/browser:browser_sources",
- + ]
- +}
- +
- +source_set("browser_sources") {
- + visibility = [ "./*" ]
- +
- + sources = [
- + "file_task_runner.cc",
- + "file_task_runner.h",
- + "userscripts_browser_client.cc",
- + "userscripts_browser_client.h",
- + "user_script_loader.cc",
- + "user_script_loader.h",
- + "user_script_prefs.cc",
- + "user_script_prefs.h",
- + "user_script_pref_info.cc",
- + "user_script_pref_info.h",
- + "ui/user_scripts_ui.h",
- + "ui/user_scripts_ui.cc",
- + ]
- +
- + deps = [
- + ":userscripts_browser_resources",
- + "//base:i18n",
- + "//components/keyed_service/content",
- + "//components/keyed_service/core",
- + "//components/pref_registry",
- + "//components/prefs",
- + "//content/public/browser",
- + "//crypto:platform",
- + "//components/user_scripts/common",
- + "//services/device/public/mojom",
- + "//services/preferences/public/cpp",
- + "//services/service_manager/public/cpp",
- + "//third_party/blink/public/common",
- + "//ui/display",
- + ]
- +
- + public_deps = [
- + "//content/public/common",
- + ]
- +
- + configs += [
- + "//build/config:precompiled_headers",
- + "//build/config/compiler:wexit_time_destructors",
- + ]
- +}
- +
- +group("closure_compile") {
- + deps = [ "resources/user-script-ui:closure_compile" ]
- +}
- +
- +grit("userscripts_browser_resources") {
- + source = "resources/browser_resources.grd"
- +
- + output_dir = "$root_gen_dir/chrome"
- + outputs = [
- + "grit/userscripts_browser_resources.h",
- + "userscripts_browser_resources.pak",
- + ]
- +}
- diff --git a/components/user_scripts/browser/file_task_runner.cc b/components/user_scripts/browser/file_task_runner.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/file_task_runner.cc
- @@ -0,0 +1,40 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include "file_task_runner.h"
- +
- +#include "base/task/sequenced_task_runner.h"
- +#include "base/task/lazy_thread_pool_task_runner.h"
- +#include "base/task/task_traits.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +base::LazyThreadPoolSequencedTaskRunner g_us_task_runner =
- + LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
- + base::TaskTraits(base::MayBlock(),
- + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
- + base::TaskPriority::USER_VISIBLE));
- +
- +} // namespace
- +
- +scoped_refptr<base::SequencedTaskRunner> GetUserScriptsFileTaskRunner() {
- + return g_us_task_runner.Get();
- +}
- +
- +} // namespace user_scripts
- diff --git a/components/user_scripts/browser/file_task_runner.h b/components/user_scripts/browser/file_task_runner.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/file_task_runner.h
- @@ -0,0 +1,34 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
- +#define USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
- +
- +#include "base/memory/ref_counted.h"
- +#include "base/task/task_traits.h"
- +
- +namespace base {
- +class SequencedTaskRunner;
- +}
- +
- +namespace user_scripts {
- +
- +scoped_refptr<base::SequencedTaskRunner> GetUserScriptsFileTaskRunner();
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
- diff --git a/components/user_scripts/browser/resources/browser_resources.grd b/components/user_scripts/browser/resources/browser_resources.grd
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/resources/browser_resources.grd
- @@ -0,0 +1,14 @@
- +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
- + <outputs>
- + <output filename="grit/userscripts_browser_resources.h" type="rc_header">
- + <emit emit_type='prepend'></emit>
- + </output>
- + <output filename="userscripts_browser_resources.pak" type="data_package" />
- + </outputs>
- + <release seq="1">
- + <includes>
- + <include name="IDR_USER_SCRIPTS_HTML" file="user-script-ui\user-scripts-ui.html" type="BINDATA" />
- + <include name="IDR_USER_SCRIPTS_JS" file="user-script-ui\user-scripts-ui.js" type="BINDATA" />
- + </includes>
- + </release>
- +</grit>
- \ No newline at end of file
- diff --git a/components/user_scripts/browser/resources/user-script-ui/BUILD.gn b/components/user_scripts/browser/resources/user-script-ui/BUILD.gn
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/resources/user-script-ui/BUILD.gn
- @@ -0,0 +1,12 @@
- +import("//third_party/closure_compiler/compile_js.gni")
- +
- +js_type_check("closure_compile") {
- + deps = [ ":view_script_source" ]
- +}
- +
- +js_library("view_script_source") {
- + deps = [
- + "//ui/webui/resources/js:cr.m",
- + "//ui/webui/resources/js:util.m",
- + ]
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
- @@ -0,0 +1,14 @@
- +<!doctype html>
- +<html lang="en">
- +<head>
- + <meta charset="utf-8">
- + <title>Local State Debug Page</title>
- + <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
- + <script type="module" src="user-scripts-ui.js"></script>
- +</head>
- +<body>
- + <pre id="content">
- + Loading Script Source file...
- + </pre>
- +</body>
- +</html>
- \ No newline at end of file
- diff --git a/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
- @@ -0,0 +1,9 @@
- +import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
- +import {$} from 'chrome://resources/js/util.m.js';
- +
- +document.addEventListener('DOMContentLoaded', function() {
- + const urlParams = new URLSearchParams(window.location.search);
- + sendWithPromise('requestSource', urlParams.get('key')).then(textContent => {
- + $('content').textContent = textContent[0].content;
- + });
- +});
- \ No newline at end of file
- diff --git a/components/user_scripts/browser/ui/user_scripts_ui.cc b/components/user_scripts/browser/ui/user_scripts_ui.cc
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/ui/user_scripts_ui.cc
- @@ -0,0 +1,147 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +
- +#include <memory>
- +
- +#include "base/bind.h"
- +#include "base/json/json_string_value_serializer.h"
- +#include "base/memory/writable_shared_memory_region.h"
- +#include "base/strings/string_util.h"
- +#include "base/values.h"
- +
- +#include "chrome/browser/browser_process.h"
- +#include "chrome/browser/profiles/profile.h"
- +#include "chrome/common/url_constants.h"
- +#include "chrome/grit/userscripts_browser_resources.h"
- +#include "components/prefs/pref_service.h"
- +#include "content/public/browser/web_ui.h"
- +#include "content/public/browser/web_ui_controller.h"
- +#include "content/public/browser/web_ui_data_source.h"
- +#include "content/public/browser/web_ui_message_handler.h"
- +
- +#include "user_scripts_ui.h"
- +#include "../userscripts_browser_client.h"
- +#include "../../common/user_script.h"
- +
- +namespace {
- +
- +class UserScriptsUIHandler : public content::WebUIMessageHandler {
- + public:
- + UserScriptsUIHandler(const UserScriptsUIHandler&) = delete;
- + UserScriptsUIHandler& operator=(const UserScriptsUIHandler&) = delete;
- + UserScriptsUIHandler();
- + ~UserScriptsUIHandler() override;
- +
- + // content::WebUIMessageHandler:
- + void RegisterMessages() override;
- +
- + private:
- + void HandleRequestSource(base::Value::ConstListView args);
- + void OnScriptsLoaded(
- + const std::string callback_id,
- + const std::string script_key,
- + std::unique_ptr<user_scripts::UserScriptList> user_scripts);
- +
- + std::unique_ptr<user_scripts::UserScriptList> loaded_scripts_;
- +
- + base::WeakPtrFactory<UserScriptsUIHandler> weak_factory_{this};
- +};
- +
- +UserScriptsUIHandler::UserScriptsUIHandler()
- + : loaded_scripts_(new user_scripts::UserScriptList()) {
- +}
- +
- +UserScriptsUIHandler::~UserScriptsUIHandler() {
- +}
- +
- +void UserScriptsUIHandler::RegisterMessages() {
- + web_ui()->RegisterMessageCallback(
- + "requestSource",
- + base::BindRepeating(&UserScriptsUIHandler::HandleRequestSource,
- + base::Unretained(this)));
- +}
- +
- +void UserScriptsUIHandler::HandleRequestSource(base::Value::ConstListView args) {
- + AllowJavascript();
- + if (args.size() < 2) return;
- +
- + std::string callback_id = args[0].GetString();
- + std::string script_key = args[1].GetString();
- + if (script_key.empty()) {
- + std::string json = "Missing key value.";
- + ResolveJavascriptCallback(base::Value(callback_id), base::Value(json));
- + return;
- + }
- +
- + user_scripts::UserScriptsBrowserClient* client = user_scripts::UserScriptsBrowserClient::GetInstance();
- + if (client == NULL) {
- + std::string json = "User scripts disabled.";
- + ResolveJavascriptCallback(base::Value(callback_id), base::Value(json));
- + } else {
- + std::unique_ptr<user_scripts::UserScriptList> scripts_to_load =
- + std::move(loaded_scripts_);
- + scripts_to_load->clear();
- +
- + client->GetLoader()->LoadScripts(std::move(scripts_to_load),
- + base::BindOnce(
- + &UserScriptsUIHandler::OnScriptsLoaded,
- + weak_factory_.GetWeakPtr(),
- + callback_id, script_key)
- + );
- + }
- +}
- +
- +void UserScriptsUIHandler::OnScriptsLoaded(
- + const std::string callback_id,
- + const std::string script_key,
- + std::unique_ptr<user_scripts::UserScriptList> user_scripts) {
- + loaded_scripts_ = std::move(user_scripts);
- +
- + base::ListValue response;
- + for (const std::unique_ptr<user_scripts::UserScript>& script : *loaded_scripts_) {
- + if (script->key() == script_key) {
- + auto scriptData = std::make_unique<base::DictionaryValue>();
- + for (const std::unique_ptr<user_scripts::UserScript::File>& js_file :
- + script->js_scripts()) {
- + base::StringPiece contents = js_file->GetContent();
- + scriptData->SetString("content", contents.data());
- + }
- + response.Append(std::move(scriptData));
- + }
- + }
- +
- + ResolveJavascriptCallback(base::Value(callback_id), response);
- +}
- +
- +} // namespace
- +
- +namespace user_scripts {
- +
- +UserScriptsUI::UserScriptsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
- + content::WebUIDataSource* html_source =
- + content::WebUIDataSource::Create(kChromeUIUserScriptsHost);
- + html_source->SetDefaultResource(IDR_USER_SCRIPTS_HTML);
- + html_source->AddResourcePath("user-scripts-ui.js", IDR_USER_SCRIPTS_JS);
- + content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
- + web_ui->AddMessageHandler(std::make_unique<UserScriptsUIHandler>());
- +}
- +
- +UserScriptsUI::~UserScriptsUI() {
- +}
- +
- +}
- diff --git a/components/user_scripts/browser/ui/user_scripts_ui.h b/components/user_scripts/browser/ui/user_scripts_ui.h
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/ui/user_scripts_ui.h
- @@ -0,0 +1,37 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_UI_USER_SCRIPTS_UI_H_
- +#define USERSCRIPTS_BROWSER_UI_USER_SCRIPTS_UI_H_
- +
- +#include "content/public/browser/web_ui_controller.h"
- +
- +namespace user_scripts {
- +
- +const char kChromeUIUserScriptsHost[] = "user-scripts";
- +
- +class UserScriptsUI : public content::WebUIController {
- + public:
- + UserScriptsUI(const UserScriptsUI&) = delete;
- + UserScriptsUI& operator=(const UserScriptsUI&) = delete;
- + explicit UserScriptsUI(content::WebUI* web_ui);
- + ~UserScriptsUI() override;
- +};
- +
- +}
- +
- +#endif
- diff --git a/components/user_scripts/browser/user_script_loader.cc b/components/user_scripts/browser/user_script_loader.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_loader.cc
- @@ -0,0 +1,716 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include "user_script_loader.h"
- +
- +#include <stddef.h>
- +
- +#include <set>
- +#include <string>
- +#include <utility>
- +
- +#include "base/bind.h"
- +#include "base/memory/writable_shared_memory_region.h"
- +#include "base/strings/string_util.h"
- +#include "base/strings/strcat.h"
- +#include "base/files/file.h"
- +#include "base/files/file_util.h"
- +#include "base/files/file_enumerator.h"
- +#include "base/i18n/file_util_icu.h"
- +#include "base/path_service.h"
- +#include "base/base_paths_android.h"
- +#include "base/strings/utf_string_conversions.h"
- +#include "base/android/content_uri_utils.h"
- +#include "base/android/jni_android.h"
- +#include "base/task/task_traits.h"
- +#include "base/version.h"
- +
- +#include "crypto/sha2.h"
- +#include "base/base64.h"
- +
- +#include "build/build_config.h"
- +#include "content/public/browser/browser_context.h"
- +#include "content/public/browser/browser_task_traits.h"
- +#include "content/public/browser/browser_thread.h"
- +#include "content/public/browser/notification_service.h"
- +#include "content/public/browser/notification_types.h"
- +#include "content/public/browser/render_process_host.h"
- +#include "ui/shell_dialogs/select_file_dialog.h"
- +#include "content/browser/file_system_access/file_system_chooser.h"
- +#include "chrome/browser/ui/chrome_select_file_policy.h"
- +#include "ui/android/window_android.h"
- +
- +#include "../common/user_scripts_features.h"
- +#include "../common/extension_messages.h"
- +#include "file_task_runner.h"
- +#include "user_script_prefs.h"
- +#include "user_script_pref_info.h"
- +#include "../common/host_id.h"
- +
- +using content::BrowserThread;
- +using content::BrowserContext;
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +static const base::StringPiece kUserScriptBegin("// ==UserScript==");
- +static const base::StringPiece kUserScriptEnd("// ==/UserScript==");
- +static const base::StringPiece kNamespaceDeclaration("// @namespace");
- +static const base::StringPiece kNameDeclaration("// @name");
- +static const base::StringPiece kVersionDeclaration("// @version");
- +static const base::StringPiece kDescriptionDeclaration("// @description");
- +static const base::StringPiece kIncludeDeclaration("// @include");
- +static const base::StringPiece kExcludeDeclaration("// @exclude");
- +static const base::StringPiece kMatchDeclaration("// @match");
- +static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
- +static const base::StringPiece kRunAtDeclaration("// @run-at");
- +static const base::StringPiece kRunAtDocumentStartValue("document-start");
- +static const base::StringPiece kRunAtDocumentEndValue("document-end");
- +static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
- +static const base::StringPiece kUrlSourceDeclaration("// @url");
- +static const base::StringPiece kUrlHomePageDeclaration("// @homepage");
- +
- +// internal use
- +static const base::StringPiece kParserError("// @error");
- +static const base::StringPiece kForceDisabled("// @disabled");
- +
- +// Helper function to parse greasesmonkey headers
- +bool GetDeclarationValue(const base::StringPiece& line,
- + const base::StringPiece& prefix,
- + std::string* value) {
- + base::StringPiece::size_type index = line.find(prefix);
- + if (index == base::StringPiece::npos)
- + return false;
- +
- + std::string temp(line.data() + index + prefix.length(),
- + line.length() - index - prefix.length());
- +
- + if (temp.empty() || !base::IsUnicodeWhitespace(temp[0]))
- + return false;
- +
- + base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
- + return true;
- +}
- +
- +} // namespace
- +
- +// static
- +bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
- + std::unique_ptr<UserScript>& script,
- + bool *found_metadata,
- + std::string& error_message) {
- + // http://wiki.greasespot.net/Metadata_block
- + base::StringPiece line;
- + size_t line_start = 0;
- + size_t line_end = line_start;
- + *found_metadata = false;
- +
- + while (line_start < script_text.length()) {
- + line_end = script_text.find('\n', line_start);
- +
- + // Handle the case where there is no trailing newline in the file.
- + if (line_end == std::string::npos)
- + line_end = script_text.length() - 1;
- +
- + line = base::StringPiece(script_text.data() + line_start,
- + line_end - line_start);
- +
- + if (!*found_metadata) {
- + if (base::StartsWith(line, kUserScriptBegin))
- + *found_metadata = true;
- + } else {
- + if (base::StartsWith(line, kUserScriptEnd))
- + break;
- +
- + std::string value;
- + if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
- + // We escape some characters that MatchPattern() considers special.
- + base::ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
- + base::ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
- + script->add_glob(value);
- + } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
- + base::ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
- + base::ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
- + script->add_exclude_glob(value);
- + } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
- + script->set_name_space(value);
- + } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
- + script->set_name(value);
- + } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
- + base::Version version(value);
- + if (version.IsValid())
- + script->set_version(version.GetString());
- + } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
- + script->set_description(value);
- + } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
- + URLPattern pattern(UserScript::ValidUserScriptSchemes());
- + if (URLPattern::ParseResult::kSuccess != pattern.Parse(value)) {
- + error_message = "Invalid UserScript Schema " + value;
- + return false;
- + }
- + script->add_url_pattern(pattern);
- + } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
- + URLPattern exclude(UserScript::ValidUserScriptSchemes());
- + if (URLPattern::ParseResult::kSuccess != exclude.Parse(value)) {
- + error_message = "Invalid UserScript Schema " + value;
- + return false;
- + }
- + script->add_exclude_url_pattern(exclude);
- + } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
- + if (value == kRunAtDocumentStartValue)
- + script->set_run_location(UserScript::DOCUMENT_START);
- + else if (value == kRunAtDocumentEndValue)
- + script->set_run_location(UserScript::DOCUMENT_END);
- + else if (value == kRunAtDocumentIdleValue)
- + script->set_run_location(UserScript::DOCUMENT_IDLE);
- + else {
- + error_message = "Invalid RunAtDeclaration " + value;
- + return false;
- + }
- + } else if (GetDeclarationValue(line, kUrlSourceDeclaration, &value) ||
- + GetDeclarationValue(line, kUrlHomePageDeclaration, &value)) {
- + script->set_url_source(value);
- + } else if (GetDeclarationValue(line, kParserError, &value)) {
- + script->set_parser_error(value);
- + } else if (GetDeclarationValue(line, kForceDisabled, &value)) {
- + script->set_force_disabled();
- + }
- +
- + // TODO(aa): Handle more types of metadata.
- + }
- +
- + line_start = line_end + 1;
- + }
- +
- + // If no patterns were specified, default to @include *. This is what
- + // Greasemonkey does.
- + if (script->globs().empty() && script->url_patterns().is_empty())
- + script->add_glob("*");
- +
- + return true;
- +}
- +
- +// static
- +bool LoadUserScriptFromFile(
- + const base::FilePath& user_script_path, const GURL& original_url,
- + std::unique_ptr<UserScript>& script,
- + bool* found_metadata,
- + std::u16string* error) {
- +
- + base::File infile;
- + if (user_script_path.IsContentUri()) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: path " << user_script_path << " is a content uri";
- +
- + infile = OpenContentUriForRead(user_script_path);
- + } else {
- + infile = base::File(user_script_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
- + }
- +
- + if (!infile.IsValid()) {
- + base::File::Error out_error = infile.error_details();
- + *error = u"Cannot open script source. Error: " +
- + base::ASCIIToUTF16(base::File::ErrorToString(out_error));
- + return false;
- + }
- +
- + auto length = infile.GetLength();
- + if (length<=0) {
- + *error = u"File is empty.";
- + return false;
- + }
- +
- + auto buffer = std::vector<char>(length);
- + int bytes_read = infile.Read(0, buffer.data(), length);
- + if (bytes_read == -1) {
- + *error = u"Could not read source file.";
- + return false;
- + }
- +
- + std::string content(buffer.begin(), buffer.end());
- + if (!base::IsStringUTF8(content)) {
- + *error = u"User script must be UTF8 encoded.";
- + return false;
- + }
- +
- + std::string detailed_error;
- + bool parseResult = UserScriptLoader::ParseMetadataHeader(content, script,
- + found_metadata, detailed_error);
- + if (parseResult == false || *found_metadata == false) {
- + std::u16string detailed_error16;
- + base::UTF8ToUTF16(detailed_error.c_str(), detailed_error.length(), &detailed_error16);
- + *error = base::StrCat({u"Invalid script header. ", detailed_error16});
- + return false;
- + }
- +
- + script->set_match_origin_as_fallback(MatchOriginAsFallbackBehavior::kNever);
- +
- + std::unique_ptr<UserScript::File> file(new UserScript::File());
- + file->set_content(content);
- +
- + // create SHA256 of file
- + char raw[crypto::kSHA256Length] = {0};
- + std::string key;
- + crypto::SHA256HashString(content, raw, crypto::kSHA256Length);
- + base::Base64Encode(base::StringPiece(raw, crypto::kSHA256Length), &key);
- + file->set_key(key);
- +
- + file->set_url(GURL(base::StrCat({"https://userscripts/file/", key, ".js"})));
- +
- + script->js_scripts().push_back(std::move(file));
- +
- + // add into key the filename
- + // this value is used in ui to discriminate scripts
- + script->set_key(user_script_path.BaseName().value());
- + return true;
- +}
- +
- +// static
- +bool GetOrCreatePath(base::FilePath& path) {
- + base::PathService::Get(base::DIR_ANDROID_APP_DATA, &path);
- + path = path.AppendASCII("userscripts");
- +
- + // create snippets directory if not exists
- + if (!base::PathExists(path)) {
- + LOG(INFO) << "Path " << path << " doesn't exists. Creating";
- + base::File::Error error = base::File::FILE_OK;
- + if (!base::CreateDirectoryAndGetError(path, &error)) {
- + LOG(ERROR) <<
- + "UserScriptLoader: failed to create directory: " << path
- + << " with error code " << error;
- + return false;
- + }
- + }
- + return true;
- +}
- +
- +// static
- +void LoadUserScripts(UserScriptList* user_scripts_list) {
- + base::FilePath path;
- + if (GetOrCreatePath(path) == false) return;
- +
- + // enumerate all files from script path
- + // we accept all files, but we check if it's a real
- + // userscript
- + base::FileEnumerator dir_enum(
- + path,
- + /*recursive=*/false, base::FileEnumerator::FILES);
- + base::FilePath full_name;
- + while (full_name = dir_enum.Next(), !full_name.empty()) {
- + std::unique_ptr<UserScript> userscript(new UserScript());
- + userscript->set_id(UserScript::GenerateUserScriptID());
- + userscript->set_host_id(HostID(HostID::HostType::EXTENSIONS,
- + "_" + base::NumberToString(userscript->id())));
- +
- + std::u16string error;
- + bool found_metadata;
- + if (LoadUserScriptFromFile(full_name, GURL(), userscript, &found_metadata, &error)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: Found user script " << userscript->name() <<
- + "-" << userscript->version() <<
- + "-" << userscript->description();
- +
- + userscript->set_file_path(full_name.AsUTF8Unsafe());
- + user_scripts_list->push_back(std::move(userscript));
- + } else {
- + LOG(ERROR) << "UserScriptLoader: load error " << error;
- + }
- + }
- +}
- +
- +UserScriptLoader::UserScriptLoader(BrowserContext* browser_context,
- + UserScriptsPrefs* prefs)
- + : loaded_scripts_(new UserScriptList()),
- + ready_(false),
- + browser_context_(browser_context),
- + prefs_(prefs) {}
- +
- +UserScriptLoader::~UserScriptLoader() {
- + for (auto& observer : observers_)
- + observer.OnUserScriptLoaderDestroyed(this);
- +}
- +
- +void UserScriptLoader::OnRenderProcessHostCreated(
- + content::RenderProcessHost* process_host) {
- + if (initial_load_complete()) {
- + SendUpdate(process_host, shared_memory_);
- + }
- +}
- +
- +void UserScriptLoader::SetReady(bool ready) {
- + bool was_ready = ready_;
- + ready_ = ready;
- + if (ready_ && !was_ready)
- + AttemptLoad();
- +}
- +
- +void UserScriptLoader::AttemptLoad() {
- + int tryOut = prefs_->GetCurrentStartupTryout();
- + if (tryOut >= 3) {
- + LOG(INFO) << "UserScriptLoader: Possible crash detected. UserScript disabled";
- + prefs_->SetEnabled(false);
- + } else {
- + prefs_->StartupTryout(tryOut+1);
- + StartLoad();
- + }
- +}
- +
- +void UserScriptLoader::StartLoad() {
- + DCHECK_CURRENTLY_ON(BrowserThread::UI);
- +
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: StartLoad";
- +
- + // Reload any loaded scripts, and clear out |loaded_scripts_| to indicate that
- + // the scripts aren't currently ready.
- + std::unique_ptr<UserScriptList> scripts_to_load = std::move(loaded_scripts_);
- + scripts_to_load->clear();
- +
- + if (prefs_->IsEnabled()) {
- + LoadScripts(std::move(scripts_to_load),
- + base::BindOnce(&UserScriptLoader::OnScriptsLoaded,
- + weak_factory_.GetWeakPtr()));
- + } else {
- + OnScriptsLoaded(std::move(scripts_to_load));
- + }
- +}
- +
- +// static
- +base::ReadOnlySharedMemoryRegion UserScriptLoader::Serialize(
- + const UserScriptList& scripts) {
- + base::Pickle pickle;
- + pickle.WriteUInt32(scripts.size());
- + for (const std::unique_ptr<UserScript>& script : scripts) {
- + // TODO(aa): This can be replaced by sending content script metadata to
- + // renderers along with other extension data in ExtensionMsg_Loaded.
- + // See crbug.com/70516.
- + script->Pickle(&pickle);
- + // Write scripts as 'data' so that we can read it out in the slave without
- + // allocating a new string.
- + for (const std::unique_ptr<UserScript::File>& js_file :
- + script->js_scripts()) {
- + base::StringPiece contents = js_file->GetContent();
- + pickle.WriteData(contents.data(), contents.length());
- + }
- + for (const std::unique_ptr<UserScript::File>& css_file :
- + script->css_scripts()) {
- + base::StringPiece contents = css_file->GetContent();
- + pickle.WriteData(contents.data(), contents.length());
- + }
- + }
- +
- + // Create the shared memory object.
- + base::MappedReadOnlyRegion shared_memory =
- + base::ReadOnlySharedMemoryRegion::Create(pickle.size());
- + if (!shared_memory.IsValid())
- + return {};
- +
- + // Copy the pickle to shared memory.
- + memcpy(shared_memory.mapping.memory(), pickle.data(), pickle.size());
- + return std::move(shared_memory.region);
- +}
- +
- +void UserScriptLoader::AddObserver(Observer* observer) {
- + observers_.AddObserver(observer);
- +}
- +
- +void UserScriptLoader::RemoveObserver(Observer* observer) {
- + observers_.RemoveObserver(observer);
- +}
- +
- +void UserScriptLoader::OnScriptsLoaded(
- + std::unique_ptr<UserScriptList> user_scripts) {
- +
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: OnScriptsLoaded";
- +
- + // Check user preferences for loaded user scripts
- + prefs_->CompareWithPrefs(*user_scripts);
- + loaded_scripts_ = std::move(user_scripts);
- +
- + base::ReadOnlySharedMemoryRegion shared_memory;
- + shared_memory =
- + UserScriptLoader::Serialize(*loaded_scripts_);
- +
- + if (!shared_memory.IsValid()) {
- + // This can happen if we run out of file descriptors. In that case, we
- + // have a choice between silently omitting all user scripts for new tabs,
- + // by nulling out shared_memory_, or only silently omitting new ones by
- + // leaving the existing object in place. The second seems less bad, even
- + // though it removes the possibility that freeing the shared memory block
- + // would open up enough FDs for long enough for a retry to succeed.
- +
- + // Pretend the extension change didn't happen.
- + return;
- + }
- +
- + // We've got scripts ready to go.
- + shared_memory_ = std::move(shared_memory);
- +
- + for (content::RenderProcessHost::iterator i(
- + content::RenderProcessHost::AllHostsIterator());
- + !i.IsAtEnd(); i.Advance()) {
- + SendUpdate(i.GetCurrentValue(), shared_memory_);
- + }
- +
- + // DCHECK(false); trying crash
- + prefs_->StartupTryout(0);
- +
- + for (auto& observer : observers_)
- + observer.OnScriptsLoaded(this, browser_context_);
- +}
- +
- +void UserScriptLoader::SendUpdate(
- + content::RenderProcessHost* process,
- + const base::ReadOnlySharedMemoryRegion& shared_memory) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: SendUpdate";
- +
- + // If the process is being started asynchronously, early return. We'll end up
- + // calling InitUserScripts when it's created which will call this again.
- + base::ProcessHandle handle = process->GetProcess().Handle();
- + if (!handle)
- + return;
- +
- + base::ReadOnlySharedMemoryRegion region_for_process =
- + shared_memory.Duplicate();
- + if (!region_for_process.IsValid())
- + return;
- +
- + process->Send(new ExtensionMsg_UpdateUserScripts(
- + std::move(region_for_process)));
- +}
- +
- +void LoadScriptsOnFileTaskRunner(
- + std::unique_ptr<UserScriptList> user_scripts,
- + UserScriptLoader::LoadScriptsCallback callback) {
- + DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
- + DCHECK(user_scripts.get());
- +
- + // load user scripts from path
- + LoadUserScripts(user_scripts.get());
- +
- + // Explicit priority to prevent unwanted task priority inheritance.
- + content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
- + ->PostTask(FROM_HERE,
- + base::BindOnce(std::move(callback), std::move(user_scripts)));
- +}
- +
- +void UserScriptLoader::LoadScripts(
- + std::unique_ptr<UserScriptList> user_scripts,
- + LoadScriptsCallback callback) {
- + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- +
- + GetUserScriptsFileTaskRunner()->PostTask(
- + FROM_HERE,
- + base::BindOnce(&LoadScriptsOnFileTaskRunner, std::move(user_scripts),
- + std::move(callback)));
- +}
- +
- +void RemoveScriptsOnFileTaskRunner(
- + const std::string& script_id,
- + UserScriptLoader::RemoveScriptCallback callback) {
- + DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
- +
- + base::FilePath path;
- + if (GetOrCreatePath(path)) {
- + base::FilePath file = path.Append(script_id);
- + if (base::DeleteFile(file) == false) {
- + LOG(ERROR) <<
- + "ERROR: failed to delete file : " << path;
- + }
- + }
- +
- + content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
- + ->PostTask(FROM_HERE,
- + base::BindOnce(std::move(callback)));
- +}
- +
- +void UserScriptLoader::OnScriptRemoved() {
- + StartLoad();
- +}
- +
- +void UserScriptLoader::RemoveScript(const std::string& script_id) {
- + if (!prefs_->IsEnabled()) return;
- + prefs_->RemoveScriptFromPrefs(script_id);
- +
- + GetUserScriptsFileTaskRunner()->PostTask(
- + FROM_HERE,
- + base::BindOnce(&RemoveScriptsOnFileTaskRunner,
- + std::move(script_id),
- + base::BindOnce(&UserScriptLoader::OnScriptRemoved,
- + weak_factory_.GetWeakPtr())));
- +}
- +
- +void UserScriptLoader::SetScriptEnabled(const std::string& script_id, bool is_enabled) {
- + if (!prefs_->IsEnabled()) return;
- + prefs_->SetScriptEnabled(script_id, is_enabled);
- + StartLoad();
- +}
- +
- +void UserScriptLoader::SelectAndAddScriptFromFile(ui::WindowAndroid* nativeWindow) {
- + DCHECK_CURRENTLY_ON(BrowserThread::UI);
- +
- + if (!prefs_->IsEnabled()) return;
- +
- + dialog_ = ui::SelectFileDialog::Create(
- + this, std::make_unique<ChromeSelectFilePolicy>(nullptr /*web_contents*/));
- +
- + ui::SelectFileDialog::FileTypeInfo allowed_file_info;
- + allowed_file_info.extensions = {{FILE_PATH_LITERAL("js")}};
- + allowed_file_info.allowed_paths =
- + ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
- + base::FilePath suggested_name;
- +
- + std::vector<std::u16string> types;
- + types.push_back(u"*/*"); /*= java SelectFileDialog.ALL_TYPES*/
- + std::pair<std::vector<std::u16string>, bool> accept_types = std::make_pair(
- + types, false /*use_media_capture*/);
- +
- + dialog_->SelectFile(
- + ui::SelectFileDialog::SELECT_OPEN_FILE,
- + std::u16string() /* dialog title*/, suggested_name, &allowed_file_info,
- + 0 /* file type index */, std::string() /* default file extension */,
- + nativeWindow,
- + &accept_types /* params */);
- +}
- +
- +
- +void LoadScriptFromPathOnFileTaskRunner(
- + const base::FilePath& path,
- + const std::string& display_name,
- + UserScriptLoader::LoadSingleScriptCallback callback ) {
- + DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
- +
- + std::unique_ptr<UserScript> userscript(new UserScript());
- + std::u16string error;
- + bool found_metadata = false;
- + bool loaded = LoadUserScriptFromFile(path, GURL(), userscript, &found_metadata, &error);
- +
- + bool result = loaded;
- + if (result || found_metadata) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: found " << userscript->name() <<
- + "-" << userscript->version() <<
- + "-" << userscript->description();
- + base::FilePath destination;
- + if (GetOrCreatePath(destination) == false) {
- + error = u"Cannot create destination.";
- + } else {
- + // we need an unique file name
- + if (display_name.empty() == false) {
- + userscript->set_key(display_name);
- + }
- +
- + // filename is original filename or display_name
- + std::string file_name(userscript->key());
- + base::i18n::ReplaceIllegalCharactersInPath(&file_name, '_');
- + destination = destination.Append(file_name);
- +
- + if (destination.ReferencesParent()) {
- + error = u"Invalid file name.";
- + result = false;
- + } else if (base::PathExists(destination)) {
- + error = u"User script already loaded.";
- + result = false;
- + } else {
- + if (loaded) {
- + // if is a correct userscript, copy it
- + result = base::CopyFile(path, destination);
- + if (result == false) {
- + error = u"Copy error.";
- + }
- + } else {
- + // else, there is a parser error
- + // write minimal values and the error string, so UI can show it
- + std::string combined_string = base::StrCat({
- + kUserScriptBegin, "\n",
- + kNamespaceDeclaration, " ", userscript->name_space(), "\n",
- + kNameDeclaration, " ", userscript->name(), "\n",
- + kVersionDeclaration, " ", userscript->version(), "\n",
- + kDescriptionDeclaration, " ", userscript->description(), "\n",
- + kUrlSourceDeclaration, " ", userscript->url_source(), "\n",
- + kParserError, " ", base::UTF16ToASCII(error), "\n",
- + kForceDisabled, " true\n",
- + kUserScriptEnd, "\n"
- + });
- +
- + if (!base::WriteFile(destination, combined_string)) {
- + error = u"Cannot write.";
- + result = false;
- + }
- + }
- + }
- + }
- + }
- +
- + if (!error.empty()) {
- + LOG(ERROR) << "UserScriptLoader: load error " << error;
- + }
- +
- + // return to callback with eventually the error
- + const std::string string_error = base::UTF16ToASCII(error);
- + content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
- + ->PostTask(FROM_HERE,
- + base::BindOnce(std::move(callback), result,
- + std::move(string_error)));
- +}
- +
- +void UserScriptLoader::TryToInstall(const base::FilePath& script_path) {
- + if (!prefs_->IsEnabled()) return;
- +
- + std::u16string file_display_name;
- + base::MaybeGetFileDisplayName(script_path, &file_display_name);
- +
- + std::string display_name = script_path.BaseName().value();
- + if (base::IsStringASCII(file_display_name))
- + display_name = base::UTF16ToASCII(file_display_name);
- +
- + GetUserScriptsFileTaskRunner()->PostTask(
- + FROM_HERE,
- + base::BindOnce(
- + &LoadScriptFromPathOnFileTaskRunner,
- + script_path, display_name,
- + base::BindOnce(
- + &UserScriptLoader::LoadScriptFromPathOnFileTaskRunnerCallback,
- + weak_factory_.GetWeakPtr()
- + )
- + ));
- +}
- +
- +void UserScriptLoader::FileSelected(
- + const base::FilePath& path, int index, void* params) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptLoader: FileSelected " << path;
- +
- + UserScriptLoader::TryToInstall(path);
- +}
- +
- +void UserScriptLoader::LoadScriptFromPathOnFileTaskRunnerCallback(
- + bool result, const std::string& error) {
- + for (auto& observer : observers_)
- + observer.OnUserScriptLoaded(this, result, error);
- +
- + StartLoad();
- +}
- +
- +void UserScriptLoader::FileSelectionCanceled(
- + void* params) {
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/browser/user_script_loader.h b/components/user_scripts/browser/user_script_loader.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_loader.h
- @@ -0,0 +1,169 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
- +#define USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
- +
- +#include <map>
- +#include <memory>
- +#include <set>
- +
- +#include "base/callback_forward.h"
- +#include "base/compiler_specific.h"
- +#include "base/memory/read_only_shared_memory_region.h"
- +#include "base/memory/weak_ptr.h"
- +#include "base/observer_list.h"
- +#include "content/public/browser/render_process_host_creation_observer.h"
- +#include "ui/shell_dialogs/select_file_dialog.h"
- +#include "content/browser/file_system_access/file_system_chooser.h"
- +#include "ui/android/window_android.h"
- +
- +#include "../common/host_id.h"
- +#include "../common/user_script.h"
- +#include "user_script_prefs.h"
- +
- +namespace base {
- +class ReadOnlySharedMemoryRegion;
- +}
- +
- +namespace content {
- +class BrowserContext;
- +class RenderProcessHost;
- +}
- +
- +namespace user_scripts {
- +
- +// Manages one "logical unit" of user scripts in shared memory by constructing a
- +// new shared memory region when the set of scripts changes. Also notifies
- +// renderers of new shared memory region when new renderers appear, or when
- +// script reloading completes. Script loading lives on the file thread.
- +class UserScriptLoader : public content::RenderProcessHostCreationObserver,
- + public ui::SelectFileDialog::Listener {
- + public:
- + UserScriptLoader(const UserScriptLoader&) = delete;
- + UserScriptLoader& operator=(const UserScriptLoader&) = delete;
- + using LoadScriptsCallback =
- + base::OnceCallback<void(std::unique_ptr<UserScriptList>)>;
- + using LoadSingleScriptCallback =
- + base::OnceCallback<void(bool result, const std::string& error)>;
- +
- + using RemoveScriptCallback =
- + base::OnceCallback<void()>;
- + class Observer {
- + public:
- + virtual void OnScriptsLoaded(UserScriptLoader* loader,
- + content::BrowserContext* browser_context) = 0;
- + virtual void OnUserScriptLoaderDestroyed(UserScriptLoader* loader) = 0;
- + virtual void OnUserScriptLoaded(UserScriptLoader* loader,
- + bool result, const std::string& error) = 0;
- + };
- +
- + // Parses the includes out of |script| and returns them in |includes|.
- + static bool ParseMetadataHeader(const base::StringPiece& script_text,
- + std::unique_ptr<UserScript>& script,
- + bool *found_metadata,
- + std::string& error_message);
- +
- + UserScriptLoader(content::BrowserContext* browser_context,
- + UserScriptsPrefs* prefs);
- + //const HostID& host_id);
- + ~UserScriptLoader() override;
- +
- + // Initiates procedure to start loading scripts on the file thread.
- + void StartLoad();
- +
- + // Returns true if we have any scripts ready.
- + bool initial_load_complete() const { return shared_memory_.IsValid(); }
- +
- + // Pickle user scripts and return pointer to the shared memory.
- + static base::ReadOnlySharedMemoryRegion Serialize(
- + const user_scripts::UserScriptList& scripts);
- +
- + // Adds or removes observers.
- + void AddObserver(Observer* observer);
- + void RemoveObserver(Observer* observer);
- +
- + // Sets the flag if the initial set of hosts has finished loading; if it's
- + // set to be true, calls AttempLoad() to bootstrap.
- + void SetReady(bool ready);
- +
- + void RemoveScript(const std::string& script_id);
- + void SetScriptEnabled(const std::string& script_id, bool is_enabled);
- +
- + void SelectAndAddScriptFromFile(ui::WindowAndroid* wa);
- + void TryToInstall(const base::FilePath& script_path);
- +
- + void LoadScripts(std::unique_ptr<UserScriptList> user_scripts,
- + LoadScriptsCallback callback);
- +
- + protected:
- + content::BrowserContext* browser_context() const { return browser_context_; }
- +
- + UserScriptsPrefs* prefs() const { return prefs_; }
- +
- + private:
- + void OnRenderProcessHostCreated(
- + content::RenderProcessHost* process_host) override;
- +
- + // Attempts to initiate a load.
- + void AttemptLoad();
- +
- + // Called once we have finished loading the scripts on the file thread.
- + void OnScriptsLoaded(std::unique_ptr<UserScriptList> user_scripts);
- +
- + // Sends the renderer process a new set of user scripts. If
- + // |changed_hosts| is not empty, this signals that only the scripts from
- + // those hosts should be updated. Otherwise, all hosts will be
- + // updated.
- + void SendUpdate(content::RenderProcessHost* process,
- + const base::ReadOnlySharedMemoryRegion& shared_memory);
- +
- + // Contains the scripts that were found the last time scripts were updated.
- + base::ReadOnlySharedMemoryRegion shared_memory_;
- +
- + // List of scripts that are currently loaded. This is null when a load is in
- + // progress.
- + std::unique_ptr<UserScriptList> loaded_scripts_;
- +
- + // If the initial set of hosts has finished loading.
- + bool ready_;
- +
- + // The browser_context for which the scripts managed here are installed.
- + content::BrowserContext* browser_context_;
- +
- + // Manage load and store from preferences
- + UserScriptsPrefs* prefs_;
- +
- + // The associated observers.
- + base::ObserverList<Observer>::Unchecked observers_;
- +
- + void OnScriptRemoved();
- +
- + // Manage file dialog requests
- + scoped_refptr<ui::SelectFileDialog> dialog_;
- + void FileSelected(const base::FilePath& path,
- + int index, void* params) override;
- + void FileSelectionCanceled(void* params) override;
- + void LoadScriptFromPathOnFileTaskRunnerCallback(
- + bool result, const std::string& error );
- +
- + base::WeakPtrFactory<UserScriptLoader> weak_factory_{this};
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
- diff --git a/components/user_scripts/browser/user_script_pref_info.cc b/components/user_scripts/browser/user_script_pref_info.cc
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_pref_info.cc
- @@ -0,0 +1,34 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include "user_script_pref_info.h"
- +
- +namespace user_scripts {
- +
- +UserScriptsListPrefs::ScriptInfo::ScriptInfo(const std::string& name,
- + const std::string& description,
- + const base::Time& install_time,
- + bool enabled)
- + : install_time(install_time),
- + enabled(enabled),
- + name_(name),
- + description_(description) {}
- +
- +UserScriptsListPrefs::ScriptInfo::ScriptInfo(const ScriptInfo& other) = default;
- +UserScriptsListPrefs::ScriptInfo::~ScriptInfo() = default;
- +
- +}
- diff --git a/components/user_scripts/browser/user_script_pref_info.h b/components/user_scripts/browser/user_script_pref_info.h
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_pref_info.h
- @@ -0,0 +1,72 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
- +#define USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
- +
- +#include "base/values.h"
- +#include "base/time/time.h"
- +#include "components/keyed_service/core/keyed_service.h"
- +
- +namespace user_scripts {
- +
- +class UserScriptsListPrefs : public KeyedService {
- + public:
- + struct ScriptInfo {
- + ScriptInfo(const std::string& name,
- + const std::string& description,
- + const base::Time& install_time,
- + bool enabled);
- + ScriptInfo(const ScriptInfo& other);
- + ~ScriptInfo();
- +
- + const std::string& name() const { return name_; }
- + void set_name(const std::string& name) { name_ = name; }
- +
- + const std::string& description() const { return description_; }
- + void set_description(const std::string& description) { description_ = description; }
- +
- + const std::string& version() const { return version_; }
- + void set_version(const std::string& version) { version_ = version; }
- +
- + const std::string& file_path() const { return file_path_; }
- + void set_file_path(const std::string& file_path) { file_path_ = file_path; }
- +
- + const std::string& url_source() const { return url_source_; }
- + void set_url_source(const std::string& url_source) { url_source_ = url_source; }
- +
- + const std::string& parser_error() const { return parser_error_; }
- + void set_parser_error(const std::string& parser_error) { parser_error_ = parser_error; }
- +
- + base::Time install_time;
- + bool enabled;
- +
- + bool force_disabled;
- +
- + private:
- + std::string name_;
- + std::string description_;
- + std::string version_;
- + std::string file_path_;
- + std::string url_source_;
- + std::string parser_error_;
- + };
- +};
- +
- +}
- +
- +#endif // USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
- diff --git a/components/user_scripts/browser/user_script_prefs.cc b/components/user_scripts/browser/user_script_prefs.cc
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_prefs.cc
- @@ -0,0 +1,278 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include <map>
- +
- +#include "base/values.h"
- +#include "base/strings/string_number_conversions.h"
- +#include "base/json/json_writer.h"
- +
- +#include "chrome/browser/browser_process.h"
- +#include "chrome/browser/profiles/profile.h"
- +#include "chrome/browser/profiles/profile_manager.h"
- +
- +#include "components/prefs/pref_registry_simple.h"
- +#include "components/prefs/pref_service.h"
- +#include "components/prefs/scoped_user_pref_update.h"
- +#include "components/pref_registry/pref_registry_syncable.h"
- +#include "user_script_prefs.h"
- +#include "user_script_pref_info.h"
- +#include "../common/user_script.h"
- +#include "../common/user_scripts_features.h"
- +
- +namespace user_scripts {
- +
- +namespace prefs {
- + const char kUserScriptsEnabled[] = "userscripts.enabled";
- +}
- +
- +namespace {
- +
- +const char kUserScriptsStartup[] = "userscripts.startup";
- +
- +const char kUserScriptsList[] = "userscripts.scripts";
- +const char kScriptIsEnabled[] = "enabled";
- +const char kScriptName[] = "name";
- +const char kScriptDescription[] = "description";
- +const char kScriptVersion[] = "version";
- +const char kScriptInstallTime[] = "install_time";
- +const char kScriptFilePath[] = "file_path";
- +const char kScriptUrlSource[] = "url_source";
- +const char kScriptParserError[] = "parser_error";
- +const char kScriptForceDisabled[] = "force_disabled";
- +
- +class PrefUpdate : public DictionaryPrefUpdate {
- + public:
- + PrefUpdate(PrefService* service,
- + const std::string& id,
- + const std::string& path)
- + : DictionaryPrefUpdate(service, path), id_(id) {}
- +
- + PrefUpdate(const PrefUpdate&) = delete;
- + PrefUpdate& operator=(const PrefUpdate&) = delete;
- + ~PrefUpdate() override = default;
- +
- + base::Value* Get() override {
- + base::Value* dict = DictionaryPrefUpdate::Get();
- + base::Value* dict_item =
- + dict->FindKeyOfType(id_, base::Value::Type::DICTIONARY);
- + if (!dict_item)
- + dict_item = dict->SetKey(id_, base::Value(base::Value::Type::DICTIONARY));
- + return dict_item;
- + }
- +
- + private:
- + const std::string id_;
- +};
- +
- +bool GetInt64FromPref(const base::DictionaryValue* dict,
- + const std::string& key,
- + int64_t* value) {
- + DCHECK(dict);
- + const std::string* value_str = dict->FindStringKey(key);
- + if (!value_str) {
- + VLOG(2) << "Can't find key in local pref dictionary. Invalid key: " << key
- + << ".";
- + return false;
- + }
- +
- + if (!base::StringToInt64(*value_str, value)) {
- + VLOG(2) << "Can't change string to int64_t. Invalid string value: "
- + << *value_str << ".";
- + return false;
- + }
- +
- + return true;
- +}
- +
- +}
- +
- +UserScriptsPrefs::UserScriptsPrefs(
- + PrefService* prefs)
- + : prefs_(prefs) {
- +}
- +
- +// static
- +void UserScriptsPrefs::RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
- + registry->RegisterBooleanPref(prefs::kUserScriptsEnabled, false);
- + registry->RegisterIntegerPref(kUserScriptsStartup, 0);
- + registry->RegisterDictionaryPref(kUserScriptsList);
- +}
- +
- +bool UserScriptsPrefs::IsEnabled() {
- + return prefs_->GetBoolean(prefs::kUserScriptsEnabled);
- +}
- +
- +void UserScriptsPrefs::SetEnabled(bool enabled) {
- + prefs_->SetBoolean(prefs::kUserScriptsEnabled, enabled);
- + prefs_->CommitPendingWrite();
- +}
- +
- +void UserScriptsPrefs::StartupTryout(int number) {
- + prefs_->SetInteger(kUserScriptsStartup, number);
- + prefs_->CommitPendingWrite();
- +}
- +
- +int UserScriptsPrefs::GetCurrentStartupTryout() {
- + return prefs_->GetInteger(kUserScriptsStartup);
- +}
- +
- +void UserScriptsPrefs::CompareWithPrefs(UserScriptList& user_scripts) {
- + if (IsEnabled() == false) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScriptsPrefs: disabled by user";
- +
- + user_scripts.clear();
- + return;
- + }
- +
- + std::vector<std::string> all_scripts;
- +
- + auto it = user_scripts.begin();
- + while (it != user_scripts.end())
- + {
- + std::string key = it->get()->key();
- + all_scripts.push_back(key);
- +
- + std::unique_ptr<UserScriptsListPrefs::ScriptInfo> scriptInfo =
- + UserScriptsPrefs::CreateScriptInfoFromPrefs(key);
- +
- + // add or update prefs
- + scriptInfo->set_name(it->get()->name());
- + scriptInfo->set_description(it->get()->description());
- + scriptInfo->set_version(it->get()->version());
- + scriptInfo->set_file_path(it->get()->file_path());
- + scriptInfo->set_url_source(it->get()->url_source());
- + scriptInfo->set_parser_error(it->get()->parser_error());
- + scriptInfo->force_disabled = (it->get()->force_disabled());
- +
- + PrefUpdate update(prefs_, key, kUserScriptsList);
- + base::Value* script_dict = update.Get();
- +
- + script_dict->SetStringKey(kScriptName, scriptInfo->name());
- + script_dict->SetStringKey(kScriptDescription, scriptInfo->description());
- + script_dict->SetBoolKey(kScriptIsEnabled, scriptInfo->enabled);
- + script_dict->SetStringKey(kScriptVersion, scriptInfo->version());
- + script_dict->SetStringKey(kScriptFilePath, scriptInfo->file_path());
- + script_dict->SetStringKey(kScriptUrlSource, scriptInfo->url_source());
- + script_dict->SetStringKey(kScriptParserError, scriptInfo->parser_error());
- + script_dict->SetBoolKey(kScriptForceDisabled, scriptInfo->force_disabled);
- +
- + std::string install_time_str =
- + base::NumberToString(scriptInfo->install_time.ToInternalValue());
- + script_dict->SetStringKey(kScriptInstallTime, install_time_str);
- +
- + if (!scriptInfo->enabled) {
- + it = user_scripts.erase(it);
- + } else {
- + ++it;
- + }
- + }
- +
- + // remove script from prefs if no more present
- + std::vector<std::string> all_scripts_to_remove;
- + const base::DictionaryValue* dict =
- + &base::Value::AsDictionaryValue(*prefs_->GetDictionary(
- + kUserScriptsList));
- + for (base::DictionaryValue::Iterator script_it(*dict); !script_it.IsAtEnd();
- + script_it.Advance()) {
- + const std::string& key = script_it.key();
- +
- + if (std::find(all_scripts.begin(), all_scripts.end(), key) == all_scripts.end()) {
- + all_scripts_to_remove.push_back(key);
- + }
- + }
- +
- + DictionaryPrefUpdate update(prefs_, kUserScriptsList);
- + base::Value* const update_dict = update.Get();
- + for (auto key : all_scripts_to_remove) {
- + update_dict->RemoveKey(key);
- + }
- +
- + return;
- +}
- +
- +std::string UserScriptsPrefs::GetScriptsInfo() {
- + std::string json_string;
- +
- + const base::Value* dict =
- + prefs_->GetDictionary(kUserScriptsList);
- +
- + if (dict) {
- + base::JSONWriter::WriteWithOptions(
- + *dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
- + base::TrimWhitespaceASCII(json_string, base::TRIM_ALL, &json_string);
- + }
- +
- + return json_string;
- +}
- +
- +std::unique_ptr<UserScriptsListPrefs::ScriptInfo> UserScriptsPrefs::CreateScriptInfoFromPrefs(
- + const std::string& script_id) const {
- +
- + auto scriptInfo = std::make_unique<UserScriptsListPrefs::ScriptInfo>(
- + script_id, "", base::Time::Now(), false);
- +
- + const base::Value* scripts =
- + prefs_->GetDictionary(kUserScriptsList);
- + if (!scripts)
- + return scriptInfo;
- +
- + const base::DictionaryValue* script = static_cast<const base::DictionaryValue*>(
- + scripts->FindDictKey(script_id));
- + if (!script)
- + return scriptInfo;
- +
- + const std::string* name = script->FindStringKey(kScriptName);
- + const std::string* description = script->FindStringKey(kScriptDescription);
- + const std::string* version = script->FindStringKey(kScriptVersion);
- + const std::string* file_path = script->FindStringKey(kScriptFilePath);
- + const std::string* url_source = script->FindStringKey(kScriptUrlSource);
- + const std::string* parser_error = script->FindStringKey(kScriptParserError);
- +
- + scriptInfo->set_name( name ? *name : "no name" );
- + scriptInfo->set_description( description ? *description : "no description" );
- + scriptInfo->set_version( version ? *version : "no version" );
- + scriptInfo->enabled = script->FindBoolKey(kScriptIsEnabled).value_or(false);
- + scriptInfo->set_file_path( file_path ? *file_path : "no file path" );
- + scriptInfo->set_url_source( url_source ? *url_source : "" );
- + scriptInfo->set_parser_error( parser_error ? *parser_error : "" );
- + scriptInfo->force_disabled = script->FindBoolKey(kScriptForceDisabled).value_or(false);
- +
- + int64_t time_interval = 0;
- + if (GetInt64FromPref(script, kScriptInstallTime, &time_interval)) {
- + scriptInfo->install_time = base::Time::FromInternalValue(time_interval);
- + }
- +
- + return scriptInfo;
- +}
- +
- +void UserScriptsPrefs::RemoveScriptFromPrefs(const std::string& script_id) {
- + DictionaryPrefUpdate update(prefs_, kUserScriptsList);
- + base::Value* const update_dict = update.Get();
- + update_dict->RemoveKey(script_id);
- +}
- +
- +void UserScriptsPrefs::SetScriptEnabled(const std::string& script_id, bool is_enabled) {
- + PrefUpdate update(prefs_, script_id, kUserScriptsList);
- + base::Value* script_dict = update.Get();
- + if (script_dict->FindBoolKey(kScriptForceDisabled).value_or(false))
- + is_enabled = true;
- + script_dict->SetBoolKey(kScriptIsEnabled, is_enabled);
- +}
- +
- +}
- diff --git a/components/user_scripts/browser/user_script_prefs.h b/components/user_scripts/browser/user_script_prefs.h
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/browser/user_script_prefs.h
- @@ -0,0 +1,62 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
- +#define USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
- +
- +#include "content/public/browser/browser_context.h"
- +#include "components/prefs/pref_service.h"
- +#include "components/pref_registry/pref_registry_syncable.h"
- +
- +#include "user_script_pref_info.h"
- +#include "../common/user_script.h"
- +
- +namespace user_scripts {
- +
- +namespace prefs {
- + extern const char kUserScriptsEnabled[];
- +}
- +
- +class UserScriptsPrefs {
- + public:
- + UserScriptsPrefs(
- + PrefService* prefs);
- +
- + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
- +
- + bool IsEnabled();
- + void SetEnabled(bool enabled);
- +
- + void StartupTryout(int number);
- + int GetCurrentStartupTryout();
- +
- + void CompareWithPrefs(UserScriptList& user_scripts);
- +
- + std::string GetScriptsInfo();
- + void RemoveScriptFromPrefs(const std::string& script_id);
- + void SetScriptEnabled(const std::string& script_id, bool is_enabled);
- +
- + std::unique_ptr<UserScriptsListPrefs::ScriptInfo> CreateScriptInfoFromPrefs(
- + const std::string& script_id) const;
- +
- + private:
- + PrefService* prefs_;
- +};
- +
- +}
- +
- +#endif // USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
- diff --git a/components/user_scripts/browser/userscripts_browser_client.cc b/components/user_scripts/browser/userscripts_browser_client.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/userscripts_browser_client.cc
- @@ -0,0 +1,78 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include "userscripts_browser_client.h"
- +
- +#include "base/logging.h"
- +
- +#include "chrome/browser/browser_process.h"
- +
- +#include "chrome/browser/profiles/profile.h"
- +#include "chrome/browser/profiles/profile_manager.h"
- +
- +#include "../common/user_scripts_features.h"
- +#include "user_script_loader.h"
- +#include "file_task_runner.h"
- +#include "user_script_prefs.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +// remember: was ExtensionsBrowserClient
- +UserScriptsBrowserClient* g_userscripts_browser_client = NULL;
- +
- +} // namespace
- +
- +UserScriptsBrowserClient::UserScriptsBrowserClient() {}
- +
- +UserScriptsBrowserClient::~UserScriptsBrowserClient() = default;
- +
- +// static
- +UserScriptsBrowserClient* UserScriptsBrowserClient::GetInstance() {
- + // only for browser process
- + if (!g_browser_process)
- + return NULL;
- +
- + // singleton
- + if (g_userscripts_browser_client)
- + return g_userscripts_browser_client;
- +
- + // make file task runner
- + GetUserScriptsFileTaskRunner().get();
- +
- + // new instance singleton
- + g_userscripts_browser_client = new UserScriptsBrowserClient();
- +
- + return g_userscripts_browser_client;
- +}
- +
- +void UserScriptsBrowserClient::SetProfile(content::BrowserContext* context) {
- + browser_context_ = context;
- +
- + prefs_ =
- + std::make_unique<user_scripts::UserScriptsPrefs>(
- + static_cast<Profile*>(context)->GetPrefs());
- +
- + userscript_loader_ =
- + std::make_unique<user_scripts::UserScriptLoader>(browser_context_, prefs_.get());
- + if (prefs_->IsEnabled()) {
- + userscript_loader_->SetReady(true);
- + }
- +}
- +
- +} // namespace user_scripts
- diff --git a/components/user_scripts/browser/userscripts_browser_client.h b/components/user_scripts/browser/userscripts_browser_client.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/browser/userscripts_browser_client.h
- @@ -0,0 +1,62 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
- +#define USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
- +
- +#include <memory>
- +#include <string>
- +#include <vector>
- +
- +#include "content/public/browser/browser_context.h"
- +
- +#include "../common/user_script.h"
- +#include "user_script_loader.h"
- +#include "user_script_prefs.h"
- +
- +namespace user_scripts {
- +
- +class UserScriptsBrowserClient {
- + public:
- + UserScriptsBrowserClient();
- + UserScriptsBrowserClient(const UserScriptsBrowserClient&) = delete;
- + UserScriptsBrowserClient& operator=(const UserScriptsBrowserClient&) = delete;
- + virtual ~UserScriptsBrowserClient();
- +
- + // Returns the single instance of |this|.
- + static UserScriptsBrowserClient* GetInstance();
- +
- + void SetProfile(content::BrowserContext* context);
- +
- + user_scripts::UserScriptsPrefs* GetPrefs() {
- + return prefs_.get();
- + }
- +
- + user_scripts::UserScriptLoader* GetLoader() {
- + return userscript_loader_.get();
- + }
- +
- + private:
- + std::unique_ptr<UserScriptList> scripts_;
- + content::BrowserContext* browser_context_;
- + std::unique_ptr<user_scripts::UserScriptsPrefs> prefs_;
- + std::unique_ptr<user_scripts::UserScriptLoader> userscript_loader_;
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
- diff --git a/components/user_scripts/common/BUILD.gn b/components/user_scripts/common/BUILD.gn
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/BUILD.gn
- @@ -0,0 +1,49 @@
- +# Copyright 2014 The Chromium Authors. All rights reserved.
- +# Use of this source code is governed by a BSD-style license that can be
- +# found in the LICENSE file.
- +
- +import("//build/config/features.gni")
- +import("//mojo/public/tools/bindings/mojom.gni")
- +
- +static_library("common") {
- + sources = [
- + "user_scripts_features.cc",
- + "user_scripts_features.h",
- + "constants.h",
- + "host_id.cc",
- + "host_id.h",
- + "script_constants.h",
- + "url_pattern_set.cc",
- + "url_pattern_set.h",
- + "url_pattern.cc",
- + "url_pattern.h",
- + "user_script.cc",
- + "user_script.h",
- + "view_type.cc",
- + "view_type.h",
- + "extension_messages.cc",
- + "extension_messages.h",
- + "extension_message_generator.cc",
- + "extension_message_generator.h",
- + ]
- +
- + configs += [
- + "//build/config:precompiled_headers",
- + "//build/config/compiler:wexit_time_destructors",
- + ]
- +
- + public_deps = [
- + "//components/services/app_service/public/cpp:app_file_handling",
- + "//content/public/common",
- + "//ipc",
- + "//skia",
- + ]
- +
- + deps = [
- + "//base",
- + "//components/url_formatter",
- + "//components/url_matcher",
- + "//components/version_info",
- + "//crypto",
- + ]
- +}
- diff --git a/components/user_scripts/common/constants.h b/components/user_scripts/common/constants.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/constants.h
- @@ -0,0 +1,21 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_CONSTANTS_H_
- +#define USERSCRIPTS_COMMON_CONSTANTS_H_
- +
- +#include "base/files/file_path.h"
- +#include "base/strings/string_piece_forward.h"
- +#include "components/services/app_service/public/mojom/types.mojom.h"
- +#include "components/version_info/channel.h"
- +#include "ui/base/layout.h"
- +
- +namespace user_scripts {
- +
- +// The origin of injected CSS.
- +enum CSSOrigin { /*CSS_ORIGIN_AUTHOR,*/ CSS_ORIGIN_USER };
- +
- +} // namespace user_scripts
- +
- +#endif // USERSCRIPTS_COMMON_CONSTANTS_H_
- diff --git a/components/user_scripts/common/error_utils.cc b/components/user_scripts/common/error_utils.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/error_utils.cc
- @@ -0,0 +1,54 @@
- +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "error_utils.h"
- +
- +#include <initializer_list>
- +
- +#include "base/check_op.h"
- +#include "base/strings/string_tokenizer.h"
- +#include "base/strings/string_util.h"
- +#include "base/strings/utf_string_conversions.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +std::string FormatErrorMessageInternal(
- + base::StringPiece format,
- + std::initializer_list<base::StringPiece> args) {
- + std::string format_str = format.as_string();
- + base::StringTokenizer tokenizer(format_str, "*");
- + tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS);
- +
- + std::vector<base::StringPiece> result_pieces;
- + auto* args_it = args.begin();
- + while (tokenizer.GetNext()) {
- + if (!tokenizer.token_is_delim()) {
- + result_pieces.push_back(tokenizer.token_piece());
- + continue;
- + }
- +
- + CHECK_NE(args_it, args.end())
- + << "More placeholders (*) than substitutions.";
- +
- + // Substitute the argument.
- + result_pieces.push_back(*args_it);
- + args_it++;
- + }
- +
- + // Not all substitutions were consumed.
- + CHECK_EQ(args_it, args.end()) << "Fewer placeholders (*) than substitutions.";
- +
- + return base::JoinString(result_pieces, "" /* separator */);
- +}
- +
- +} // namespace
- +
- +std::string ErrorUtils::FormatErrorMessage(base::StringPiece format,
- + base::StringPiece s1) {
- + return FormatErrorMessageInternal(format, {s1});
- +}
- +
- +} // namespace user_scripts
- diff --git a/components/user_scripts/common/error_utils.h b/components/user_scripts/common/error_utils.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/error_utils.h
- @@ -0,0 +1,24 @@
- +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_ERROR_UTILS_H_
- +#define USERSCRIPTS_COMMON_ERROR_UTILS_H_
- +
- +#include <string>
- +
- +#include "base/strings/string_piece.h"
- +
- +namespace user_scripts {
- +
- +class ErrorUtils {
- + public:
- + // Creates an error messages from a pattern.
- + static std::string FormatErrorMessage(base::StringPiece format,
- + base::StringPiece s1);
- +
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_COMMON_ERROR_UTILS_H_
- diff --git a/components/user_scripts/common/extension_message_generator.cc b/components/user_scripts/common/extension_message_generator.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/extension_message_generator.cc
- @@ -0,0 +1,29 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +// Get basic type definitions.
- +#define IPC_MESSAGE_IMPL
- +#include "components/user_scripts/common/extension_message_generator.h"
- +
- +// Generate constructors.
- +#include "ipc/struct_constructor_macros.h"
- +#include "components/user_scripts/common/extension_message_generator.h"
- +
- +// Generate param traits write methods.
- +#include "ipc/param_traits_write_macros.h"
- +namespace IPC {
- +#include "components/user_scripts/common/extension_message_generator.h"
- +} // namespace IPC
- +
- +// Generate param traits read methods.
- +#include "ipc/param_traits_read_macros.h"
- +namespace IPC {
- +#include "components/user_scripts/common/extension_message_generator.h"
- +} // namespace IPC
- +
- +// Generate param traits log methods.
- +#include "ipc/param_traits_log_macros.h"
- +namespace IPC {
- +#include "components/user_scripts/common/extension_message_generator.h"
- +} // namespace IPC
- diff --git a/components/user_scripts/common/extension_message_generator.h b/components/user_scripts/common/extension_message_generator.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/extension_message_generator.h
- @@ -0,0 +1,11 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +// Multiply-included file, hence no include guard.
- +
- +#undef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +#include "extension_messages.h"
- +#ifndef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +#error "Failed to include header extension_messages.h"
- +#endif
- diff --git a/components/user_scripts/common/extension_messages.cc b/components/user_scripts/common/extension_messages.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/extension_messages.cc
- @@ -0,0 +1,40 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "extension_messages.h"
- +
- +#include <stddef.h>
- +
- +#include <memory>
- +#include <utility>
- +
- +#include "content/public/common/common_param_traits.h"
- +
- +namespace IPC {
- +
- +void ParamTraits<HostID>::Write(base::Pickle* m, const param_type& p) {
- + WriteParam(m, p.type());
- + WriteParam(m, p.id());
- +}
- +
- +bool ParamTraits<HostID>::Read(const base::Pickle* m,
- + base::PickleIterator* iter,
- + param_type* r) {
- + HostID::HostType type;
- + std::string id;
- + if (!ReadParam(m, iter, &type))
- + return false;
- + if (!ReadParam(m, iter, &id))
- + return false;
- + *r = HostID(type, id);
- + return true;
- +}
- +
- +void ParamTraits<HostID>::Log(
- + const param_type& p, std::string* l) {
- + LogParam(p.type(), l);
- + LogParam(p.id(), l);
- +}
- +
- +} // namespace IPC
- diff --git a/components/user_scripts/common/extension_messages.h b/components/user_scripts/common/extension_messages.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/extension_messages.h
- @@ -0,0 +1,70 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +// IPC messages for extensions.
- +
- +#ifndef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +#define USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +
- +#include <stdint.h>
- +
- +#include <map>
- +#include <memory>
- +#include <set>
- +#include <string>
- +#include <vector>
- +
- +#include "base/memory/read_only_shared_memory_region.h"
- +#include "base/values.h"
- +#include "content/public/common/common_param_traits.h"
- +#include "constants.h"
- +#include "host_id.h"
- +#include "ipc/ipc_message_start.h"
- +#include "ipc/ipc_message_macros.h"
- +#include "url/gurl.h"
- +#include "url/origin.h"
- +
- +#define IPC_MESSAGE_START ExtensionMsgStart
- +
- +IPC_ENUM_TRAITS_MAX_VALUE(HostID::HostType, HostID::HOST_TYPE_LAST)
- +
- +// Singly-included section for custom IPC traits.
- +#ifndef INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +#define INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +
- +namespace IPC {
- +
- +template <>
- +struct ParamTraits<HostID> {
- + typedef HostID param_type;
- + static void Write(base::Pickle* m, const param_type& p);
- + static bool Read(const base::Pickle* m,
- + base::PickleIterator* iter,
- + param_type* r);
- + static void Log(const param_type& p, std::string* l);
- +};
- +
- +
- +} // namespace IPC
- +
- +#endif // INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- +
- +// Notification that the user scripts have been updated. It has one
- +// ReadOnlySharedMemoryRegion argument consisting of the pickled script data.
- +// This memory region is valid in the context of the renderer.
- +// If |owner| is not empty, then the shared memory handle refers to |owner|'s
- +// programmatically-defined scripts. Otherwise, the handle refers to all
- +// hosts' statically defined scripts. So far, only extension-hosts support
- +// statically defined scripts; WebUI-hosts don't.
- +// If |changed_hosts| is not empty, only the host in that set will
- +// be updated. Otherwise, all hosts that have scripts in the shared memory
- +// region will be updated. Note that the empty set => all hosts case is not
- +// supported for per-extension programmatically-defined script regions; in such
- +// regions, the owner is expected to list itself as the only changed host.
- +// If |whitelisted_only| is true, this process should only run whitelisted
- +// scripts and not all user scripts.
- +IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateUserScripts,
- + base::ReadOnlySharedMemoryRegion)
- +
- +#endif // USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
- diff --git a/components/user_scripts/common/host_id.cc b/components/user_scripts/common/host_id.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/host_id.cc
- @@ -0,0 +1,31 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "host_id.h"
- +
- +#include <tuple>
- +
- +HostID::HostID()
- + : type_(HostType::EXTENSIONS) {
- +}
- +
- +HostID::HostID(HostType type, const std::string& id)
- + : type_(type), id_(id) {
- +}
- +
- +HostID::HostID(const HostID& host_id)
- + : type_(host_id.type()),
- + id_(host_id.id()) {
- +}
- +
- +HostID::~HostID() {
- +}
- +
- +bool HostID::operator<(const HostID& host_id) const {
- + return std::tie(type_, id_) < std::tie(host_id.type_, host_id.id_);
- +}
- +
- +bool HostID::operator==(const HostID& host_id) const {
- + return type_ == host_id.type_ && id_ == host_id.id_;
- +}
- diff --git a/components/user_scripts/common/host_id.h b/components/user_scripts/common/host_id.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/host_id.h
- @@ -0,0 +1,35 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_HOST_ID_H_
- +#define USERSCRIPTS_COMMON_HOST_ID_H_
- +
- +#include <string>
- +
- +// IDs of hosts who own user scripts.
- +// A HostID is immutable after creation.
- +struct HostID {
- + enum HostType { EXTENSIONS, WEBUI, HOST_TYPE_LAST = WEBUI };
- +
- + HostID();
- + HostID(HostType type, const std::string& id);
- + HostID(const HostID& host_id);
- + ~HostID();
- +
- + bool operator<(const HostID& host_id) const;
- + bool operator==(const HostID& host_id) const;
- +
- + HostType type() const { return type_; }
- + const std::string& id() const { return id_; }
- +
- + private:
- + // The type of the host.
- + HostType type_;
- +
- + // Similar to extension_id, host_id is a unique indentifier for a host,
- + // e.g., an Extension or WebUI.
- + std::string id_;
- +};
- +
- +#endif // USERSCRIPTS_COMMON_HOST_ID_H_
- diff --git a/components/user_scripts/common/script_constants.h b/components/user_scripts/common/script_constants.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/script_constants.h
- @@ -0,0 +1,33 @@
- +// Copyright 2020 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
- +#define USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
- +
- +namespace user_scripts {
- +
- +// Whether to fall back to matching the origin for frames where the URL
- +// cannot be matched directly, such as those with about: or data: schemes.
- +enum class MatchOriginAsFallbackBehavior {
- + // Never fall back on the origin; this means scripts will never match on
- + // these frames.
- + kNever,
- + // Match the origin only for about:-scheme frames, and then climb the frame
- + // tree to find an appropriate ancestor to get a full URL (including path).
- + // This is for supporting the "match_about_blank" key.
- + // TODO(devlin): I wonder if we could simplify this to be "MatchForAbout",
- + // and not worry about climbing the frame tree. It would be a behavior
- + // change, but I wonder how many extensions it would impact in practice.
- + kMatchForAboutSchemeAndClimbTree,
- + // Match the origin as a fallback whenever applicable. This won't have a
- + // corresponding path.
- + kAlways,
- +};
- +
- +// TODO(devlin): Move the other non-UserScript-specific constants like
- +// RunLocation and InjectionType from UserScript into here.
- +
- +}
- +
- +#endif // USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
- diff --git a/components/user_scripts/common/url_pattern.cc b/components/user_scripts/common/url_pattern.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/url_pattern.cc
- @@ -0,0 +1,803 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "url_pattern.h"
- +
- +#include <stddef.h>
- +
- +#include <ostream>
- +
- +#include "base/logging.h"
- +#include "base/strings/pattern.h"
- +#include "base/strings/strcat.h"
- +#include "base/strings/string_number_conversions.h"
- +#include "base/strings/string_piece.h"
- +#include "base/strings/string_split.h"
- +#include "base/strings/string_util.h"
- +#include "base/strings/stringprintf.h"
- +#include "content/public/common/url_constants.h"
- +#include "constants.h"
- +#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
- +#include "net/base/url_util.h"
- +#include "url/gurl.h"
- +#include "url/url_util.h"
- +
- +const char URLPattern::kAllUrlsPattern[] = "<all_urls>";
- +
- +namespace {
- +
- +// TODO(aa): What about more obscure schemes like javascript: ?
- +// Note: keep this array in sync with kValidSchemeMasks.
- +const char* const kValidSchemes[] = {
- + url::kHttpScheme, url::kHttpsScheme,
- + url::kFileScheme, url::kFtpScheme,
- + /*content::kChromeUIScheme,*/ /*extensions::kExtensionScheme,*/
- + url::kFileSystemScheme, url::kWsScheme,
- + url::kWssScheme, url::kDataScheme,
- + url::kUrnScheme,
- +};
- +
- +const int kValidSchemeMasks[] = {
- + URLPattern::SCHEME_HTTP, URLPattern::SCHEME_HTTPS,
- + URLPattern::SCHEME_FILE, URLPattern::SCHEME_FTP,
- + /*URLPattern::SCHEME_CHROMEUI,*/ /*URLPattern::SCHEME_EXTENSION,*/
- + URLPattern::SCHEME_FILESYSTEM, URLPattern::SCHEME_WS,
- + URLPattern::SCHEME_WSS, URLPattern::SCHEME_DATA,
- + URLPattern::SCHEME_URN,
- +};
- +
- +static_assert(base::size(kValidSchemes) == base::size(kValidSchemeMasks),
- + "must keep these arrays in sync");
- +
- +const char kParseSuccess[] = "Success.";
- +const char kParseErrorMissingSchemeSeparator[] = "Missing scheme separator.";
- +const char kParseErrorInvalidScheme[] = "Invalid scheme.";
- +const char kParseErrorWrongSchemeType[] = "Wrong scheme type.";
- +const char kParseErrorEmptyHost[] = "Host can not be empty.";
- +const char kParseErrorInvalidHostWildcard[] = "Invalid host wildcard.";
- +const char kParseErrorEmptyPath[] = "Empty path.";
- +const char kParseErrorInvalidPort[] = "Invalid port.";
- +const char kParseErrorInvalidHost[] = "Invalid host.";
- +
- +// Message explaining each URLPattern::ParseResult.
- +const char* const kParseResultMessages[] = {
- + kParseSuccess,
- + kParseErrorMissingSchemeSeparator,
- + kParseErrorInvalidScheme,
- + kParseErrorWrongSchemeType,
- + kParseErrorEmptyHost,
- + kParseErrorInvalidHostWildcard,
- + kParseErrorEmptyPath,
- + kParseErrorInvalidPort,
- + kParseErrorInvalidHost,
- +};
- +
- +static_assert(static_cast<int>(URLPattern::ParseResult::kNumParseResults) ==
- + base::size(kParseResultMessages),
- + "must add message for each parse result");
- +
- +const char kPathSeparator[] = "/";
- +
- +bool IsStandardScheme(base::StringPiece scheme) {
- + // "*" gets the same treatment as a standard scheme.
- + if (scheme == "*")
- + return true;
- +
- + return url::IsStandard(scheme.data(),
- + url::Component(0, static_cast<int>(scheme.length())));
- +}
- +
- +bool IsValidPortForScheme(base::StringPiece scheme, base::StringPiece port) {
- + if (port == "*")
- + return true;
- +
- + // Only accept non-wildcard ports if the scheme uses ports.
- + if (url::DefaultPortForScheme(scheme.data(), scheme.length()) ==
- + url::PORT_UNSPECIFIED) {
- + return false;
- + }
- +
- + int parsed_port = url::PORT_UNSPECIFIED;
- + if (!base::StringToInt(port, &parsed_port))
- + return false;
- + return (parsed_port >= 0) && (parsed_port < 65536);
- +}
- +
- +// Returns |path| with the trailing wildcard stripped if one existed.
- +//
- +// The functions that rely on this (OverlapsWith and Contains) are only
- +// called for the patterns inside URLPatternSet. In those cases, we know that
- +// the path will have only a single wildcard at the end. This makes figuring
- +// out overlap much easier. It seems like there is probably a computer-sciency
- +// way to solve the general case, but we don't need that yet.
- +base::StringPiece StripTrailingWildcard(base::StringPiece path) {
- + if (base::EndsWith(path, "*"))
- + path.remove_suffix(1);
- + return path;
- +}
- +
- +// Removes trailing dot from |host_piece| if any.
- +base::StringPiece CanonicalizeHostForMatching(base::StringPiece host_piece) {
- + if (base::EndsWith(host_piece, "."))
- + host_piece.remove_suffix(1);
- + return host_piece;
- +}
- +
- +} // namespace
- +
- +// static
- +bool URLPattern::IsValidSchemeForExtensions(base::StringPiece scheme) {
- + for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
- + if (scheme == kValidSchemes[i])
- + return true;
- + }
- + return false;
- +}
- +
- +// static
- +int URLPattern::GetValidSchemeMaskForExtensions() {
- + int result = 0;
- + for (size_t i = 0; i < base::size(kValidSchemeMasks); ++i)
- + result |= kValidSchemeMasks[i];
- + return result;
- +}
- +
- +URLPattern::URLPattern()
- + : valid_schemes_(SCHEME_NONE),
- + match_all_urls_(false),
- + match_subdomains_(false),
- + port_("*") {}
- +
- +URLPattern::URLPattern(int valid_schemes)
- + : valid_schemes_(valid_schemes),
- + match_all_urls_(false),
- + match_subdomains_(false),
- + port_("*") {}
- +
- +URLPattern::URLPattern(int valid_schemes, base::StringPiece pattern)
- + // Strict error checking is used, because this constructor is only
- + // appropriate when we know |pattern| is valid.
- + : valid_schemes_(valid_schemes),
- + match_all_urls_(false),
- + match_subdomains_(false),
- + port_("*") {
- + ParseResult result = Parse(pattern);
- + DCHECK_EQ(ParseResult::kSuccess, result)
- + << "Parsing unexpectedly failed for pattern: " << pattern << ": "
- + << GetParseResultString(result);
- +}
- +
- +URLPattern::URLPattern(const URLPattern& other) = default;
- +
- +URLPattern::URLPattern(URLPattern&& other) = default;
- +
- +URLPattern::~URLPattern() {
- +}
- +
- +URLPattern& URLPattern::operator=(const URLPattern& other) = default;
- +
- +URLPattern& URLPattern::operator=(URLPattern&& other) = default;
- +
- +bool URLPattern::operator<(const URLPattern& other) const {
- + return GetAsString() < other.GetAsString();
- +}
- +
- +bool URLPattern::operator>(const URLPattern& other) const {
- + return GetAsString() > other.GetAsString();
- +}
- +
- +bool URLPattern::operator==(const URLPattern& other) const {
- + return GetAsString() == other.GetAsString();
- +}
- +
- +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern) {
- + return out << '"' << url_pattern.GetAsString() << '"';
- +}
- +
- +URLPattern::ParseResult URLPattern::Parse(base::StringPiece pattern) {
- + spec_.clear();
- + SetMatchAllURLs(false);
- + SetMatchSubdomains(false);
- + SetPort("*");
- +
- + // Special case pattern to match every valid URL.
- + if (pattern == kAllUrlsPattern) {
- + SetMatchAllURLs(true);
- + return ParseResult::kSuccess;
- + }
- +
- + // Parse out the scheme.
- + size_t scheme_end_pos = pattern.find(url::kStandardSchemeSeparator);
- + bool has_standard_scheme_separator = true;
- +
- + // Some urls also use ':' alone as the scheme separator.
- + if (scheme_end_pos == base::StringPiece::npos) {
- + scheme_end_pos = pattern.find(':');
- + has_standard_scheme_separator = false;
- + }
- +
- + if (scheme_end_pos == base::StringPiece::npos)
- + return ParseResult::kMissingSchemeSeparator;
- +
- + if (!SetScheme(pattern.substr(0, scheme_end_pos)))
- + return ParseResult::kInvalidScheme;
- +
- + bool standard_scheme = IsStandardScheme(scheme_);
- + if (standard_scheme != has_standard_scheme_separator)
- + return ParseResult::kWrongSchemeSeparator;
- +
- + // Advance past the scheme separator.
- + scheme_end_pos +=
- + (standard_scheme ? strlen(url::kStandardSchemeSeparator) : 1);
- + if (scheme_end_pos >= pattern.size())
- + return ParseResult::kEmptyHost;
- +
- + // Parse out the host and path.
- + size_t host_start_pos = scheme_end_pos;
- + size_t path_start_pos = 0;
- +
- + if (!standard_scheme) {
- + path_start_pos = host_start_pos;
- + } else if (scheme_ == url::kFileScheme) {
- + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
- + if (host_end_pos == base::StringPiece::npos) {
- + // Allow hostname omission.
- + // e.g. file://* is interpreted as file:///*,
- + // file://foo* is interpreted as file:///foo*.
- + path_start_pos = host_start_pos - 1;
- + } else {
- + // Ignore hostname if scheme is file://.
- + // e.g. file://localhost/foo is equal to file:///foo.
- + path_start_pos = host_end_pos;
- + }
- + } else {
- + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
- +
- + // Host is required.
- + if (host_start_pos == host_end_pos)
- + return ParseResult::kEmptyHost;
- +
- + if (host_end_pos == base::StringPiece::npos)
- + return ParseResult::kEmptyPath;
- +
- + base::StringPiece host_and_port =
- + pattern.substr(host_start_pos, host_end_pos - host_start_pos);
- +
- + size_t port_separator_pos = base::StringPiece::npos;
- + if (host_and_port[0] != '[') {
- + // Not IPv6 (either IPv4 or just a normal address).
- + port_separator_pos = host_and_port.find(':');
- + } else { // IPv6.
- + size_t host_end_pos = host_and_port.find(']');
- + if (host_end_pos == base::StringPiece::npos)
- + return ParseResult::kInvalidHost;
- + if (host_end_pos == 1)
- + return ParseResult::kEmptyHost;
- +
- + if (host_end_pos < host_and_port.length() - 1) {
- + // The host isn't the only component. Check for a port. This would
- + // require a ':' to follow the closing ']' from the host.
- + if (host_and_port[host_end_pos + 1] != ':')
- + return ParseResult::kInvalidHost;
- +
- + port_separator_pos = host_end_pos + 1;
- + }
- + }
- +
- + if (port_separator_pos != base::StringPiece::npos &&
- + !SetPort(host_and_port.substr(port_separator_pos + 1))) {
- + return ParseResult::kInvalidPort;
- + }
- +
- + // Note: this substr() will be the entire string if the port position
- + // wasn't found.
- + base::StringPiece host_piece = host_and_port.substr(0, port_separator_pos);
- +
- + if (host_piece.empty())
- + return ParseResult::kEmptyHost;
- +
- + if (host_piece == "*") {
- + match_subdomains_ = true;
- + host_piece = base::StringPiece();
- + } else if (base::StartsWith(host_piece, "*.")) {
- + if (host_piece.length() == 2) {
- + // We don't allow just '*.' as a host.
- + return ParseResult::kEmptyHost;
- + }
- + match_subdomains_ = true;
- + host_piece = host_piece.substr(2);
- + }
- +
- + host_ = std::string(host_piece);
- +
- + path_start_pos = host_end_pos;
- + }
- +
- + SetPath(pattern.substr(path_start_pos));
- +
- + // No other '*' can occur in the host, though. This isn't necessary, but is
- + // done as a convenience to developers who might otherwise be confused and
- + // think '*' works as a glob in the host.
- + if (host_.find('*') != std::string::npos)
- + return ParseResult::kInvalidHostWildcard;
- +
- + if (!host_.empty()) {
- + // If |host_| is present (i.e., isn't a wildcard), we need to canonicalize
- + // it.
- + url::CanonHostInfo host_info;
- + host_ = net::CanonicalizeHost(host_, &host_info);
- + // net::CanonicalizeHost() returns an empty string on failure.
- + if (host_.empty())
- + return ParseResult::kInvalidHost;
- + }
- +
- + // Null characters are not allowed in hosts.
- + if (host_.find('\0') != std::string::npos)
- + return ParseResult::kInvalidHost;
- +
- + return ParseResult::kSuccess;
- +}
- +
- +void URLPattern::SetValidSchemes(int valid_schemes) {
- + // TODO(devlin): Should we check that valid_schemes agrees with |scheme_|
- + // here? Otherwise, valid_schemes_ and schemes_ may stop agreeing with each
- + // other (e.g., in the case of `*://*/*`, where the scheme should only be
- + // http or https).
- + spec_.clear();
- + valid_schemes_ = valid_schemes;
- +}
- +
- +void URLPattern::SetHost(base::StringPiece host) {
- + spec_.clear();
- + host_.assign(host.data(), host.size());
- +}
- +
- +void URLPattern::SetMatchAllURLs(bool val) {
- + spec_.clear();
- + match_all_urls_ = val;
- +
- + if (val) {
- + match_subdomains_ = true;
- + scheme_ = "*";
- + host_.clear();
- + SetPath("/*");
- + }
- +}
- +
- +void URLPattern::SetMatchSubdomains(bool val) {
- + spec_.clear();
- + match_subdomains_ = val;
- +}
- +
- +bool URLPattern::SetScheme(base::StringPiece scheme) {
- + spec_.clear();
- + scheme_.assign(scheme.data(), scheme.size());
- + if (scheme_ == "*") {
- + valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS);
- + } else if (!IsValidScheme(scheme_)) {
- + return false;
- + }
- + return true;
- +}
- +
- +bool URLPattern::IsValidScheme(base::StringPiece scheme) const {
- + if (valid_schemes_ == SCHEME_ALL)
- + return true;
- +
- + for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
- + if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i]))
- + return true;
- + }
- +
- + return false;
- +}
- +
- +void URLPattern::SetPath(base::StringPiece path) {
- + spec_.clear();
- + path_.assign(path.data(), path.size());
- + path_escaped_ = path_;
- + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\");
- + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?");
- +}
- +
- +bool URLPattern::SetPort(base::StringPiece port) {
- + spec_.clear();
- + if (IsValidPortForScheme(scheme_, port)) {
- + port_.assign(port.data(), port.size());
- + return true;
- + }
- + return false;
- +}
- +
- +bool URLPattern::MatchesURL(const GURL& test) const {
- + // Invalid URLs can never match.
- + if (!test.is_valid())
- + return false;
- +
- + const GURL* test_url = &test;
- + bool has_inner_url = test.inner_url() != nullptr;
- +
- + if (has_inner_url) {
- + if (!test.SchemeIsFileSystem())
- + return false; // The only nested URLs we handle are filesystem URLs.
- + test_url = test.inner_url();
- + }
- +
- + // Ensure the scheme matches first, since <all_urls> may not match this URL if
- + // the scheme is excluded.
- + if (!MatchesScheme(test_url->scheme_piece()))
- + return false;
- +
- + if (match_all_urls_)
- + return true;
- +
- + // Unless |match_all_urls_| is true, the grammar only permits matching
- + // URLs with nonempty paths.
- + if (!test.has_path())
- + return false;
- +
- + std::string path_for_request = test.PathForRequest();
- + if (has_inner_url) {
- + path_for_request = base::StringPrintf("%s%s", test_url->path_piece().data(),
- + path_for_request.c_str());
- + }
- +
- + return MatchesSecurityOriginHelper(*test_url) &&
- + MatchesPath(path_for_request);
- +}
- +
- +bool URLPattern::MatchesSecurityOrigin(const GURL& test) const {
- + const GURL* test_url = &test;
- + bool has_inner_url = test.inner_url() != NULL;
- +
- + if (has_inner_url) {
- + if (!test.SchemeIsFileSystem())
- + return false; // The only nested URLs we handle are filesystem URLs.
- + test_url = test.inner_url();
- + }
- +
- + if (!MatchesScheme(test_url->scheme()))
- + return false;
- +
- + if (match_all_urls_)
- + return true;
- +
- + return MatchesSecurityOriginHelper(*test_url);
- +}
- +
- +bool URLPattern::MatchesScheme(base::StringPiece test) const {
- + if (!IsValidScheme(test))
- + return false;
- +
- + return scheme_ == "*" || test == scheme_;
- +}
- +
- +bool URLPattern::MatchesHost(base::StringPiece host) const {
- + // TODO(devlin): This is a bit sad. Parsing urls is expensive. However, it's
- + // important that we do this conversion to a GURL in order to canonicalize the
- + // host (the pattern's host_ already is canonicalized from Parse()). We can't
- + // just do string comparison.
- + return MatchesHost(
- + GURL(base::StringPrintf("%s%s%s/", url::kHttpScheme,
- + url::kStandardSchemeSeparator, host.data())));
- +}
- +
- +bool URLPattern::MatchesHost(const GURL& test) const {
- + base::StringPiece test_host(CanonicalizeHostForMatching(test.host_piece()));
- + const base::StringPiece pattern_host(CanonicalizeHostForMatching(host_));
- +
- + // If the hosts are exactly equal, we have a match.
- + if (test_host == pattern_host)
- + return true;
- +
- + // If we're matching subdomains, and we have no host in the match pattern,
- + // that means that we're matching all hosts, which means we have a match no
- + // matter what the test host is.
- + if (match_subdomains_ && pattern_host.empty())
- + return true;
- +
- + // Otherwise, we can only match if our match pattern matches subdomains.
- + if (!match_subdomains_)
- + return false;
- +
- + // We don't do subdomain matching against IP addresses, so we can give up now
- + // if the test host is an IP address.
- + if (test.HostIsIPAddress())
- + return false;
- +
- + // Check if the test host is a subdomain of our host.
- + if (test_host.length() <= (pattern_host.length() + 1))
- + return false;
- +
- + if (!base::EndsWith(test_host, pattern_host))
- + return false;
- +
- + return test_host[test_host.length() - pattern_host.length() - 1] == '.';
- +}
- +
- +bool URLPattern::MatchesEffectiveTld(
- + net::registry_controlled_domains::PrivateRegistryFilter private_filter,
- + net::registry_controlled_domains::UnknownRegistryFilter unknown_filter)
- + const {
- + // Check if it matches all urls or is a pattern like http://*/*.
- + if (match_all_urls_ || (match_subdomains_ && host_.empty()))
- + return true;
- +
- + // If this doesn't even match subdomains, it can't possibly be a TLD wildcard.
- + if (!match_subdomains_)
- + return false;
- +
- + // If there was more than just a TLD in the host (e.g., *.foobar.com), it
- + // doesn't match all hosts in an effective TLD.
- + if (net::registry_controlled_domains::HostHasRegistryControlledDomain(
- + host_, unknown_filter, private_filter)) {
- + return false;
- + }
- +
- + // At this point the host could either be just a TLD ("com") or some unknown
- + // TLD-like string ("notatld"). To disambiguate between them construct a
- + // fake URL, and check the registry.
- + //
- + // If we recognized this TLD, then this is a pattern like *.com, and it
- + // matches an effective TLD.
- + return net::registry_controlled_domains::HostHasRegistryControlledDomain(
- + "notatld." + host_, unknown_filter, private_filter);
- +}
- +
- +bool URLPattern::MatchesSingleOrigin() const {
- + // Strictly speaking, the port is part of the origin, but in URLPattern it
- + // defaults to *. It's not very interesting anyway, so leave it out.
- + return !MatchesEffectiveTld() && scheme_ != "*" && !match_subdomains_;
- +}
- +
- +bool URLPattern::MatchesPath(base::StringPiece test) const {
- + // Make the behaviour of OverlapsWith consistent with MatchesURL, which is
- + // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'.
- + // The below if is a no-copy way of doing (test + "/*" == path_escaped_).
- + if (path_escaped_.length() == test.length() + 2 &&
- + base::StartsWith(path_escaped_.c_str(), test) &&
- + base::EndsWith(path_escaped_, "/*")) {
- + return true;
- + }
- +
- + return base::MatchPattern(test, path_escaped_);
- +}
- +
- +const std::string& URLPattern::GetAsString() const {
- + if (!spec_.empty())
- + return spec_;
- +
- + if (match_all_urls_) {
- + spec_ = kAllUrlsPattern;
- + return spec_;
- + }
- +
- + bool standard_scheme = IsStandardScheme(scheme_);
- +
- + std::string spec = scheme_ +
- + (standard_scheme ? url::kStandardSchemeSeparator : ":");
- +
- + if (scheme_ != url::kFileScheme && standard_scheme) {
- + if (match_subdomains_) {
- + spec += "*";
- + if (!host_.empty())
- + spec += ".";
- + }
- +
- + if (!host_.empty())
- + spec += host_;
- +
- + if (port_ != "*") {
- + spec += ":";
- + spec += port_;
- + }
- + }
- +
- + if (!path_.empty())
- + spec += path_;
- +
- + spec_ = std::move(spec);
- + return spec_;
- +}
- +
- +bool URLPattern::OverlapsWith(const URLPattern& other) const {
- + if (match_all_urls() || other.match_all_urls())
- + return true;
- + return (MatchesAnyScheme(other.GetExplicitSchemes()) ||
- + other.MatchesAnyScheme(GetExplicitSchemes()))
- + && (MatchesHost(other.host()) || other.MatchesHost(host()))
- + && (MatchesPortPattern(other.port()) || other.MatchesPortPattern(port()))
- + && (MatchesPath(StripTrailingWildcard(other.path())) ||
- + other.MatchesPath(StripTrailingWildcard(path())));
- +}
- +
- +bool URLPattern::Contains(const URLPattern& other) const {
- + // Important: it's not enough to just check match_all_urls(); we also need to
- + // make sure that the schemes in this pattern are a superset of those in
- + // |other|.
- + if (match_all_urls() &&
- + (valid_schemes_ & other.valid_schemes_) == other.valid_schemes_) {
- + return true;
- + }
- +
- + return MatchesAllSchemes(other.GetExplicitSchemes()) &&
- + MatchesHost(other.host()) &&
- + (!other.match_subdomains_ || match_subdomains_) &&
- + MatchesPortPattern(other.port()) &&
- + MatchesPath(StripTrailingWildcard(other.path()));
- +}
- +
- +absl::optional<URLPattern> URLPattern::CreateIntersection(
- + const URLPattern& other) const {
- + // Easy case: Schemes don't overlap. Return nullopt.
- + int intersection_schemes = URLPattern::SCHEME_NONE;
- + if (valid_schemes_ == URLPattern::SCHEME_ALL)
- + intersection_schemes = other.valid_schemes_;
- + else if (other.valid_schemes_ == URLPattern::SCHEME_ALL)
- + intersection_schemes = valid_schemes_;
- + else
- + intersection_schemes = valid_schemes_ & other.valid_schemes_;
- +
- + if (intersection_schemes == URLPattern::SCHEME_NONE)
- + return absl::nullopt;
- +
- + {
- + // In a few cases, we can (mostly) return a copy of one of the patterns.
- + // This can happen when either:
- + // - The URLPattern's are identical (possibly excluding valid_schemes_)
- + // - One of the patterns has match_all_urls() equal to true.
- + // NOTE(devlin): Theoretically, we could use Contains() instead of
- + // match_all_urls() here. However, Contains() strips the trailing wildcard
- + // from the path, which could yield the incorrect result.
- + const URLPattern* copy_source = nullptr;
- + if (*this == other || other.match_all_urls())
- + copy_source = this;
- + else if (match_all_urls())
- + copy_source = &other;
- +
- + if (copy_source) {
- + // NOTE: equality checks don't take into account valid_schemes_, and
- + // schemes can be different in the case of match_all_urls() as well, so
- + // we can't always just return *copy_source.
- + if (intersection_schemes == copy_source->valid_schemes_)
- + return *copy_source;
- + URLPattern result(intersection_schemes);
- + ParseResult parse_result = result.Parse(copy_source->GetAsString());
- + CHECK_EQ(ParseResult::kSuccess, parse_result);
- + return result;
- + }
- + }
- +
- + // No more easy cases. Go through component by component to find the patterns
- + // that intersect.
- +
- + // Note: Alias the function type (rather than using auto) because
- + // MatchesHost() is overloaded.
- + using match_function_type = bool (URLPattern::*)(base::StringPiece) const;
- +
- + auto get_intersection = [this, &other](base::StringPiece own_str,
- + base::StringPiece other_str,
- + match_function_type match_function,
- + base::StringPiece* out) {
- + if ((this->*match_function)(other_str)) {
- + *out = other_str;
- + return true;
- + }
- + if ((other.*match_function)(own_str)) {
- + *out = own_str;
- + return true;
- + }
- + return false;
- + };
- +
- + base::StringPiece scheme;
- + base::StringPiece host;
- + base::StringPiece port;
- + base::StringPiece path;
- + // If any pieces fail to overlap, then there is no intersection.
- + if (!get_intersection(scheme_, other.scheme_, &URLPattern::MatchesScheme,
- + &scheme) ||
- + !get_intersection(host_, other.host_, &URLPattern::MatchesHost, &host) ||
- + !get_intersection(port_, other.port_, &URLPattern::MatchesPortPattern,
- + &port) ||
- + !get_intersection(path_, other.path_, &URLPattern::MatchesPath, &path)) {
- + return absl::nullopt;
- + }
- +
- + // Only match subdomains if both patterns match subdomains.
- + base::StringPiece subdomains;
- + if (match_subdomains_ && other.match_subdomains_) {
- + // The host may be empty (e.g., in the case of *://*/* - in that case, only
- + // append '*' instead of '*.'.
- + subdomains = host.empty() ? "*" : "*.";
- + }
- +
- + base::StringPiece scheme_separator =
- + IsStandardScheme(scheme) ? url::kStandardSchemeSeparator : ":";
- +
- + std::string pattern_str = base::StrCat(
- + {scheme, scheme_separator, subdomains, host, ":", port, path});
- +
- + URLPattern pattern(intersection_schemes);
- + ParseResult result = pattern.Parse(pattern_str);
- + // TODO(devlin): I don't think there's any way this should ever fail, but
- + // use a CHECK() to flush any cases out. If nothing crops up, downgrade this
- + // to a DCHECK in M72.
- + CHECK_EQ(ParseResult::kSuccess, result);
- +
- + return pattern;
- +}
- +
- +bool URLPattern::MatchesAnyScheme(
- + const std::vector<std::string>& schemes) const {
- + for (auto i = schemes.cbegin(); i != schemes.cend(); ++i) {
- + if (MatchesScheme(*i))
- + return true;
- + }
- +
- + return false;
- +}
- +
- +bool URLPattern::MatchesAllSchemes(
- + const std::vector<std::string>& schemes) const {
- + for (auto i = schemes.cbegin(); i != schemes.cend(); ++i) {
- + if (!MatchesScheme(*i))
- + return false;
- + }
- +
- + return true;
- +}
- +
- +bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const {
- + // Ignore hostname if scheme is file://.
- + if (scheme_ != url::kFileScheme && !MatchesHost(test))
- + return false;
- +
- + if (!MatchesPortPattern(base::NumberToString(test.EffectiveIntPort())))
- + return false;
- +
- + return true;
- +}
- +
- +bool URLPattern::MatchesPortPattern(base::StringPiece port) const {
- + return port_ == "*" || port_ == port;
- +}
- +
- +std::vector<std::string> URLPattern::GetExplicitSchemes() const {
- + std::vector<std::string> result;
- +
- + if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) {
- + result.push_back(scheme_);
- + return result;
- + }
- +
- + for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
- + if (MatchesScheme(kValidSchemes[i])) {
- + result.push_back(kValidSchemes[i]);
- + }
- + }
- +
- + return result;
- +}
- +
- +std::vector<URLPattern> URLPattern::ConvertToExplicitSchemes() const {
- + std::vector<std::string> explicit_schemes = GetExplicitSchemes();
- + std::vector<URLPattern> result;
- +
- + for (std::vector<std::string>::const_iterator i = explicit_schemes.begin();
- + i != explicit_schemes.end(); ++i) {
- + URLPattern temp = *this;
- + temp.SetScheme(*i);
- + temp.SetMatchAllURLs(false);
- + result.push_back(temp);
- + }
- +
- + return result;
- +}
- +
- +// static
- +const char* URLPattern::GetParseResultString(
- + URLPattern::ParseResult parse_result) {
- + return kParseResultMessages[static_cast<int>(parse_result)];
- +}
- diff --git a/components/user_scripts/common/url_pattern.h b/components/user_scripts/common/url_pattern.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/url_pattern.h
- @@ -0,0 +1,302 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +#ifndef USERSCRIPTS_COMMON_URL_PATTERN_H_
- +#define USERSCRIPTS_COMMON_URL_PATTERN_H_
- +
- +#include <functional>
- +#include <iosfwd>
- +#include <string>
- +#include <vector>
- +
- +#include "base/strings/string_piece.h"
- +#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
- +
- +class GURL;
- +
- +// A pattern that can be used to match URLs. A URLPattern is a very restricted
- +// subset of URL syntax:
- +//
- +// <url-pattern> := <scheme>://<host><port><path> | '<all_urls>'
- +// <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' |
- +// 'chrome-extension' | 'filesystem'
- +// <host> := '*' | <IPv4 address> | [<IPv6 address>] |
- +// '*.' <anychar except '/' and '*'>+
- +// <port> := [':' ('*' | <port number between 0 and 65535>)]
- +// <path> := '/' <any chars>
- +//
- +// * Host is not used when the scheme is 'file'.
- +// * The path can have embedded '*' characters which act as glob wildcards.
- +// * '<all_urls>' is a special pattern that matches any valid URL that contains
- +// a valid scheme (as specified by valid_schemes_).
- +// * The '*' scheme pattern excludes file URLs.
- +//
- +// Examples of valid patterns:
- +// - http://*/*
- +// - http://*/foo*
- +// - https://*.google.com/foo*bar
- +// - file://monkey*
- +// - http://127.0.0.1/*
- +// - http://[2607:f8b0:4005:805::200e]/*
- +//
- +// Examples of invalid patterns:
- +// - http://* -- path not specified
- +// - http://*foo/bar -- * not allowed as substring of host component
- +// - http://foo.*.bar/baz -- * must be first component
- +// - http:/bar -- scheme separator not found
- +// - foo://* -- invalid scheme
- +// - chrome:// -- we don't support chrome internal URLs
- +class URLPattern {
- + public:
- + // A collection of scheme bitmasks for use with valid_schemes.
- + enum SchemeMasks {
- + SCHEME_NONE = 0,
- + SCHEME_HTTP = 1 << 0,
- + SCHEME_HTTPS = 1 << 1,
- + SCHEME_FILE = 1 << 2,
- + SCHEME_FTP = 1 << 3,
- + SCHEME_CHROMEUI = 1 << 4,
- + SCHEME_EXTENSION = 1 << 5,
- + SCHEME_FILESYSTEM = 1 << 6,
- + SCHEME_WS = 1 << 7,
- + SCHEME_WSS = 1 << 8,
- + SCHEME_DATA = 1 << 9,
- + SCHEME_URN = 1 << 10,
- +
- + // IMPORTANT!
- + // SCHEME_ALL will match every scheme, including chrome://, chrome-
- + // extension://, about:, etc. Because this has lots of security
- + // implications, third-party extensions should usually not be able to get
- + // access to URL patterns initialized this way. If there is a reason
- + // for violating this general rule, document why this it safe.
- + SCHEME_ALL = -1,
- + };
- +
- + // Error codes returned from Parse().
- + enum class ParseResult {
- + kSuccess = 0,
- + kMissingSchemeSeparator,
- + kInvalidScheme,
- + kWrongSchemeSeparator,
- + kEmptyHost,
- + kInvalidHostWildcard,
- + kEmptyPath,
- + kInvalidPort,
- + kInvalidHost,
- + kNumParseResults,
- + };
- +
- + // The <all_urls> string pattern.
- + static const char kAllUrlsPattern[];
- +
- + // Returns true if the given |scheme| is considered valid for extensions.
- + static bool IsValidSchemeForExtensions(base::StringPiece scheme);
- +
- + // Returns the mask for all schemes considered valid for extensions.
- + static int GetValidSchemeMaskForExtensions();
- +
- + explicit URLPattern(int valid_schemes);
- +
- + // Convenience to construct a URLPattern from a string. If the string is not
- + // known ahead of time, use Parse() instead, which returns success or failure.
- + URLPattern(int valid_schemes, base::StringPiece pattern);
- +
- + URLPattern();
- + URLPattern(const URLPattern& other);
- + URLPattern(URLPattern&& other);
- + ~URLPattern();
- +
- + URLPattern& operator=(const URLPattern& other);
- + URLPattern& operator=(URLPattern&& other);
- +
- + bool operator<(const URLPattern& other) const;
- + bool operator>(const URLPattern& other) const;
- + bool operator==(const URLPattern& other) const;
- +
- + // Initializes this instance by parsing the provided string. Returns
- + // URLPattern::ParseResult::kSuccess on success, or an error code otherwise.
- + // On failure, this instance will have some intermediate values and is in an
- + // invalid state.
- + ParseResult Parse(base::StringPiece pattern_str);
- +
- + // Gets the bitmask of valid schemes.
- + int valid_schemes() const { return valid_schemes_; }
- + void SetValidSchemes(int valid_schemes);
- +
- + // Gets the host the pattern matches. This can be an empty string if the
- + // pattern matches all hosts (the input was <scheme>://*/<whatever>).
- + const std::string& host() const { return host_; }
- + void SetHost(base::StringPiece host);
- +
- + // Gets whether to match subdomains of host().
- + bool match_subdomains() const { return match_subdomains_; }
- + void SetMatchSubdomains(bool val);
- +
- + // Gets the path the pattern matches with the leading slash. This can have
- + // embedded asterisks which are interpreted using glob rules.
- + const std::string& path() const { return path_; }
- + void SetPath(base::StringPiece path);
- +
- + // Returns true if this pattern matches all (valid) urls.
- + bool match_all_urls() const { return match_all_urls_; }
- + void SetMatchAllURLs(bool val);
- +
- + // Sets the scheme for pattern matches. This can be a single '*' if the
- + // pattern matches all valid schemes (as defined by the valid_schemes_
- + // property). Returns false on failure (if the scheme is not valid).
- + bool SetScheme(base::StringPiece scheme);
- + // Note: You should use MatchesScheme() instead of this getter unless you
- + // absolutely need the exact scheme. This is exposed for testing.
- + const std::string& scheme() const { return scheme_; }
- +
- + // Returns true if the specified scheme can be used in this URL pattern, and
- + // false otherwise. Uses valid_schemes_ to determine validity.
- + bool IsValidScheme(base::StringPiece scheme) const;
- +
- + // Returns true if this instance matches the specified URL. Always returns
- + // false for invalid URLs.
- + bool MatchesURL(const GURL& test) const;
- +
- + // Returns true if this instance matches the specified security origin.
- + bool MatchesSecurityOrigin(const GURL& test) const;
- +
- + // Returns true if |test| matches our scheme.
- + // Note that if test is "filesystem", this may fail whereas MatchesURL
- + // may succeed. MatchesURL is smart enough to look at the inner_url instead
- + // of the outer "filesystem:" part.
- + bool MatchesScheme(base::StringPiece test) const;
- +
- + // Returns true if |test| matches our host.
- + bool MatchesHost(base::StringPiece test) const;
- + bool MatchesHost(const GURL& test) const;
- +
- + // Returns true if |test| matches our path.
- + bool MatchesPath(base::StringPiece test) const;
- +
- + // Returns true if the pattern matches all patterns in an (e)TLD. This
- + // includes patterns like *://*.com/*, *://*.co.uk/*, etc. A pattern that
- + // matches all domains (e.g., *://*/*) will return true.
- + // |private_filter| specifies whether private registries (like appspot.com)
- + // should be considered; if included, patterns like *://*.appspot.com/* will
- + // return true. By default, we exclude private registries (so *.appspot.com
- + // returns false).
- + // Note: This is an expensive method, and should be used sparingly!
- + // You should probably use URLPatternSet::ShouldWarnAllHosts(), which is
- + // cached.
- + bool MatchesEffectiveTld(
- + net::registry_controlled_domains::PrivateRegistryFilter private_filter =
- + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES,
- + net::registry_controlled_domains::UnknownRegistryFilter unknown_filter =
- + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES) const;
- +
- + // Returns true if the pattern only matches a single origin. The pattern may
- + // include a path.
- + bool MatchesSingleOrigin() const;
- +
- + // Sets the port. Returns false if the port is invalid.
- + bool SetPort(base::StringPiece port);
- + const std::string& port() const { return port_; }
- +
- + // Returns a string representing this instance.
- + const std::string& GetAsString() const;
- +
- + // Determines whether there is a URL that would match this instance and
- + // another instance. This method is symmetrical: Calling
- + // other.OverlapsWith(this) would result in the same answer.
- + bool OverlapsWith(const URLPattern& other) const;
- +
- + // Returns true if this pattern matches all possible URLs that |other| can
- + // match. For example, http://*.google.com encompasses http://www.google.com.
- + bool Contains(const URLPattern& other) const;
- +
- + // Creates a new URLPattern that represents the intersection of this
- + // URLPattern with the |other|, or base::nullopt if no intersection exists.
- + // For instance, given the patterns http://*.google.com/* and
- + // *://maps.google.com/*, the intersection is http://maps.google.com/*.
- + // NOTES:
- + // - Though scheme intersections are supported, the serialization of
- + // URLPatternSet does not record them. Be sure that this is safe for your
- + // use cases.
- + // - Path intersection is done on a best-effort basis. If one path clearly
- + // contains another, it will be handled correctly, but this method does not
- + // deal with cases like /*a* and /*b* (where technically the intersection
- + // is /*a*b*|/*b*a*); the intersection returned for that case will be empty.
- + absl::optional<URLPattern> CreateIntersection(const URLPattern& other) const;
- +
- + // Converts this URLPattern into an equivalent set of URLPatterns that don't
- + // use a wildcard in the scheme component. If this URLPattern doesn't use a
- + // wildcard scheme, then the returned set will contain one element that is
- + // equivalent to this instance.
- + std::vector<URLPattern> ConvertToExplicitSchemes() const;
- +
- + static bool EffectiveHostCompare(const URLPattern& a, const URLPattern& b) {
- + if (a.match_all_urls_ && b.match_all_urls_)
- + return false;
- + return a.host_.compare(b.host_) < 0;
- + }
- +
- + // Used for origin comparisons in a std::set.
- + class EffectiveHostCompareFunctor {
- + public:
- + bool operator()(const URLPattern& a, const URLPattern& b) const {
- + return EffectiveHostCompare(a, b);
- + }
- + };
- +
- + // Get an error string for a ParseResult.
- + static const char* GetParseResultString(URLPattern::ParseResult parse_result);
- +
- + private:
- + // Returns true if any of the |schemes| items matches our scheme.
- + bool MatchesAnyScheme(const std::vector<std::string>& schemes) const;
- +
- + // Returns true if all of the |schemes| items matches our scheme.
- + bool MatchesAllSchemes(const std::vector<std::string>& schemes) const;
- +
- + bool MatchesSecurityOriginHelper(const GURL& test) const;
- +
- + // Returns true if our port matches the |port| pattern (it may be "*").
- + bool MatchesPortPattern(base::StringPiece port) const;
- +
- + // If the URLPattern contains a wildcard scheme, returns a list of
- + // equivalent literal schemes, otherwise returns the current scheme.
- + std::vector<std::string> GetExplicitSchemes() const;
- +
- + // A bitmask containing the schemes which are considered valid for this
- + // pattern. Parse() uses this to decide whether a pattern contains a valid
- + // scheme.
- + int valid_schemes_;
- +
- + // True if this is a special-case "<all_urls>" pattern.
- + bool match_all_urls_;
- +
- + // The scheme for the pattern.
- + std::string scheme_;
- +
- + // The host without any leading "*" components.
- + std::string host_;
- +
- + // Whether we should match subdomains of the host. This is true if the first
- + // component of the pattern's host was "*".
- + bool match_subdomains_;
- +
- + // The port.
- + std::string port_;
- +
- + // The path to match. This is everything after the host of the URL, or
- + // everything after the scheme in the case of file:// URLs.
- + std::string path_;
- +
- + // The path with "?" and "\" characters escaped for use with the
- + // MatchPattern() function.
- + std::string path_escaped_;
- +
- + // A string representing this URLPattern.
- + mutable std::string spec_;
- +};
- +
- +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern);
- +
- +typedef std::vector<URLPattern> URLPatternList;
- +
- +#endif // USERSCRIPTS_COMMON_URL_PATTERN_H_
- diff --git a/components/user_scripts/common/url_pattern_set.cc b/components/user_scripts/common/url_pattern_set.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/url_pattern_set.cc
- @@ -0,0 +1,335 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "url_pattern_set.h"
- +
- +#include <iterator>
- +#include <ostream>
- +
- +#include "base/containers/contains.h"
- +#include "base/logging.h"
- +#include "base/stl_util.h"
- +#include "base/values.h"
- +#include "error_utils.h"
- +#include "url_pattern.h"
- +#include "url/gurl.h"
- +#include "url/origin.h"
- +#include "url/url_constants.h"
- +#include "user_scripts_features.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +const char kInvalidURLPatternError[] = "Invalid url pattern '*'";
- +
- +} // namespace
- +
- +// static
- +URLPatternSet URLPatternSet::CreateDifference(const URLPatternSet& set1,
- + const URLPatternSet& set2) {
- + return URLPatternSet(base::STLSetDifference<std::set<URLPattern>>(
- + set1.patterns_, set2.patterns_));
- +}
- +
- +// static
- +URLPatternSet URLPatternSet::CreateIntersection(
- + const URLPatternSet& set1,
- + const URLPatternSet& set2,
- + IntersectionBehavior intersection_behavior) {
- + // Note: leverage return value optimization; always return the same object.
- + URLPatternSet result;
- +
- + if (intersection_behavior == IntersectionBehavior::kStringComparison) {
- + // String comparison just relies on STL set behavior, which looks at the
- + // string representation.
- + result = URLPatternSet(base::STLSetIntersection<std::set<URLPattern>>(
- + set1.patterns_, set2.patterns_));
- + return result;
- + }
- +
- + // Look for a semantic intersection.
- +
- + // Step 1: Iterate over each set. Find any patterns that are completely
- + // contained by the other (thus being necessarily present in any intersection)
- + // and add them, collecting the others in a set of unique items.
- + // Note: Use a collection of pointers for the uniques to avoid excessive
- + // copies. Since these are owned by the URLPatternSet passed in, which is
- + // const, this should be safe.
- + std::vector<const URLPattern*> unique_set1;
- + for (const URLPattern& pattern : set1) {
- + if (set2.ContainsPattern(pattern))
- + result.patterns_.insert(pattern);
- + else
- + unique_set1.push_back(&pattern);
- + }
- + std::vector<const URLPattern*> unique_set2;
- + for (const URLPattern& pattern : set2) {
- + if (set1.ContainsPattern(pattern))
- + result.patterns_.insert(pattern);
- + else
- + unique_set2.push_back(&pattern);
- + }
- +
- + // If we're just looking for patterns contained by both, we're done.
- + if (intersection_behavior == IntersectionBehavior::kPatternsContainedByBoth)
- + return result;
- +
- + DCHECK_EQ(IntersectionBehavior::kDetailed, intersection_behavior);
- +
- + // Step 2: Iterate over all the unique patterns and find the intersections
- + // they have with the other patterns.
- + for (const auto* pattern : unique_set1) {
- + for (const auto* pattern2 : unique_set2) {
- + absl::optional<URLPattern> intersection =
- + pattern->CreateIntersection(*pattern2);
- + if (intersection)
- + result.patterns_.insert(std::move(*intersection));
- + }
- + }
- +
- + return result;
- +}
- +
- +// static
- +URLPatternSet URLPatternSet::CreateUnion(const URLPatternSet& set1,
- + const URLPatternSet& set2) {
- + return URLPatternSet(
- + base::STLSetUnion<std::set<URLPattern>>(set1.patterns_, set2.patterns_));
- +}
- +
- +// static
- +URLPatternSet URLPatternSet::CreateUnion(
- + const std::vector<URLPatternSet>& sets) {
- + URLPatternSet result;
- + if (sets.empty())
- + return result;
- +
- + // N-way union algorithm is basic O(nlog(n)) merge algorithm.
- + //
- + // Do the first merge step into a working set so that we don't mutate any of
- + // the input.
- + // TODO(devlin): Looks like this creates a bunch of copies; we can probably
- + // clean that up.
- + std::vector<URLPatternSet> working;
- + for (size_t i = 0; i < sets.size(); i += 2) {
- + if (i + 1 < sets.size())
- + working.push_back(CreateUnion(sets[i], sets[i + 1]));
- + else
- + working.push_back(sets[i].Clone());
- + }
- +
- + for (size_t skip = 1; skip < working.size(); skip *= 2) {
- + for (size_t i = 0; i < (working.size() - skip); i += skip) {
- + URLPatternSet u = CreateUnion(working[i], working[i + skip]);
- + working[i].patterns_.swap(u.patterns_);
- + }
- + }
- +
- + result.patterns_.swap(working[0].patterns_);
- + return result;
- +}
- +
- +URLPatternSet::URLPatternSet() = default;
- +
- +URLPatternSet::URLPatternSet(URLPatternSet&& rhs) = default;
- +
- +URLPatternSet::URLPatternSet(const std::set<URLPattern>& patterns)
- + : patterns_(patterns) {}
- +
- +URLPatternSet::~URLPatternSet() = default;
- +
- +URLPatternSet& URLPatternSet::operator=(URLPatternSet&& rhs) = default;
- +
- +bool URLPatternSet::operator==(const URLPatternSet& other) const {
- + return patterns_ == other.patterns_;
- +}
- +
- +std::ostream& operator<<(std::ostream& out,
- + const URLPatternSet& url_pattern_set) {
- + out << "{ ";
- +
- + auto iter = url_pattern_set.patterns().cbegin();
- + if (!url_pattern_set.patterns().empty()) {
- + out << *iter;
- + ++iter;
- + }
- +
- + for (;iter != url_pattern_set.patterns().end(); ++iter)
- + out << ", " << *iter;
- +
- + if (!url_pattern_set.patterns().empty())
- + out << " ";
- +
- + out << "}";
- + return out;
- +}
- +
- +URLPatternSet URLPatternSet::Clone() const {
- + return URLPatternSet(patterns_);
- +}
- +
- +bool URLPatternSet::is_empty() const {
- + return patterns_.empty();
- +}
- +
- +size_t URLPatternSet::size() const {
- + return patterns_.size();
- +}
- +
- +bool URLPatternSet::AddPattern(const URLPattern& pattern) {
- + return patterns_.insert(pattern).second;
- +}
- +
- +void URLPatternSet::AddPatterns(const URLPatternSet& set) {
- + patterns_.insert(set.patterns().begin(),
- + set.patterns().end());
- +}
- +
- +void URLPatternSet::ClearPatterns() {
- + patterns_.clear();
- +}
- +
- +bool URLPatternSet::AddOrigin(int valid_schemes, const GURL& origin) {
- + if (origin.is_empty())
- + return false;
- + const url::Origin real_origin = url::Origin::Create(origin);
- + DCHECK(real_origin.IsSameOriginWith(url::Origin::Create(
- + origin.DeprecatedGetOriginAsURL())));
- + URLPattern origin_pattern(valid_schemes);
- + // Origin adding could fail if |origin| does not match |valid_schemes|.
- + if (origin_pattern.Parse(origin.spec()) !=
- + URLPattern::ParseResult::kSuccess) {
- + return false;
- + }
- + origin_pattern.SetPath("/*");
- + return AddPattern(origin_pattern);
- +}
- +
- +bool URLPatternSet::Contains(const URLPatternSet& other) const {
- + for (auto it = other.begin(); it != other.end(); ++it) {
- + if (!ContainsPattern(*it))
- + return false;
- + }
- +
- + return true;
- +}
- +
- +bool URLPatternSet::ContainsPattern(const URLPattern& pattern) const {
- + for (auto it = begin(); it != end(); ++it) {
- + if (it->Contains(pattern))
- + return true;
- + }
- + return false;
- +}
- +
- +bool URLPatternSet::MatchesURL(const GURL& url) const {
- + for (auto pattern = patterns_.cbegin(); pattern != patterns_.cend();
- + ++pattern) {
- + if (pattern->MatchesURL(url)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: URLPatternSet::MatchesURL true " << url.spec();
- +
- + return true;
- + }
- + }
- +
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: URLPatternSet::MatchesURL false " << url.spec();
- +
- + return false;
- +}
- +
- +bool URLPatternSet::MatchesAllURLs() const {
- + for (auto host = begin(); host != end(); ++host) {
- + if (host->match_all_urls() ||
- + (host->match_subdomains() && host->host().empty()))
- + return true;
- + }
- + return false;
- +}
- +
- +bool URLPatternSet::MatchesSecurityOrigin(const GURL& origin) const {
- + for (auto pattern = patterns_.begin(); pattern != patterns_.end();
- + ++pattern) {
- + if (pattern->MatchesSecurityOrigin(origin))
- + return true;
- + }
- +
- + return false;
- +}
- +
- +bool URLPatternSet::OverlapsWith(const URLPatternSet& other) const {
- + // Two extension extents overlap if there is any one URL that would match at
- + // least one pattern in each of the extents.
- + for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
- + for (auto j = other.patterns().cbegin(); j != other.patterns().cend();
- + ++j) {
- + if (i->OverlapsWith(*j))
- + return true;
- + }
- + }
- +
- + return false;
- +}
- +
- +std::unique_ptr<base::ListValue> URLPatternSet::ToValue() const {
- + std::unique_ptr<base::ListValue> value(new base::ListValue);
- + for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
- + base::Value pattern_str_value(i->GetAsString());
- + if (!base::Contains(value->GetList(), pattern_str_value))
- + value->Append(std::move(pattern_str_value));
- + }
- + return value;
- +}
- +
- +bool URLPatternSet::Populate(const std::vector<std::string>& patterns,
- + int valid_schemes,
- + bool allow_file_access,
- + std::string* error) {
- + ClearPatterns();
- + for (size_t i = 0; i < patterns.size(); ++i) {
- + URLPattern pattern(valid_schemes);
- + if (pattern.Parse(patterns[i]) != URLPattern::ParseResult::kSuccess) {
- + if (error) {
- + *error = ErrorUtils::FormatErrorMessage(kInvalidURLPatternError,
- + patterns[i]);
- + } else {
- + LOG(ERROR) << "Invalid url pattern: " << patterns[i];
- + }
- + return false;
- + }
- + if (!allow_file_access && pattern.MatchesScheme(url::kFileScheme)) {
- + pattern.SetValidSchemes(
- + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
- + }
- + AddPattern(pattern);
- + }
- + return true;
- +}
- +
- +std::unique_ptr<std::vector<std::string>> URLPatternSet::ToStringVector()
- + const {
- + std::unique_ptr<std::vector<std::string>> value(new std::vector<std::string>);
- + for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
- + value->push_back(i->GetAsString());
- + }
- + return value;
- +}
- +
- +bool URLPatternSet::Populate(const base::ListValue& value,
- + int valid_schemes,
- + bool allow_file_access,
- + std::string* error) {
- + std::vector<std::string> patterns;
- + for (const base::Value& pattern : value.GetList()) {
- + const std::string* item = pattern.GetIfString();
- + if (!item)
- + return false;
- + patterns.push_back(*item);
- + }
- + return Populate(patterns, valid_schemes, allow_file_access, error);
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/common/url_pattern_set.h b/components/user_scripts/common/url_pattern_set.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/url_pattern_set.h
- @@ -0,0 +1,160 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
- +#define USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
- +
- +#include <stddef.h>
- +
- +#include <iosfwd>
- +#include <memory>
- +#include <set>
- +
- +#include "url_pattern.h"
- +
- +class GURL;
- +
- +namespace base {
- +class ListValue;
- +class Value;
- +}
- +
- +namespace user_scripts {
- +
- +// Represents the set of URLs an extension uses for web content.
- +class URLPatternSet {
- + public:
- + URLPatternSet(const URLPatternSet&) = delete;
- + URLPatternSet& operator=(const URLPatternSet&) = delete;
- + typedef std::set<URLPattern>::const_iterator const_iterator;
- + typedef std::set<URLPattern>::iterator iterator;
- +
- + // Returns |set1| - |set2|.
- + static URLPatternSet CreateDifference(const URLPatternSet& set1,
- + const URLPatternSet& set2);
- +
- + enum class IntersectionBehavior {
- + // For the following descriptions, consider the two URLPatternSets:
- + // Set 1: {"https://example.com/*", "https://*.google.com/*", "http://*/*"}
- + // Set 2: {"https://example.com/*", "https://google.com/maps",
- + // "*://chromium.org/*"}
- +
- + // Only includes patterns that are exactly in both sets. The intersection of
- + // the two sets above is {"https://example.com/*"}, since that is the only
- + // pattern that appears exactly in each.
- + kStringComparison,
- +
- + // Includes patterns that are effectively contained by both sets. The
- + // intersection of the two sets above is
- + // {
- + // "https://example.com/*" (contained exactly by each set)
- + // "https://google.com/maps" (contained exactly by set 2 and a strict
- + // subset of https://*.google.com/* in set 1)
- + // }
- + kPatternsContainedByBoth,
- +
- + // Includes patterns that are contained by both sets and creates new
- + // patterns to represent the intersection of any others. The intersection of
- + // the two sets above is
- + // {
- + // "https://example.com/*" (contained exactly by each set)
- + // "https://google.com/maps" (contained exactly by set 2 and a strict
- + // subset of https://*.google.com/* in set 1)
- + // "http://chromium.org/*" (the overlap between "http://*/*" in set 1 and
- + // *://chromium.org/*" in set 2).
- + // }
- + // Note that this is the most computationally expensive - potentially
- + // O(n^2) - since it can require comparing each pattern in one set to every
- + // pattern in the other set.
- + kDetailed,
- + };
- +
- + // Returns the intersection of |set1| and |set2| according to
- + // |intersection_behavior|.
- + static URLPatternSet CreateIntersection(
- + const URLPatternSet& set1,
- + const URLPatternSet& set2,
- + IntersectionBehavior intersection_behavior);
- +
- + // Returns the union of |set1| and |set2|.
- + static URLPatternSet CreateUnion(const URLPatternSet& set1,
- + const URLPatternSet& set2);
- +
- + // Returns the union of all sets in |sets|.
- + static URLPatternSet CreateUnion(const std::vector<URLPatternSet>& sets);
- +
- + URLPatternSet();
- + URLPatternSet(URLPatternSet&& rhs);
- + explicit URLPatternSet(const std::set<URLPattern>& patterns);
- + ~URLPatternSet();
- +
- + URLPatternSet& operator=(URLPatternSet&& rhs);
- + bool operator==(const URLPatternSet& rhs) const;
- +
- + bool is_empty() const;
- + size_t size() const;
- + const std::set<URLPattern>& patterns() const { return patterns_; }
- + const_iterator begin() const { return patterns_.begin(); }
- + const_iterator end() const { return patterns_.end(); }
- + iterator erase(iterator iter) { return patterns_.erase(iter); }
- +
- + // Returns a copy of this URLPatternSet; not instrumented as a copy
- + // constructor to avoid accidental/unnecessary copies.
- + URLPatternSet Clone() const;
- +
- + // Adds a pattern to the set. Returns true if a new pattern was inserted,
- + // false if the pattern was already in the set.
- + bool AddPattern(const URLPattern& pattern);
- +
- + // Adds all patterns from |set| into this.
- + void AddPatterns(const URLPatternSet& set);
- +
- + void ClearPatterns();
- +
- + // Adds a pattern based on |origin| to the set.
- + bool AddOrigin(int valid_schemes, const GURL& origin);
- +
- + // Returns true if every URL that matches |set| is matched by this. In other
- + // words, if every pattern in |set| is encompassed by a pattern in this.
- + bool Contains(const URLPatternSet& set) const;
- +
- + // Returns true if any pattern in this set encompasses |pattern|.
- + bool ContainsPattern(const URLPattern& pattern) const;
- +
- + // Test if the extent contains a URL.
- + bool MatchesURL(const GURL& url) const;
- +
- + // Test if the extent matches all URLs (for example, <all_urls>).
- + bool MatchesAllURLs() const;
- +
- + bool MatchesSecurityOrigin(const GURL& origin) const;
- +
- + // Returns true if there is a single URL that would be in two extents.
- + bool OverlapsWith(const URLPatternSet& other) const;
- +
- + // Converts to and from Value for serialization to preferences.
- + std::unique_ptr<base::ListValue> ToValue() const;
- + bool Populate(const base::ListValue& value,
- + int valid_schemes,
- + bool allow_file_access,
- + std::string* error);
- +
- + // Converts to and from a vector of strings.
- + std::unique_ptr<std::vector<std::string>> ToStringVector() const;
- + bool Populate(const std::vector<std::string>& patterns,
- + int valid_schemes,
- + bool allow_file_access,
- + std::string* error);
- +
- + private:
- + // The list of URL patterns that comprise the extent.
- + std::set<URLPattern> patterns_;
- +};
- +
- +std::ostream& operator<<(std::ostream& out,
- + const URLPatternSet& url_pattern_set);
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
- diff --git a/components/user_scripts/common/user_script.cc b/components/user_scripts/common/user_script.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/user_script.cc
- @@ -0,0 +1,329 @@
- +// Copyright 2013 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "user_script.h"
- +
- +#include <stddef.h>
- +#include <stdint.h>
- +
- +#include <memory>
- +#include <utility>
- +
- +#include "base/atomic_sequence_num.h"
- +#include "base/command_line.h"
- +#include "base/pickle.h"
- +#include "base/strings/pattern.h"
- +#include "base/strings/string_util.h"
- +#include "user_scripts_features.h"
- +
- +namespace {
- +
- +// This cannot be a plain int or int64_t because we need to generate unique IDs
- +// from multiple threads.
- +base::AtomicSequenceNumber g_user_script_id_generator;
- +
- +bool UrlMatchesGlobs(const std::vector<std::string>* globs,
- + const GURL& url) {
- + for (auto glob = globs->cbegin(); glob != globs->cend(); ++glob) {
- + if (base::MatchPattern(url.spec(), *glob))
- + return true;
- + }
- +
- + return false;
- +}
- +
- +} // namespace
- +
- +namespace user_scripts {
- +
- +// The bitmask for valid user script injectable schemes used by URLPattern.
- +enum {
- + kValidUserScriptSchemes = //URLPattern::SCHEME_CHROMEUI |
- + URLPattern::SCHEME_HTTP |
- + URLPattern::SCHEME_HTTPS
- + //| URLPattern::SCHEME_FILE |
- + //URLPattern::SCHEME_FTP
- +};
- +
- +// static
- +const char UserScript::kFileExtension[] = ".user.js";
- +
- +// static
- +int UserScript::GenerateUserScriptID() {
- + return g_user_script_id_generator.GetNext();
- +}
- +
- +bool UserScript::IsURLUserScript(const GURL& url,
- + const std::string& mime_type) {
- + return base::EndsWith(url.ExtractFileName(), kFileExtension,
- + base::CompareCase::INSENSITIVE_ASCII) &&
- + mime_type != "text/html";
- +}
- +
- +// static
- +int UserScript::ValidUserScriptSchemes(bool canExecuteScriptEverywhere) {
- + if (canExecuteScriptEverywhere)
- + return URLPattern::SCHEME_ALL;
- + int valid_schemes = kValidUserScriptSchemes;
- + // if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
- + // switches::kExtensionsOnChromeURLs)) {
- + // valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
- + // }
- + return valid_schemes;
- +}
- +
- +UserScript::File::File(const base::FilePath& extension_root,
- + const base::FilePath& relative_path,
- + const GURL& url)
- + : extension_root_(extension_root),
- + relative_path_(relative_path),
- + url_(url) {
- +}
- +
- +UserScript::File::File() {}
- +
- +UserScript::File::File(const File& other)
- + : extension_root_(other.extension_root_),
- + relative_path_(other.relative_path_),
- + url_(other.url_),
- + external_content_(other.external_content_),
- + content_(other.content_),
- + key_(other.key_) {}
- +
- +UserScript::File::~File() {}
- +
- +UserScript::UserScript() = default;
- +UserScript::~UserScript() = default;
- +
- +void UserScript::add_url_pattern(const URLPattern& pattern) {
- + url_set_.AddPattern(pattern);
- +}
- +
- +void UserScript::add_exclude_url_pattern(const URLPattern& pattern) {
- + exclude_url_set_.AddPattern(pattern);
- +}
- +
- +bool UserScript::MatchesURL(const GURL& url) const {
- + // Since the injecton is also provided for native pages,
- + // we must verify that the render process does not include
- + // scripts in the schema that are not allowed
- +
- + // we allow only URLPattern::SCHEME_HTTP(S)
- + URLPattern pattern(kValidUserScriptSchemes);
- + pattern.Parse(url.spec());
- + if (!pattern.IsValidScheme(pattern.scheme()))
- + return false;
- +
- + if (!url_set_.is_empty()) {
- + if (!url_set_.MatchesURL(url)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: No Match for url_set";
- + return false;
- + }
- + }
- +
- + if (!exclude_url_set_.is_empty()) {
- + if (exclude_url_set_.MatchesURL(url)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: No Match for exclude_url_set";
- + return false;
- + }
- + }
- +
- + if (!globs_.empty()) {
- + if (!UrlMatchesGlobs(&globs_, url)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: No Match for globs";
- + return false;
- + }
- + }
- +
- + if (!exclude_globs_.empty()) {
- + if (UrlMatchesGlobs(&exclude_globs_, url)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: No Match for exclude_globs";
- + return false;
- + }
- + }
- +
- + return true;
- +}
- +
- +bool UserScript::MatchesDocument(const GURL& effective_document_url,
- + bool is_subframe) const {
- + if (is_subframe && !match_all_frames())
- + return false;
- +
- + return MatchesURL(effective_document_url);
- +}
- +
- +void UserScript::File::Pickle(base::Pickle* pickle) const {
- + pickle->WriteString(url_.spec());
- + // Do not write path. It's not needed in the renderer.
- + // Do not write content. It will be serialized by other means.
- +}
- +
- +void UserScript::File::Unpickle(const base::Pickle& pickle,
- + base::PickleIterator* iter) {
- + // Read the url from the pickle.
- + std::string url;
- + CHECK(iter->ReadString(&url));
- + set_url(GURL(url));
- +}
- +
- +void UserScript::Pickle(base::Pickle* pickle) const {
- + // Write the simple types to the pickle.
- + pickle->WriteInt(run_location());
- + pickle->WriteInt(user_script_id_);
- + pickle->WriteString(name_);
- + pickle->WriteBool(emulate_greasemonkey());
- + pickle->WriteBool(match_all_frames());
- + pickle->WriteInt(static_cast<int>(match_origin_as_fallback()));
- + pickle->WriteBool(is_incognito_enabled());
- +
- + PickleHostID(pickle, host_id_);
- + pickle->WriteInt(consumer_instance_type());
- + PickleGlobs(pickle, globs_);
- + PickleGlobs(pickle, exclude_globs_);
- + PickleURLPatternSet(pickle, url_set_);
- + PickleURLPatternSet(pickle, exclude_url_set_);
- + PickleScripts(pickle, js_scripts_);
- + PickleScripts(pickle, css_scripts_);
- +}
- +
- +void UserScript::PickleGlobs(base::Pickle* pickle,
- + const std::vector<std::string>& globs) const {
- + pickle->WriteUInt32(globs.size());
- + for (auto glob = globs.cbegin(); glob != globs.cend(); ++glob) {
- + pickle->WriteString(*glob);
- + }
- +}
- +
- +void UserScript::PickleHostID(base::Pickle* pickle,
- + const HostID& host_id) const {
- + pickle->WriteInt(host_id.type());
- + pickle->WriteString(host_id.id());
- +}
- +
- +void UserScript::PickleURLPatternSet(base::Pickle* pickle,
- + const URLPatternSet& pattern_list) const {
- + pickle->WriteUInt32(pattern_list.patterns().size());
- + for (auto pattern = pattern_list.begin(); pattern != pattern_list.end();
- + ++pattern) {
- + pickle->WriteInt(pattern->valid_schemes());
- + pickle->WriteString(pattern->GetAsString());
- + }
- +}
- +
- +void UserScript::PickleScripts(base::Pickle* pickle,
- + const FileList& scripts) const {
- + pickle->WriteUInt32(scripts.size());
- + for (const std::unique_ptr<File>& file : scripts)
- + file->Pickle(pickle);
- +}
- +
- +void UserScript::Unpickle(const base::Pickle& pickle,
- + base::PickleIterator* iter) {
- + // Read the run location.
- + int run_location = 0;
- + CHECK(iter->ReadInt(&run_location));
- + CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST);
- + run_location_ = static_cast<RunLocation>(run_location);
- +
- + CHECK(iter->ReadInt(&user_script_id_));
- + CHECK(iter->ReadString(&name_));
- + CHECK(iter->ReadBool(&emulate_greasemonkey_));
- + CHECK(iter->ReadBool(&match_all_frames_));
- + int match_origin_as_fallback_int = 0;
- + CHECK(iter->ReadInt(&match_origin_as_fallback_int));
- + match_origin_as_fallback_ =
- + static_cast<MatchOriginAsFallbackBehavior>(match_origin_as_fallback_int);
- + CHECK(iter->ReadBool(&incognito_enabled_));
- +
- + UnpickleHostID(pickle, iter, &host_id_);
- +
- + int consumer_instance_type = 0;
- + CHECK(iter->ReadInt(&consumer_instance_type));
- + consumer_instance_type_ =
- + static_cast<ConsumerInstanceType>(consumer_instance_type);
- +
- + UnpickleGlobs(pickle, iter, &globs_);
- + UnpickleGlobs(pickle, iter, &exclude_globs_);
- + UnpickleURLPatternSet(pickle, iter, &url_set_);
- + UnpickleURLPatternSet(pickle, iter, &exclude_url_set_);
- + UnpickleScripts(pickle, iter, &js_scripts_);
- + UnpickleScripts(pickle, iter, &css_scripts_);
- +}
- +
- +void UserScript::UnpickleGlobs(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + std::vector<std::string>* globs) {
- + uint32_t num_globs = 0;
- + CHECK(iter->ReadUInt32(&num_globs));
- + globs->clear();
- + for (uint32_t i = 0; i < num_globs; ++i) {
- + std::string glob;
- + CHECK(iter->ReadString(&glob));
- + globs->push_back(glob);
- + }
- +}
- +
- +void UserScript::UnpickleHostID(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + HostID* host_id) {
- + int type = 0;
- + std::string id;
- + CHECK(iter->ReadInt(&type));
- + CHECK(iter->ReadString(&id));
- + *host_id = HostID(static_cast<HostID::HostType>(type), id);
- +}
- +
- +void UserScript::UnpickleURLPatternSet(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + URLPatternSet* pattern_list) {
- + uint32_t num_patterns = 0;
- + CHECK(iter->ReadUInt32(&num_patterns));
- +
- + pattern_list->ClearPatterns();
- + for (uint32_t i = 0; i < num_patterns; ++i) {
- + int valid_schemes;
- + CHECK(iter->ReadInt(&valid_schemes));
- +
- + std::string pattern_str;
- + CHECK(iter->ReadString(&pattern_str));
- +
- + URLPattern pattern(kValidUserScriptSchemes);
- + URLPattern::ParseResult result = pattern.Parse(pattern_str);
- + CHECK(URLPattern::ParseResult::kSuccess == result)
- + << URLPattern::GetParseResultString(result) << " "
- + << pattern_str.c_str();
- +
- + pattern.SetValidSchemes(valid_schemes);
- + pattern_list->AddPattern(pattern);
- + }
- +}
- +
- +void UserScript::UnpickleScripts(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + FileList* scripts) {
- + uint32_t num_files = 0;
- + CHECK(iter->ReadUInt32(&num_files));
- + scripts->clear();
- + for (uint32_t i = 0; i < num_files; ++i) {
- + std::unique_ptr<File> file(new File());
- + file->Unpickle(pickle, iter);
- + scripts->push_back(std::move(file));
- + }
- +}
- +
- +UserScriptIDPair::UserScriptIDPair(int id, const HostID& host_id)
- + : id(id), host_id(host_id) {}
- +
- +UserScriptIDPair::UserScriptIDPair(int id) : id(id), host_id(HostID()) {}
- +
- +bool operator<(const UserScriptIDPair& a, const UserScriptIDPair& b) {
- + return a.id < b.id;
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/common/user_script.h b/components/user_scripts/common/user_script.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/user_script.h
- @@ -0,0 +1,403 @@
- +// Copyright 2013 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_USER_SCRIPT_H_
- +#define USERSCRIPTS_COMMON_USER_SCRIPT_H_
- +
- +#include <memory>
- +#include <string>
- +#include <vector>
- +
- +#include "base/files/file_path.h"
- +#include "base/strings/string_piece.h"
- +#include "script_constants.h"
- +#include "host_id.h"
- +#include "url_pattern.h"
- +#include "url_pattern_set.h"
- +#include "url/gurl.h"
- +
- +namespace base {
- +class Pickle;
- +class PickleIterator;
- +}
- +
- +namespace user_scripts {
- +
- +// Represents a user script, either a standalone one, or one that is part of an
- +// extension.
- +class UserScript {
- + public:
- + UserScript(const UserScript&) = delete;
- + UserScript& operator=(const UserScript&) = delete;
- + // The file extension for standalone user scripts.
- + static const char kFileExtension[];
- +
- + static int GenerateUserScriptID();
- +
- + // Check if a URL should be treated as a user script and converted to an
- + // extension.
- + static bool IsURLUserScript(const GURL& url, const std::string& mime_type);
- +
- + // Get the valid user script schemes for the current process. If
- + // canExecuteScriptEverywhere is true, this will return ALL_SCHEMES.
- + static int ValidUserScriptSchemes(bool canExecuteScriptEverywhere = false);
- +
- + // TODO(rdevlin.cronin) This and RunLocation don't really belong here, since
- + // they are used for more than UserScripts (e.g., tabs.executeScript()).
- + // The type of injected script.
- + enum InjectionType {
- + // A content script specified in the extension's manifest.
- + CONTENT_SCRIPT,
- + // A script injected via, e.g. tabs.executeScript().
- + //PROGRAMMATIC_SCRIPT
- + };
- + // The last type of injected script; used for enum verification in IPC.
- + // Update this if you add more injected script types!
- + static const InjectionType INJECTION_TYPE_LAST = CONTENT_SCRIPT/*PROGRAMMATIC_SCRIPT*/;
- +
- + // Locations that user scripts can be run inside the document.
- + // The three run locations must strictly follow each other in both load order
- + // (i.e., start *always* comes before end) and numerically, as we use
- + // arithmetic checking (e.g., curr == last + 1). So, no bitmasks here!!
- + enum RunLocation {
- + UNDEFINED,
- + DOCUMENT_START, // After the documentElement is created, but before
- + // anything else happens.
- + DOCUMENT_END, // After the entire document is parsed. Same as
- + // DOMContentLoaded.
- + DOCUMENT_IDLE, // Sometime after DOMContentLoaded, as soon as the document
- + // is "idle". Currently this uses the simple heuristic of:
- + // min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no
- + // particular injection point is guaranteed.
- + RUN_DEFERRED, // The user script's injection was deferred for permissions
- + // reasons, and was executed at a later time.
- + BROWSER_DRIVEN, // The user script will be injected when triggered by an
- + // IPC in the browser process.
- + RUN_LOCATION_LAST // Leave this as the last item.
- + };
- +
- + // Holds script file info.
- + class File {
- + public:
- + File(const base::FilePath& extension_root,
- + const base::FilePath& relative_path,
- + const GURL& url);
- + File();
- + File(const File& other);
- + ~File();
- +
- + const base::FilePath& extension_root() const { return extension_root_; }
- + const base::FilePath& relative_path() const { return relative_path_; }
- +
- + const GURL& url() const { return url_; }
- + void set_url(const GURL& url) { url_ = url; }
- +
- + // If external_content_ is set returns it as content otherwise it returns
- + // content_
- + const base::StringPiece GetContent() const {
- + if (external_content_.data())
- + return external_content_;
- + else
- + return content_;
- + }
- + void set_external_content(const base::StringPiece& content) {
- + external_content_ = content;
- + }
- + void set_content(const base::StringPiece& content) {
- + content_.assign(content.begin(), content.end());
- + }
- +
- + const std::string& key() const { return key_; }
- + void set_key(const std::string& key) {
- + key_ = key;
- + }
- +
- + // Serialization support. The content and FilePath members will not be
- + // serialized!
- + void Pickle(base::Pickle* pickle) const;
- + void Unpickle(const base::Pickle& pickle, base::PickleIterator* iter);
- +
- + private:
- + // Where the script file lives on the disk. We keep the path split so that
- + // it can be localized at will.
- + base::FilePath extension_root_;
- + base::FilePath relative_path_;
- +
- + // The url to this script file.
- + GURL url_;
- +
- + // The script content. It can be set to either loaded_content_ or
- + // externally allocated string.
- + base::StringPiece external_content_;
- +
- + // Set when the content is loaded by LoadContent
- + std::string content_;
- +
- + std::string key_;
- + };
- +
- + using FileList = std::vector<std::unique_ptr<File>>;
- +
- + // Type of a API consumer instance that user scripts will be injected on.
- + enum ConsumerInstanceType { TAB, WEBVIEW };
- +
- + // Constructor. Default the run location to document end, which is like
- + // Greasemonkey and probably more useful for typical scripts.
- + UserScript();
- + ~UserScript();
- +
- + // Performs a copy of all fields except file contents.
- + // static std::unique_ptr<UserScript> CopyMetadataFrom(const UserScript& other);
- +
- + const std::string& name_space() const { return name_space_; }
- + void set_name_space(const std::string& name_space) {
- + name_space_ = name_space;
- + }
- +
- + const std::string& name() const { return name_; }
- + void set_name(const std::string& name) { name_ = name; }
- +
- + const std::string& version() const { return version_; }
- + void set_version(const std::string& version) {
- + version_ = version;
- + }
- +
- + const std::string& key() const { return key_; }
- + void set_key(const std::string& key) {
- + key_ = key;
- + }
- +
- + const std::string& file_path() const { return file_path_; }
- + void set_file_path(const std::string& file_path) {
- + file_path_ = file_path;
- + }
- +
- + const std::string& url_source() const { return url_source_; }
- + void set_url_source(const std::string& url_source) {
- + url_source_ = url_source;
- + }
- +
- + const std::string& description() const { return description_; }
- + void set_description(const std::string& description) {
- + description_ = description;
- + }
- +
- + const std::string& parser_error() const { return parser_error_; }
- + void set_parser_error(const std::string& parser_error) {
- + parser_error_ = parser_error;
- + }
- +
- + bool force_disabled() const { return force_disabled_; }
- + void set_force_disabled() {
- + force_disabled_ = true;
- + }
- +
- + // The place in the document to run the script.
- + RunLocation run_location() const { return run_location_; }
- + void set_run_location(RunLocation location) { run_location_ = location; }
- +
- + // Whether to emulate greasemonkey when running this script.
- + bool emulate_greasemonkey() const { return emulate_greasemonkey_; }
- + void set_emulate_greasemonkey(bool val) { emulate_greasemonkey_ = val; }
- +
- + // Whether to match all frames, or only the top one.
- + bool match_all_frames() const { return match_all_frames_; }
- + void set_match_all_frames(bool val) { match_all_frames_ = val; }
- +
- + // Whether to match the origin as a fallback if the URL cannot be used
- + // directly.
- + MatchOriginAsFallbackBehavior match_origin_as_fallback() const {
- + return match_origin_as_fallback_;
- + }
- + void set_match_origin_as_fallback(MatchOriginAsFallbackBehavior val) {
- + match_origin_as_fallback_ = val;
- + }
- +
- + // The globs, if any, that determine which pages this script runs against.
- + // These are only used with "standalone" Greasemonkey-like user scripts.
- + const std::vector<std::string>& globs() const { return globs_; }
- + void add_glob(const std::string& glob) { globs_.push_back(glob); }
- + void clear_globs() { globs_.clear(); }
- + const std::vector<std::string>& exclude_globs() const {
- + return exclude_globs_;
- + }
- + void add_exclude_glob(const std::string& glob) {
- + exclude_globs_.push_back(glob);
- + }
- + void clear_exclude_globs() { exclude_globs_.clear(); }
- +
- + // The URLPatterns, if any, that determine which pages this script runs
- + // against.
- + const URLPatternSet& url_patterns() const { return url_set_; }
- + void add_url_pattern(const URLPattern& pattern);
- + const URLPatternSet& exclude_url_patterns() const {
- + return exclude_url_set_;
- + }
- + void add_exclude_url_pattern(const URLPattern& pattern);
- +
- + // List of js scripts for this user script
- + FileList& js_scripts() { return js_scripts_; }
- + const FileList& js_scripts() const { return js_scripts_; }
- +
- + // List of css scripts for this user script
- + FileList& css_scripts() { return css_scripts_; }
- + const FileList& css_scripts() const { return css_scripts_; }
- +
- + const std::string& extension_id() const { return host_id_.id(); }
- +
- + const HostID& host_id() const { return host_id_; }
- + void set_host_id(const HostID& host_id) { host_id_ = host_id; }
- +
- + const ConsumerInstanceType& consumer_instance_type() const {
- + return consumer_instance_type_;
- + }
- + void set_consumer_instance_type(
- + const ConsumerInstanceType& consumer_instance_type) {
- + consumer_instance_type_ = consumer_instance_type;
- + }
- +
- + int id() const { return user_script_id_; }
- + void set_id(int id) { user_script_id_ = id; }
- +
- + // TODO(lazyboy): Incognito information is extension specific, it doesn't
- + // belong here. We should be able to determine this in the renderer/ where it
- + // is used.
- + bool is_incognito_enabled() const { return incognito_enabled_; }
- + void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; }
- +
- + // Returns true if the script should be applied to the specified URL, false
- + // otherwise.
- + bool MatchesURL(const GURL& url) const;
- +
- + // Returns true if the script should be applied to the given
- + // |effective_document_url|. It is the caller's responsibility to calculate
- + // |effective_document_url| based on match_origin_as_fallback().
- + bool MatchesDocument(const GURL& effective_document_url,
- + bool is_subframe) const;
- +
- + // Serializes the UserScript into a pickle. The content of the scripts and
- + // paths to UserScript::Files will not be serialized!
- + void Pickle(base::Pickle* pickle) const;
- +
- + // Deserializes the script from a pickle. Note that this always succeeds
- + // because presumably we were the one that pickled it, and we did it
- + // correctly.
- + void Unpickle(const base::Pickle& pickle, base::PickleIterator* iter);
- +
- + private:
- + // base::Pickle helper functions used to pickle the individual types of
- + // components.
- + void PickleGlobs(base::Pickle* pickle,
- + const std::vector<std::string>& globs) const;
- + void PickleHostID(base::Pickle* pickle, const HostID& host_id) const;
- + void PickleURLPatternSet(base::Pickle* pickle,
- + const URLPatternSet& pattern_list) const;
- + void PickleScripts(base::Pickle* pickle, const FileList& scripts) const;
- +
- + // Unpickle helper functions used to unpickle individual types of components.
- + void UnpickleGlobs(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + std::vector<std::string>* globs);
- + void UnpickleHostID(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + HostID* host_id);
- + void UnpickleURLPatternSet(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + URLPatternSet* pattern_list);
- + void UnpickleScripts(const base::Pickle& pickle,
- + base::PickleIterator* iter,
- + FileList* scripts);
- +
- + // The location to run the script inside the document.
- + RunLocation run_location_ = DOCUMENT_IDLE;
- +
- + // The namespace of the script. This is used by Greasemonkey in the same way
- + // as XML namespaces. Only used when parsing Greasemonkey-style scripts.
- + std::string name_space_;
- +
- + // The script's name. Only used when parsing Greasemonkey-style scripts.
- + std::string name_;
- +
- + // A longer description. Only used when parsing Greasemonkey-style scripts.
- + std::string description_;
- +
- + // Parser error to show to user
- + std::string parser_error_;
- +
- + // A version number of the script. Only used when parsing Greasemonkey-style
- + // scripts.
- + std::string version_;
- +
- + // Greasemonkey-style globs that determine pages to inject the script into.
- + // These are only used with standalone scripts.
- + std::vector<std::string> globs_;
- + std::vector<std::string> exclude_globs_;
- +
- + // URLPatterns that determine pages to inject the script into. These are
- + // only used with scripts that are part of extensions.
- + URLPatternSet url_set_;
- + URLPatternSet exclude_url_set_;
- +
- + // List of js scripts defined in content_scripts
- + FileList js_scripts_;
- +
- + // List of css scripts defined in content_scripts
- + FileList css_scripts_;
- +
- + // internal key of scripts
- + std::string key_;
- +
- + std::string file_path_;
- +
- + // url source of script
- + std::string url_source_;
- +
- + // The ID of the host this script is a part of. The |ID| of the
- + // |host_id| can be empty if the script is a "standlone" user script.
- + HostID host_id_;
- +
- + // The type of the consumer instance that the script will be injected.
- + ConsumerInstanceType consumer_instance_type_ = TAB;
- +
- + // The globally-unique id associated with this user script. -1 indicates
- + // "invalid".
- + int user_script_id_ = -1;
- +
- + // Whether we should try to emulate Greasemonkey's APIs when running this
- + // script.
- + bool emulate_greasemonkey_ = false;
- +
- + // Whether the user script should run in all frames, or only just the top one.
- + bool match_all_frames_ = false;
- +
- + // Whether the user script should run in frames whose initiator / precursor
- + // origin matches a match pattern, if an appropriate URL cannot be found for
- + // the frame for matching purposes, such as in the case of about:, data:, and
- + // other schemes.
- + MatchOriginAsFallbackBehavior match_origin_as_fallback_ =
- + MatchOriginAsFallbackBehavior::kNever;
- +
- + // True if the script should be injected into an incognito tab.
- + bool incognito_enabled_ = false;
- +
- + // Script cannot be enabled
- + bool force_disabled_ = false;
- +};
- +
- +// Information we need while removing scripts from a UserScriptLoader.
- +struct UserScriptIDPair {
- + UserScriptIDPair(int id, const HostID& host_id);
- + explicit UserScriptIDPair(int id);
- +
- + int id;
- + HostID host_id;
- +};
- +
- +bool operator<(const UserScriptIDPair& a, const UserScriptIDPair& b);
- +
- +using UserScriptList = std::vector<std::unique_ptr<UserScript>>;
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_COMMON_USER_SCRIPT_H_
- diff --git a/components/user_scripts/common/user_scripts_features.cc b/components/user_scripts/common/user_scripts_features.cc
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/common/user_scripts_features.cc
- @@ -0,0 +1,32 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#include "user_scripts_features.h"
- +
- +#include "build/build_config.h"
- +
- +namespace user_scripts {
- +
- +namespace features {
- +
- +const base::Feature kEnableLoggingUserScripts =
- + {"EnableLoggingUserScripts",
- + base::FEATURE_DISABLED_BY_DEFAULT};
- +
- +}
- +
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/common/user_scripts_features.h b/components/user_scripts/common/user_scripts_features.h
- new file mode 100644
- --- /dev/null
- +++ b/components/user_scripts/common/user_scripts_features.h
- @@ -0,0 +1,34 @@
- +/*
- + This file is part of Bromite.
- +
- + Bromite is free software: you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation, either version 3 of the License, or
- + (at your option) any later version.
- +
- + Bromite is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
- +*/
- +
- +#ifndef USERSCRIPTS_COMMON_USERSCRIPTS_FEATURES_H_
- +#define USERSCRIPTS_COMMON_USERSCRIPTS_FEATURES_H_
- +
- +// This file defines all the base::FeatureList features for the Password Manager
- +// module.
- +
- +#include "base/feature_list.h"
- +
- +namespace user_scripts {
- +
- +namespace features {
- + extern const base::Feature kEnableLoggingUserScripts;
- +}
- +
- +}
- +
- +#endif
- \ No newline at end of file
- diff --git a/components/user_scripts/common/view_type.cc b/components/user_scripts/common/view_type.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/view_type.cc
- @@ -0,0 +1,39 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "view_type.h"
- +
- +#include "base/strings/string_piece.h"
- +
- +namespace user_scripts {
- +
- +bool GetViewTypeFromString(const std::string& view_type,
- + ViewType* view_type_out) {
- + // TODO(devlin): This map doesn't contain the following values:
- + // - VIEW_TYPE_BACKGROUND_CONTENTS
- + // - VIEW_TYPE_COMPONENT
- + // - VIEW_TYPE_EXTENSION_GUEST
- + // Why? Is it just because we don't expose those types to JS?
- + static const struct {
- + ViewType type;
- + base::StringPiece name;
- + } constexpr kTypeMap[] = {
- + // {VIEW_TYPE_APP_WINDOW, "APP_WINDOW"},
- + // {VIEW_TYPE_EXTENSION_BACKGROUND_PAGE, "BACKGROUND"},
- + // {VIEW_TYPE_EXTENSION_DIALOG, "EXTENSION_DIALOG"},
- + // {VIEW_TYPE_EXTENSION_POPUP, "POPUP"},
- + {VIEW_TYPE_TAB_CONTENTS, "TAB"},
- + };
- +
- + for (const auto& entry : kTypeMap) {
- + if (entry.name == view_type) {
- + *view_type_out = entry.type;
- + return true;
- + }
- + }
- +
- + return false;
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/common/view_type.h b/components/user_scripts/common/view_type.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/common/view_type.h
- @@ -0,0 +1,48 @@
- +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_COMMON_VIEW_TYPE_H_
- +#define USERSCRIPTS_COMMON_VIEW_TYPE_H_
- +
- +#include <string>
- +
- +namespace user_scripts {
- +
- +// Icky RTTI used by a few systems to distinguish the host type of a given
- +// WebContents.
- +//
- +// Do not change or reuse the the entry values in this list as this is used in
- +// ExtensionViewType enum in tools/metrics/histograms/enums.xml.
- +//
- +// TODO(aa): Remove this and teach those systems to keep track of their own
- +// data.
- +enum ViewType {
- + VIEW_TYPE_INVALID = 0,
- + // VIEW_TYPE_APP_WINDOW = 1,
- + // VIEW_TYPE_BACKGROUND_CONTENTS = 2,
- +
- + // // For custom parts of Chrome if no other type applies.
- + // VIEW_TYPE_COMPONENT = 3,
- +
- + // VIEW_TYPE_EXTENSION_BACKGROUND_PAGE = 4,
- + // VIEW_TYPE_EXTENSION_DIALOG = 5,
- + // VIEW_TYPE_EXTENSION_GUEST = 6,
- + // VIEW_TYPE_EXTENSION_POPUP = 7,
- +
- + // Panels were removed in https://crbug.com/571511.
- + // DEPRECATED_VIEW_TYPE_PANEL = 8,
- +
- + VIEW_TYPE_TAB_CONTENTS = 9,
- +
- + VIEW_TYPE_LAST = VIEW_TYPE_TAB_CONTENTS
- +};
- +
- +// Matches the |view_type| to the corresponding ViewType, and populates
- +// |view_type_out|. Returns true if a match is found.
- +bool GetViewTypeFromString(const std::string& view_type,
- + ViewType* view_type_out);
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_COMMON_VIEW_TYPE_H_
- diff --git a/components/user_scripts/renderer/BUILD.gn b/components/user_scripts/renderer/BUILD.gn
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/BUILD.gn
- @@ -0,0 +1,67 @@
- +# Copyright 2015 The Chromium Authors. All rights reserved.
- +# Use of this source code is governed by a BSD-style license that can be
- +# found in the LICENSE file.
- +
- +import("//tools/grit/grit_rule.gni")
- +import("//tools/grit/repack.gni")
- +
- +group("user_scripts_resources") {
- + public_deps = [
- + ":user_scripts_renderer_resources",
- + ]
- +}
- +
- +grit("user_scripts_renderer_resources") {
- + source = "resources/user_scripts_renderer_resources.grd"
- + outputs = [
- + "grit/user_scripts_renderer_resources.h",
- + "user_scripts_renderer_resources.pak",
- + ]
- + grit_flags = [
- + "-E",
- + "mojom_root=" + rebase_path(root_gen_dir, root_build_dir),
- + ]
- +}
- +
- +static_library("renderer") {
- + sources = [
- + "extension_frame_helper.cc",
- + "extension_frame_helper.h",
- + "injection_host.cc",
- + "injection_host.h",
- + "script_injection_manager.cc",
- + "script_injection_manager.h",
- + "script_injection_callback.cc",
- + "script_injection_callback.h",
- + "script_injection.cc",
- + "script_injection.h",
- + "script_injector.h",
- + "script_context.cc",
- + "script_context.h",
- + "scripts_run_info.cc",
- + "scripts_run_info.h",
- + "user_script_injector.cc",
- + "user_script_injector.h",
- + "user_script_set_manager.cc",
- + "user_script_set_manager.h",
- + "user_script_set.cc",
- + "user_script_set.h",
- + "user_scripts_dispatcher.cc",
- + "user_scripts_dispatcher.h",
- + "user_scripts_renderer_client.cc",
- + "user_scripts_renderer_client.h",
- + "web_ui_injection_host.cc",
- + "web_ui_injection_host.h",
- + ]
- +
- + deps = [
- + ":user_scripts_resources",
- + "//base",
- + "//content/public/common",
- + "//content/public/renderer",
- + "//components/user_scripts/common",
- + "//mojo/public/cpp/bindings",
- + "//third_party/blink/public:blink_headers",
- + "//v8",
- + ]
- +}
- diff --git a/components/user_scripts/renderer/extension_frame_helper.cc b/components/user_scripts/renderer/extension_frame_helper.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/extension_frame_helper.cc
- @@ -0,0 +1,96 @@
- +// Copyright 2013 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "extension_frame_helper.h"
- +
- +#include <set>
- +
- +#include "base/metrics/histogram_macros.h"
- +#include "base/strings/string_util.h"
- +#include "base/timer/elapsed_timer.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_view.h"
- +#include "../common/constants.h"
- +#include "third_party/blink/public/platform/web_security_origin.h"
- +#include "third_party/blink/public/web/web_console_message.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_document_loader.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "third_party/blink/public/web/web_settings.h"
- +#include "third_party/blink/public/web/web_view.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +base::LazyInstance<std::set<const ExtensionFrameHelper*>>::DestructorAtExit
- + g_frame_helpers = LAZY_INSTANCE_INITIALIZER;
- +
- +// Runs every callback in |callbacks_to_be_run_and_cleared| while |frame_helper|
- +// is valid, and clears |callbacks_to_be_run_and_cleared|.
- +void RunCallbacksWhileFrameIsValid(
- + base::WeakPtr<ExtensionFrameHelper> frame_helper,
- + std::vector<base::OnceClosure>* callbacks_to_be_run_and_cleared) {
- + // The JavaScript code can cause re-entrancy. To avoid a deadlock, don't run
- + // callbacks that are added during the iteration.
- + std::vector<base::OnceClosure> callbacks;
- + callbacks_to_be_run_and_cleared->swap(callbacks);
- + for (auto& callback : callbacks) {
- + std::move(callback).Run();
- + if (!frame_helper.get())
- + return; // Frame and ExtensionFrameHelper invalidated by callback.
- + }
- +}
- +
- +} // namespace
- +
- +ExtensionFrameHelper::ExtensionFrameHelper(content::RenderFrame* render_frame)
- + : content::RenderFrameObserver(render_frame),
- + content::RenderFrameObserverTracker<ExtensionFrameHelper>(render_frame),
- + tab_id_(-1) {
- + g_frame_helpers.Get().insert(this);
- +}
- +
- +ExtensionFrameHelper::~ExtensionFrameHelper() {
- + g_frame_helpers.Get().erase(this);
- +}
- +
- +void ExtensionFrameHelper::ScheduleAtDocumentStart(
- + base::OnceClosure callback) {
- + document_element_created_callbacks_.push_back(std::move(callback));
- +}
- +
- +void ExtensionFrameHelper::ScheduleAtDocumentEnd(
- + base::OnceClosure callback) {
- + document_load_finished_callbacks_.push_back(std::move(callback));
- +}
- +
- +void ExtensionFrameHelper::ScheduleAtDocumentIdle(
- + base::OnceClosure callback) {
- + document_idle_callbacks_.push_back(std::move(callback));
- +}
- +
- +void ExtensionFrameHelper::RunScriptsAtDocumentStart() {
- + RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
- + &document_element_created_callbacks_);
- + // |this| might be dead by now.
- +}
- +
- +void ExtensionFrameHelper::RunScriptsAtDocumentEnd() {
- + RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
- + &document_load_finished_callbacks_);
- + // |this| might be dead by now.
- +}
- +
- +void ExtensionFrameHelper::RunScriptsAtDocumentIdle() {
- + RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
- + &document_idle_callbacks_);
- + // |this| might be dead by now.
- +}
- +
- +void ExtensionFrameHelper::OnDestruct() {
- + delete this;
- +}
- +
- +} // namespace user_scripts
- diff --git a/components/user_scripts/renderer/extension_frame_helper.h b/components/user_scripts/renderer/extension_frame_helper.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/extension_frame_helper.h
- @@ -0,0 +1,91 @@
- +// Copyright 2013 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
- +#define USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
- +
- +#include <string>
- +#include <vector>
- +
- +#include "base/callback_forward.h"
- +#include "base/memory/weak_ptr.h"
- +#include "content/public/renderer/render_frame_observer.h"
- +#include "content/public/renderer/render_frame_observer_tracker.h"
- +#include "../common/view_type.h"
- +#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
- +#include "v8/include/v8.h"
- +
- +struct ExtensionMsg_ExternalConnectionInfo;
- +struct ExtensionMsg_TabConnectionInfo;
- +
- +namespace base {
- +class ListValue;
- +}
- +
- +namespace user_scripts {
- +
- +class Dispatcher;
- +struct Message;
- +struct PortId;
- +class ScriptContext;
- +
- +// RenderFrame-level plumbing for extension features.
- +class ExtensionFrameHelper
- + : public content::RenderFrameObserver,
- + public content::RenderFrameObserverTracker<ExtensionFrameHelper> {
- + public:
- + ExtensionFrameHelper(const ExtensionFrameHelper&) = delete;
- + ExtensionFrameHelper& operator=(const ExtensionFrameHelper&) = delete;
- + ExtensionFrameHelper(content::RenderFrame* render_frame /*,
- + Dispatcher* extension_dispatcher*/);
- + ~ExtensionFrameHelper() override;
- +
- + int tab_id() const { return tab_id_; }
- +
- + // Called when the document element has been inserted in this frame. This
- + // method may invoke untrusted JavaScript code that invalidate the frame and
- + // this ExtensionFrameHelper.
- + void RunScriptsAtDocumentStart();
- +
- + // Called after the DOMContentLoaded event has fired.
- + void RunScriptsAtDocumentEnd();
- +
- + // Called before the window.onload event is fired.
- + void RunScriptsAtDocumentIdle();
- +
- + // Schedule a callback, to be run at the next RunScriptsAtDocumentStart
- + // notification. Only call this when you are certain that there will be such a
- + // notification, e.g. from RenderFrameObserver::DidCreateDocumentElement.
- + // Otherwise the callback is never invoked, or invoked for a document that you
- + // were not expecting.
- + void ScheduleAtDocumentStart(base::OnceClosure callback);
- +
- + // Schedule a callback, to be run at the next RunScriptsAtDocumentEnd call.
- + void ScheduleAtDocumentEnd(base::OnceClosure callback);
- +
- + // Schedule a callback, to be run at the next RunScriptsAtDocumentIdle call.
- + void ScheduleAtDocumentIdle(base::OnceClosure callback);
- +
- + private:
- +
- + void OnDestruct() override;
- +
- + // The id of the tab the render frame is attached to.
- + int tab_id_;
- +
- + // Callbacks to be run at the next RunScriptsAtDocumentStart notification.
- + std::vector<base::OnceClosure> document_element_created_callbacks_;
- +
- + // Callbacks to be run at the next RunScriptsAtDocumentEnd notification.
- + std::vector<base::OnceClosure> document_load_finished_callbacks_;
- +
- + // Callbacks to be run at the next RunScriptsAtDocumentIdle notification.
- + std::vector<base::OnceClosure> document_idle_callbacks_;
- +
- + base::WeakPtrFactory<ExtensionFrameHelper> weak_ptr_factory_{this};
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
- diff --git a/components/user_scripts/renderer/injection_host.cc b/components/user_scripts/renderer/injection_host.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/injection_host.cc
- @@ -0,0 +1,12 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "injection_host.h"
- +
- +InjectionHost::InjectionHost(const HostID& host_id) :
- + id_(host_id) {
- +}
- +
- +InjectionHost::~InjectionHost() {
- +}
- diff --git a/components/user_scripts/renderer/injection_host.h b/components/user_scripts/renderer/injection_host.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/injection_host.h
- @@ -0,0 +1,41 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_INJECTION_HOST_H_
- +#define USERSCRIPTS_RENDERER_INJECTION_HOST_H_
- +
- +#include "../common/host_id.h"
- +#include "url/gurl.h"
- +
- +namespace content {
- +class RenderFrame;
- +}
- +
- +// An interface for all kinds of hosts who own user scripts.
- +class InjectionHost {
- + public:
- + InjectionHost(const InjectionHost&) = delete;
- + InjectionHost& operator=(const InjectionHost&) = delete;
- + InjectionHost(const HostID& host_id);
- + virtual ~InjectionHost();
- +
- + // Returns the CSP to be used for the isolated world. Currently this only
- + // bypasses the main world CSP. If null is returned, the main world CSP is not
- + // bypassed.
- + virtual const std::string* GetContentSecurityPolicy() const = 0;
- +
- + // The base url for the host.
- + virtual const GURL& url() const = 0;
- +
- + // The human-readable name of the host.
- + virtual const std::string& name() const = 0;
- +
- + const HostID& id() const { return id_; }
- +
- + private:
- + // The ID of the host.
- + HostID id_;
- +};
- +
- +#endif // USERSCRIPTS_RENDERER_INJECTION_HOST_H_
- diff --git a/components/user_scripts/renderer/resources/greasemonkey_api.js b/components/user_scripts/renderer/resources/greasemonkey_api.js
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/resources/greasemonkey_api.js
- @@ -0,0 +1,82 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +// -----------------------------------------------------------------------------
- +// NOTE: If you change this file you need to touch renderer_resources.grd to
- +// have your change take effect.
- +// -----------------------------------------------------------------------------
- +
- +// Partial implementation of the Greasemonkey API, see:
- +// http://wiki.greasespot.net/Greasemonkey_Manual:APIs
- +
- +function GM_addStyle(css) {
- + var parent = document.getElementsByTagName("head")[0];
- + if (!parent) {
- + parent = document.documentElement;
- + }
- + var style = document.createElement("style");
- + style.type = "text/css";
- + var textNode = document.createTextNode(css);
- + style.appendChild(textNode);
- + parent.appendChild(style);
- +}
- +
- +function GM_xmlhttpRequest(details) {
- + function setupEvent(xhr, url, eventName, callback) {
- + xhr[eventName] = function () {
- + var isComplete = xhr.readyState == 4;
- + var responseState = {
- + responseText: xhr.responseText,
- + readyState: xhr.readyState,
- + responseHeaders: isComplete ? xhr.getAllResponseHeaders() : "",
- + status: isComplete ? xhr.status : 0,
- + statusText: isComplete ? xhr.statusText : "",
- + finalUrl: isComplete ? url : ""
- + };
- + callback(responseState);
- + };
- + }
- +
- + var xhr = new XMLHttpRequest();
- + var eventNames = ["onload", "onerror", "onreadystatechange"];
- + for (var i = 0; i < eventNames.length; i++ ) {
- + var eventName = eventNames[i];
- + if (eventName in details) {
- + setupEvent(xhr, details.url, eventName, details[eventName]);
- + }
- + }
- +
- + xhr.open(details.method, details.url);
- +
- + if (details.overrideMimeType) {
- + xhr.overrideMimeType(details.overrideMimeType);
- + }
- + if (details.headers) {
- + for (var header in details.headers) {
- + xhr.setRequestHeader(header, details.headers[header]);
- + }
- + }
- + xhr.send(details.data ? details.data : null);
- +}
- +
- +function GM_openInTab(url) {
- + window.open(url, "");
- +}
- +
- +function GM_log(message) {
- + window.console.log(message);
- +}
- +
- +(function() {
- + function generateGreasemonkeyStub(name) {
- + return function() {
- + console.log("%s is not supported.", name);
- + };
- + }
- +
- + var apis = ["GM_getValue", "GM_setValue", "GM_registerMenuCommand"];
- + for (var i = 0, api; api = apis[i]; i++) {
- + window[api] = generateGreasemonkeyStub(api);
- + }
- +})();
- diff --git a/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd b/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
- @@ -0,0 +1,14 @@
- +<?xml version="1.0" encoding="UTF-8"?>
- +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
- + <outputs>
- + <output filename="grit/user_scripts_renderer_resources.h" type="rc_header">
- + <emit emit_type='prepend'></emit>
- + </output>
- + <output filename="user_scripts_renderer_resources.pak" type="data_package" />
- + </outputs>
- + <release seq="1">
- + <includes>
- + <include name="IDR_GREASEMONKEY_API_JS" file="greasemonkey_api.js" type="BINDATA" />
- + </includes>
- + </release>
- +</grit>
- diff --git a/components/user_scripts/renderer/script_context.cc b/components/user_scripts/renderer/script_context.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_context.cc
- @@ -0,0 +1,215 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "script_context.h"
- +
- +#include "base/command_line.h"
- +#include "base/containers/flat_set.h"
- +#include "base/containers/contains.h"
- +#include "base/logging.h"
- +#include "base/no_destructor.h"
- +#include "base/stl_util.h"
- +#include "base/strings/string_util.h"
- +#include "base/strings/stringprintf.h"
- +#include "base/values.h"
- +#include "content/public/common/content_switches.h"
- +#include "content/public/common/url_constants.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "../common/constants.h"
- +#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
- +#include "third_party/blink/public/platform/web_security_origin.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_document_loader.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "third_party/blink/public/web/web_navigation_params.h"
- +#include "v8/include/v8.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +GURL GetEffectiveDocumentURL(
- + blink::WebLocalFrame* frame,
- + const GURL& document_url,
- + MatchOriginAsFallbackBehavior match_origin_as_fallback,
- + bool allow_inaccessible_parents) {
- + auto should_consider_origin = [document_url, match_origin_as_fallback]() {
- + switch (match_origin_as_fallback) {
- + case MatchOriginAsFallbackBehavior::kNever:
- + return false;
- + case MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree:
- + return document_url.SchemeIs(url::kAboutScheme);
- + case MatchOriginAsFallbackBehavior::kAlways:
- + // TODO(devlin): Add more schemes here - blob, filesystem, etc.
- + return document_url.SchemeIs(url::kAboutScheme) ||
- + document_url.SchemeIs(url::kDataScheme);
- + }
- +
- + NOTREACHED();
- + };
- +
- + // If we don't need to consider the origin, we're done.
- + if (!should_consider_origin())
- + return document_url;
- +
- + // Get the "security origin" for the frame. For about: frames, this is the
- + // origin of that of the controlling frame - e.g., an about:blank frame on
- + // https://example.com will have the security origin of https://example.com.
- + // Other frames, like data: frames, will have an opaque origin. For these,
- + // we can get the precursor origin.
- + const blink::WebSecurityOrigin web_frame_origin = frame->GetSecurityOrigin();
- + const url::Origin frame_origin = web_frame_origin;
- + const url::SchemeHostPort& tuple_or_precursor_tuple =
- + frame_origin.GetTupleOrPrecursorTupleIfOpaque();
- +
- + // When there's no valid tuple (which can happen in the case of e.g. a
- + // browser-initiated navigation to an opaque URL), there's no origin to
- + // fallback to. Bail.
- + if (!tuple_or_precursor_tuple.IsValid())
- + return document_url;
- +
- + const url::Origin origin_or_precursor_origin =
- + url::Origin::Create(tuple_or_precursor_tuple.GetURL());
- +
- + if (!allow_inaccessible_parents &&
- + !web_frame_origin.CanAccess(
- + blink::WebSecurityOrigin(origin_or_precursor_origin))) {
- + // The frame can't access its precursor. Bail.
- + return document_url;
- + }
- +
- + // Looks like the initiator origin is an appropriate fallback!
- +
- + if (match_origin_as_fallback == MatchOriginAsFallbackBehavior::kAlways) {
- + // The easy case! We use the origin directly. We're done.
- + return origin_or_precursor_origin.GetURL();
- + }
- +
- + DCHECK_EQ(MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree,
- + match_origin_as_fallback);
- +
- + // Unfortunately, in this case, we have to climb the frame tree. This is for
- + // match patterns that are associated with paths as well, not just origins.
- + // For instance, if an extension wants to run on google.com/maps/* with
- + // match_about_blank true, then it should run on about:-scheme frames created
- + // by google.com/maps, but not about:-scheme frames created by google.com
- + // (which is what the precursor tuple origin would be).
- +
- + // Traverse the frame/window hierarchy to find the closest non-about:-page
- + // with the same origin as the precursor and return its URL.
- + // Note: This can return the incorrect result, e.g. if a parent frame
- + // navigates a grandchild frame.
- + blink::WebFrame* parent = frame;
- + GURL parent_url;
- + blink::WebDocument parent_document;
- + base::flat_set<blink::WebFrame*> already_visited_frames;
- + do {
- + already_visited_frames.insert(parent);
- + if (parent->Parent())
- + parent = parent->Parent();
- + else
- + parent = parent->Opener();
- +
- + // Avoid an infinite loop - see https://crbug.com/568432 and
- + // https://crbug.com/883526.
- + if (base::Contains(already_visited_frames, parent))
- + return document_url;
- +
- + parent_document = parent && parent->IsWebLocalFrame()
- + ? parent->ToWebLocalFrame()->GetDocument()
- + : blink::WebDocument();
- +
- + // We reached the end of the ancestral chain without finding a valid parent,
- + // or found a remote web frame (in which case, it's a different origin).
- + // Bail and use the original URL.
- + if (parent_document.IsNull())
- + return document_url;
- +
- + url::SchemeHostPort parent_tuple_or_precursor_tuple =
- + url::Origin(parent->GetSecurityOrigin())
- + .GetTupleOrPrecursorTupleIfOpaque();
- + if (!parent_tuple_or_precursor_tuple.IsValid() ||
- + parent_tuple_or_precursor_tuple != tuple_or_precursor_tuple) {
- + // The parent has a different tuple origin than frame; this could happen
- + // in edge cases where a parent navigates an iframe or popup of a child
- + // frame at a different origin. [1] In this case, bail, since we can't
- + // find a full URL (i.e., one including the path) with the same security
- + // origin to use for the frame in question.
- + // [1] Consider a frame tree like:
- + // <html> <!--example.com-->
- + // <iframe id="a" src="a.com">
- + // <iframe id="b" src="b.com"></iframe>
- + // </iframe>
- + // </html>
- + // Frame "a" is cross-origin from the top-level frame, and so the
- + // example.com top-level frame can't directly access frame "b". However,
- + // it can navigate it through
- + // window.frames[0].frames[0].location.href = 'about:blank';
- + // In that case, the precursor origin tuple origin of frame "b" would be
- + // example.com, but the parent tuple origin is a.com.
- + // Note that usually, this would have bailed earlier with a remote frame,
- + // but it may not if we're at the process limit.
- + return document_url;
- + }
- +
- + parent_url = GURL(parent_document.Url());
- + } while (parent_url.SchemeIs(url::kAboutScheme));
- +
- + DCHECK(!parent_url.is_empty());
- + DCHECK(!parent_document.IsNull());
- +
- + // We should know that the frame can access the parent document (unless we
- + // explicitly allow it not to), since it has the same tuple origin as the
- + // frame, and we checked the frame access above.
- + DCHECK(allow_inaccessible_parents ||
- + web_frame_origin.CanAccess(parent_document.GetSecurityOrigin()));
- + return parent_url;
- +}
- +
- +using FrameToDocumentLoader =
- + base::flat_map<blink::WebLocalFrame*, blink::WebDocumentLoader*>;
- +
- +FrameToDocumentLoader& FrameDocumentLoaderMap() {
- + static base::NoDestructor<FrameToDocumentLoader> map;
- + return *map;
- +}
- +
- +blink::WebDocumentLoader* CurrentDocumentLoader(
- + const blink::WebLocalFrame* frame) {
- + auto& map = FrameDocumentLoaderMap();
- + auto it = map.find(frame);
- + return it == map.end() ? frame->GetDocumentLoader() : it->second;
- +}
- +
- +} // namespace
- +
- +// static
- +GURL ScriptContext::GetDocumentLoaderURLForFrame(
- + const blink::WebLocalFrame* frame) {
- + // Normally we would use frame->document().url() to determine the document's
- + // URL, but to decide whether to inject a content script, we use the URL from
- + // the data source. This "quirk" helps prevents content scripts from
- + // inadvertently adding DOM elements to the compose iframe in Gmail because
- + // the compose iframe's dataSource URL is about:blank, but the document URL
- + // changes to match the parent document after Gmail document.writes into
- + // it to create the editor.
- + // http://code.google.com/p/chromium/issues/detail?id=86742
- + blink::WebDocumentLoader* document_loader = CurrentDocumentLoader(frame);
- + return document_loader ? GURL(document_loader->GetUrl()) : GURL();
- +}
- +
- +// static
- +GURL ScriptContext::GetEffectiveDocumentURLForInjection(
- + blink::WebLocalFrame* frame,
- + const GURL& document_url,
- + MatchOriginAsFallbackBehavior match_origin_as_fallback) {
- + // We explicitly allow inaccessible parents here. Extensions should still be
- + // able to inject into a sandboxed iframe if it has access to the embedding
- + // origin.
- + constexpr bool allow_inaccessible_parents = true;
- + return GetEffectiveDocumentURL(frame, document_url, match_origin_as_fallback,
- + allow_inaccessible_parents);
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/script_context.h b/components/user_scripts/renderer/script_context.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_context.h
- @@ -0,0 +1,69 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
- +#define USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
- +
- +#include <memory>
- +#include <string>
- +#include <utility>
- +#include <vector>
- +
- +#include "base/callback.h"
- +#include "base/compiler_specific.h"
- +#include "base/threading/thread_checker.h"
- +#include "base/unguessable_token.h"
- +#include "../common/script_constants.h"
- +#include "script_injection_callback.h"
- +#include "url/gurl.h"
- +#include "v8/include/v8.h"
- +
- +namespace blink {
- +class WebDocumentLoader;
- +class WebLocalFrame;
- +}
- +
- +namespace content {
- +class RenderFrame;
- +}
- +
- +namespace user_scripts {
- +
- +// Extensions wrapper for a v8::Context.
- +//
- +// v8::Contexts can be constructed on any thread, and must only be accessed or
- +// destroyed that thread.
- +//
- +// Note that ScriptContexts bound to worker threads will not have the full
- +// functionality as those bound to the main RenderThread.
- +class ScriptContext {
- + public:
- + ScriptContext(const ScriptContext&) = delete;
- + ScriptContext& operator=(const ScriptContext&) = delete;
- + // TODO(devlin): Move all these Get*URL*() methods out of here? While they are
- + // vaguely ScriptContext related, there's enough here that they probably
- + // warrant another class or utility file.
- +
- + // Utility to get the URL we will match against for a frame. If the frame has
- + // committed, this is the commited URL. Otherwise it is the provisional URL.
- + // The returned URL may be invalid.
- + static GURL GetDocumentLoaderURLForFrame(const blink::WebLocalFrame* frame);
- +
- + // Used to determine the "effective" URL for extension script injection.
- + // If |document_url| is an about: or data: URL, returns the URL of the first
- + // frame without an about: or data: URL that matches the initiator origin.
- + // This may not be the immediate parent. Returns |document_url| if it is not
- + // an about: or data: URL, if |match_origin_as_fallback| is set to not match,
- + // or if a suitable parent cannot be found.
- + // Considers parent contexts that cannot be accessed (as is the case for
- + // sandboxed frames).
- + static GURL GetEffectiveDocumentURLForInjection(
- + blink::WebLocalFrame* frame,
- + const GURL& document_url,
- + MatchOriginAsFallbackBehavior match_origin_as_fallback);
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
- diff --git a/components/user_scripts/renderer/script_injection.cc b/components/user_scripts/renderer/script_injection.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection.cc
- @@ -0,0 +1,343 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "script_injection.h"
- +
- +#include <map>
- +#include <utility>
- +
- +#include "base/bind.h"
- +#include "base/feature_list.h"
- +#include "base/lazy_instance.h"
- +#include "base/metrics/histogram_macros.h"
- +#include "base/timer/elapsed_timer.h"
- +#include "base/values.h"
- +#include "base/logging.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_frame_observer.h"
- +#include "content/public/renderer/v8_value_converter.h"
- +#include "../common/host_id.h"
- +#include "script_injection_callback.h"
- +#include "scripts_run_info.h"
- +#include "third_party/blink/public/platform/web_isolated_world_info.h"
- +#include "third_party/blink/public/platform/web_security_origin.h"
- +#include "third_party/blink/public/platform/web_string.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "third_party/blink/public/web/web_script_source.h"
- +#include "url/gurl.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +using IsolatedWorldMap = std::map<std::string, int>;
- +base::LazyInstance<IsolatedWorldMap>::DestructorAtExit g_isolated_worlds =
- + LAZY_INSTANCE_INITIALIZER;
- +
- +const int64_t kInvalidRequestId = -1;
- +
- +// Gets the isolated world ID to use for the given |injection_host|. If no
- +// isolated world has been created for that |injection_host| one will be created
- +// and initialized.
- +int GetIsolatedWorldIdForInstance(const InjectionHost* injection_host) {
- + static int g_next_isolated_world_id = 1; // Embedder isolated worlds can use IDs in [1, 1<<29).
- +
- + IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
- +
- + int id = 0;
- + const std::string& key = injection_host->id().id();
- + auto iter = isolated_worlds.find(key);
- + if (iter != isolated_worlds.end()) {
- + id = iter->second;
- + } else {
- + id = g_next_isolated_world_id++;
- + // This map will tend to pile up over time, but realistically, you're never
- + // going to have enough injection hosts for it to matter.
- + isolated_worlds[key] = id;
- + }
- +
- + blink::WebIsolatedWorldInfo info;
- + info.security_origin =
- + blink::WebSecurityOrigin::Create(injection_host->url());
- + info.human_readable_name = blink::WebString::FromUTF8(injection_host->name());
- + info.stable_id = blink::WebString::FromUTF8(key);
- +
- + const std::string* csp = injection_host->GetContentSecurityPolicy();
- + if (csp)
- + info.content_security_policy = blink::WebString::FromUTF8(*csp);
- +
- + // Even though there may be an existing world for this |injection_host|'s key,
- + // the properties may have changed (e.g. due to an extension update).
- + // Overwrite any existing entries.
- + blink::SetIsolatedWorldInfo(id, info);
- +
- + return id;
- +}
- +
- +// This class manages its own lifetime.
- +class TimedScriptInjectionCallback : public ScriptInjectionCallback {
- + public:
- + TimedScriptInjectionCallback(base::WeakPtr<ScriptInjection> injection)
- + : ScriptInjectionCallback(
- + base::BindOnce(&TimedScriptInjectionCallback::OnCompleted,
- + base::Unretained(this))),
- + injection_(injection) {}
- + ~TimedScriptInjectionCallback() override {}
- +
- + void OnCompleted(const std::vector<v8::Local<v8::Value>>& result) {
- + if (injection_) {
- + base::TimeTicks timestamp(base::TimeTicks::Now());
- + absl::optional<base::TimeDelta> elapsed;
- + // If the script will never execute (such as if the context is destroyed),
- + // willExecute() will not be called, but OnCompleted() will. Only log a
- + // time for execution if the script, in fact, executed.
- + if (!start_time_.is_null())
- + elapsed = timestamp - start_time_;
- + injection_->OnJsInjectionCompleted(result, elapsed);
- + }
- + }
- +
- + void WillExecute() override {
- + start_time_ = base::TimeTicks::Now();
- + }
- +
- + private:
- + base::WeakPtr<ScriptInjection> injection_;
- + base::TimeTicks start_time_;
- +};
- +
- +} // namespace
- +
- +// Watches for the deletion of a RenderFrame, after which is_valid will return
- +// false.
- +class ScriptInjection::FrameWatcher : public content::RenderFrameObserver {
- + public:
- + FrameWatcher(const FrameWatcher&) = delete;
- + FrameWatcher& operator=(const FrameWatcher&) = delete;
- + FrameWatcher(content::RenderFrame* render_frame,
- + ScriptInjection* injection)
- + : content::RenderFrameObserver(render_frame),
- + injection_(injection) {}
- + ~FrameWatcher() override {}
- +
- + private:
- + void WillDetach() override { injection_->invalidate_render_frame(); }
- + void OnDestruct() override { injection_->invalidate_render_frame(); }
- +
- + ScriptInjection* injection_;
- +};
- +
- +// static
- +std::string ScriptInjection::GetHostIdForIsolatedWorld(int isolated_world_id) {
- + const IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
- +
- + for (const auto& iter : isolated_worlds) {
- + if (iter.second == isolated_world_id)
- + return iter.first;
- + }
- + return std::string();
- +}
- +
- +// static
- +void ScriptInjection::RemoveIsolatedWorld(const std::string& host_id) {
- + g_isolated_worlds.Get().erase(host_id);
- +}
- +
- +ScriptInjection::ScriptInjection(
- + std::unique_ptr<ScriptInjector> injector,
- + content::RenderFrame* render_frame,
- + std::unique_ptr<const InjectionHost> injection_host,
- + UserScript::RunLocation run_location,
- + bool log_activity)
- + : injector_(std::move(injector)),
- + render_frame_(render_frame),
- + injection_host_(std::move(injection_host)),
- + run_location_(run_location),
- + request_id_(kInvalidRequestId),
- + complete_(false),
- + did_inject_js_(false),
- + log_activity_(log_activity),
- + frame_watcher_(new FrameWatcher(render_frame, this)) {
- + CHECK(injection_host_.get());
- +}
- +
- +ScriptInjection::~ScriptInjection() {
- + if (!complete_)
- + NotifyWillNotInject(ScriptInjector::WONT_INJECT);
- +}
- +
- +ScriptInjection::InjectionResult ScriptInjection::TryToInject(
- + UserScript::RunLocation current_location,
- + ScriptsRunInfo* scripts_run_info,
- + CompletionCallback async_completion_callback) {
- + if (current_location < run_location_)
- + return INJECTION_WAITING; // Wait for the right location.
- +
- + if (request_id_ != kInvalidRequestId) {
- + // We're waiting for permission right now, try again later.
- + return INJECTION_WAITING;
- + }
- +
- + if (!injection_host_) {
- + NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
- + return INJECTION_FINISHED; // We're done.
- + }
- +
- + InjectionResult result = Inject(scripts_run_info);
- + // If the injection is blocked, we need to set the manager so we can
- + // notify it upon completion.
- + if (result == INJECTION_BLOCKED)
- + async_completion_callback_ = std::move(async_completion_callback);
- + return result;
- +}
- +
- +ScriptInjection::InjectionResult ScriptInjection::OnPermissionGranted(
- + ScriptsRunInfo* scripts_run_info) {
- + if (!injection_host_) {
- + NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
- + return INJECTION_FINISHED;
- + }
- +
- + return Inject(scripts_run_info);
- +}
- +
- +void ScriptInjection::OnHostRemoved() {
- + injection_host_.reset(nullptr);
- +}
- +
- +void ScriptInjection::NotifyWillNotInject(
- + ScriptInjector::InjectFailureReason reason) {
- + complete_ = true;
- + injector_->OnWillNotInject(reason, render_frame_);
- +}
- +
- +ScriptInjection::InjectionResult ScriptInjection::Inject(
- + ScriptsRunInfo* scripts_run_info) {
- + DCHECK(injection_host_);
- + //DCHECK(scripts_run_info);
- + DCHECK(!complete_);
- + bool should_inject_js = injector_->ShouldInjectJs(
- + run_location_, scripts_run_info->executing_scripts[host_id().id()]);
- + bool should_inject_css = injector_->ShouldInjectCss(
- + run_location_, scripts_run_info->injected_stylesheets[host_id().id()]);
- +
- + // This can happen if the extension specified a script to
- + // be run in multiple rules, and the script has already run.
- + // See crbug.com/631247.
- + if (!should_inject_js && !should_inject_css) {
- + return INJECTION_FINISHED;
- + }
- +
- + if (should_inject_js)
- + InjectJs(&(scripts_run_info->executing_scripts[host_id().id()]),
- + &(scripts_run_info->num_js));
- + if (should_inject_css)
- + InjectCss(&(scripts_run_info->injected_stylesheets[host_id().id()]),
- + &(scripts_run_info->num_css));
- +
- + complete_ = did_inject_js_ || !should_inject_js;
- +
- + if (complete_) {
- + injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
- + render_frame_);
- + } else {
- + ++scripts_run_info->num_blocking_js;
- + }
- +
- + return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
- +}
- +
- +void ScriptInjection::InjectJs(std::set<std::string>* executing_scripts,
- + size_t* num_injected_js_scripts) {
- + DCHECK(!did_inject_js_);
- + std::vector<blink::WebScriptSource> sources = injector_->GetJsSources(
- + run_location_, executing_scripts, num_injected_js_scripts);
- + DCHECK(!sources.empty());
- + int world_id = GetIsolatedWorldIdForInstance(injection_host_.get());
- + bool is_user_gesture = injector_->IsUserGesture();
- +
- + std::unique_ptr<blink::WebScriptExecutionCallback> callback(
- + new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr()));
- +
- + base::ElapsedTimer exec_timer;
- +
- + // For content scripts executing during page load, we run them asynchronously
- + // in order to reduce UI jank experienced by the user. (We don't do this for
- + // DOCUMENT_START scripts, because there's no UI to jank until after those
- + // run, so we run them as soon as we can.)
- + // Note: We could potentially also run deferred and browser-driven scripts
- + // asynchronously; however, these are rare enough that there probably isn't
- + // UI jank. If this changes, we can update this.
- + bool should_execute_asynchronously =
- + injector_->script_type() == UserScript::CONTENT_SCRIPT &&
- + (run_location_ == UserScript::DOCUMENT_END ||
- + run_location_ == UserScript::DOCUMENT_IDLE);
- + blink::WebLocalFrame::ScriptExecutionType execution_option =
- + should_execute_asynchronously
- + ? blink::WebLocalFrame::kAsynchronousBlockingOnload
- + : blink::WebLocalFrame::kSynchronous;
- +
- + render_frame_->GetWebFrame()->RequestExecuteScript(
- + world_id, sources, is_user_gesture,
- + execution_option, callback.release(),
- + blink::BackForwardCacheAware::kPossiblyDisallow,
- + blink::WebLocalFrame::PromiseBehavior::kDontWait);
- +}
- +
- +void ScriptInjection::OnJsInjectionCompleted(
- + const std::vector<v8::Local<v8::Value>>& results,
- + absl::optional<base::TimeDelta> elapsed) {
- + DCHECK(!did_inject_js_);
- +
- + bool expects_results = injector_->ExpectsResults();
- + if (expects_results) {
- + if (!results.empty() && !results[0].IsEmpty()) {
- + // Right now, we only support returning single results (per frame).
- + // It's safe to always use the main world context when converting
- + // here. V8ValueConverterImpl shouldn't actually care about the
- + // context scope, and it switches to v8::Object's creation context
- + // when encountered.
- + v8::Local<v8::Context> context =
- + render_frame_->GetWebFrame()->MainWorldScriptContext();
- + execution_result_ =
- + content::V8ValueConverter::Create()->FromV8Value(results[0], context);
- + }
- + if (!execution_result_.get())
- + execution_result_ = std::make_unique<base::Value>();
- + }
- + did_inject_js_ = true;
- +
- + // If |async_completion_callback_| is set, it means the script finished
- + // asynchronously, and we should run it.
- + if (!async_completion_callback_.is_null()) {
- + complete_ = true;
- + injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
- + render_frame_);
- + // Warning: this object can be destroyed after this line!
- + std::move(async_completion_callback_).Run(this);
- + }
- +}
- +
- +void ScriptInjection::InjectCss(std::set<std::string>* injected_stylesheets,
- + size_t* num_injected_stylesheets) {
- + std::vector<blink::WebString> css_sources = injector_->GetCssSources(
- + run_location_, injected_stylesheets, num_injected_stylesheets);
- + blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
- + // Default CSS origin is "author", but can be overridden to "user" by scripts.
- + absl::optional<CSSOrigin> css_origin = injector_->GetCssOrigin();
- + blink::WebDocument::CSSOrigin blink_css_origin =
- + css_origin && *css_origin == CSS_ORIGIN_USER
- + ? blink::WebDocument::kUserOrigin
- + : blink::WebDocument::kAuthorOrigin;
- + blink::WebStyleSheetKey style_sheet_key;
- + if (const absl::optional<std::string>& injection_key =
- + injector_->GetInjectionKey())
- + style_sheet_key = blink::WebString::FromASCII(*injection_key);
- + for (const blink::WebString& css : css_sources)
- + web_frame->GetDocument().InsertStyleSheet(css, &style_sheet_key,
- + blink_css_origin);
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/script_injection.h b/components/user_scripts/renderer/script_injection.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection.h
- @@ -0,0 +1,154 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
- +#define USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
- +
- +#include <stdint.h>
- +
- +#include <memory>
- +#include <vector>
- +
- +#include "base/callback.h"
- +#include "base/memory/weak_ptr.h"
- +#include "../common/user_script.h"
- +#include "injection_host.h"
- +#include "script_injector.h"
- +
- +struct HostID;
- +
- +namespace content {
- +class RenderFrame;
- +}
- +
- +namespace v8 {
- +class Value;
- +template <class T> class Local;
- +}
- +
- +namespace user_scripts {
- +struct ScriptsRunInfo;
- +
- +// A script wrapper which is aware of whether or not it is allowed to execute,
- +// and contains the implementation to do so.
- +class ScriptInjection {
- + public:
- + ScriptInjection(const ScriptInjection&) = delete;
- + ScriptInjection& operator=(const ScriptInjection&) = delete;
- + enum InjectionResult {
- + INJECTION_FINISHED,
- + INJECTION_BLOCKED,
- + INJECTION_WAITING
- + };
- +
- + using CompletionCallback = base::OnceCallback<void(ScriptInjection*)>;
- +
- + // Return the id of the injection host associated with the given world.
- + static std::string GetHostIdForIsolatedWorld(int world_id);
- +
- + // Remove the isolated world associated with the given injection host.
- + static void RemoveIsolatedWorld(const std::string& host_id);
- +
- + ScriptInjection(std::unique_ptr<ScriptInjector> injector,
- + content::RenderFrame* render_frame,
- + std::unique_ptr<const InjectionHost> injection_host,
- + UserScript::RunLocation run_location,
- + bool log_activity);
- + ~ScriptInjection();
- +
- + // Try to inject the script at the |current_location|. This returns
- + // INJECTION_FINISHED if injection has injected or will never inject, returns
- + // INJECTION_BLOCKED if injection is running asynchronously and has not
- + // finished yet, returns INJECTION_WAITING if injections is delayed (either
- + // for permission purposes or because |current_location| is not the designated
- + // |run_location_|).
- + // If INJECTION_BLOCKED is returned, |async_completion_callback| will be
- + // called upon completion.
- + InjectionResult TryToInject(
- + UserScript::RunLocation current_location,
- + ScriptsRunInfo* scripts_run_info,
- + CompletionCallback async_completion_callback);
- +
- + // Called when permission for the given injection has been granted.
- + // Returns INJECTION_FINISHED if injection has injected or will never inject,
- + // returns INJECTION_BLOCKED if injection is ran asynchronously.
- + InjectionResult OnPermissionGranted(ScriptsRunInfo* scripts_run_info);
- +
- + // Resets the pointer of the injection host when the host is gone.
- + void OnHostRemoved();
- +
- + void invalidate_render_frame() { render_frame_ = nullptr; }
- +
- + // Accessors.
- + content::RenderFrame* render_frame() const { return render_frame_; }
- + const HostID& host_id() const { return injection_host_->id(); }
- + int64_t request_id() const { return request_id_; }
- +
- + // Called when JS injection for the given frame has been completed or
- + // cancelled.
- + void OnJsInjectionCompleted(const std::vector<v8::Local<v8::Value>>& results,
- + absl::optional<base::TimeDelta> elapsed);
- +
- + private:
- + class FrameWatcher;
- +
- + // Sends a message to the browser to request permission to inject.
- + void RequestPermissionFromBrowser();
- +
- + // Injects the script. Returns INJECTION_FINISHED if injection has finished,
- + // otherwise INJECTION_BLOCKED.
- + InjectionResult Inject(ScriptsRunInfo* scripts_run_info);
- +
- + // Inject any JS scripts into the frame for the injection.
- + void InjectJs(std::set<std::string>* executing_scripts,
- + size_t* num_injected_js_scripts);
- +
- + // Inject any CSS source into the frame for the injection.
- + void InjectCss(std::set<std::string>* injected_stylesheets,
- + size_t* num_injected_stylesheets);
- +
- + // Notify that we will not inject, and mark it as acknowledged.
- + void NotifyWillNotInject(ScriptInjector::InjectFailureReason reason);
- +
- + // The injector for this injection.
- + std::unique_ptr<ScriptInjector> injector_;
- +
- + // The RenderFrame into which this should inject the script.
- + content::RenderFrame* render_frame_;
- +
- + // The associated injection host.
- + std::unique_ptr<const InjectionHost> injection_host_;
- +
- + // The location in the document load at which we inject the script.
- + UserScript::RunLocation run_location_;
- +
- + // This injection's request id. This will be -1 unless the injection is
- + // currently waiting on permission.
- + int64_t request_id_;
- +
- + // Whether or not the injection is complete, either via injecting the script
- + // or because it will never complete.
- + bool complete_;
- +
- + // Whether or not the injection successfully injected JS.
- + bool did_inject_js_;
- +
- + // Whether or not we should log dom activity for this injection.
- + bool log_activity_;
- +
- + // Results storage.
- + std::unique_ptr<base::Value> execution_result_;
- +
- + // The callback to run upon completing asynchronously.
- + CompletionCallback async_completion_callback_;
- +
- + // A helper class to hold the render frame and watch for its deletion.
- + std::unique_ptr<FrameWatcher> frame_watcher_;
- +
- + base::WeakPtrFactory<ScriptInjection> weak_ptr_factory_{this};
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
- diff --git a/components/user_scripts/renderer/script_injection_callback.cc b/components/user_scripts/renderer/script_injection_callback.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection_callback.cc
- @@ -0,0 +1,25 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "script_injection_callback.h"
- +
- +#include "third_party/blink/public/platform/web_vector.h"
- +
- +namespace user_scripts {
- +
- +ScriptInjectionCallback::ScriptInjectionCallback(
- + CompleteCallback injection_completed_callback)
- + : injection_completed_callback_(std::move(injection_completed_callback)) {}
- +
- +ScriptInjectionCallback::~ScriptInjectionCallback() {
- +}
- +
- +void ScriptInjectionCallback::Completed(
- + const blink::WebVector<v8::Local<v8::Value>>& result) {
- + std::vector<v8::Local<v8::Value>> stl_result(result.begin(), result.end());
- + std::move(injection_completed_callback_).Run(stl_result);
- + delete this;
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/script_injection_callback.h b/components/user_scripts/renderer/script_injection_callback.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection_callback.h
- @@ -0,0 +1,38 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
- +#define USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
- +
- +#include <vector>
- +
- +#include "base/callback.h"
- +#include "third_party/blink/public/web/web_script_execution_callback.h"
- +#include "v8/include/v8.h"
- +
- +namespace user_scripts {
- +
- +// A wrapper around a callback to notify a script injection when injection
- +// completes.
- +// This class manages its own lifetime.
- +class ScriptInjectionCallback : public blink::WebScriptExecutionCallback {
- + public:
- + ScriptInjectionCallback(const ScriptInjectionCallback&) = delete;
- + ScriptInjectionCallback& operator=(const ScriptInjectionCallback&) = delete;
- + using CompleteCallback =
- + base::OnceCallback<void(const std::vector<v8::Local<v8::Value>>& result)>;
- +
- + explicit ScriptInjectionCallback(
- + CompleteCallback injection_completed_callback);
- + ~ScriptInjectionCallback() override;
- +
- + void Completed(const blink::WebVector<v8::Local<v8::Value>>& result) override;
- +
- + private:
- + CompleteCallback injection_completed_callback_;
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
- diff --git a/components/user_scripts/renderer/script_injection_manager.cc b/components/user_scripts/renderer/script_injection_manager.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection_manager.cc
- @@ -0,0 +1,417 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "script_injection_manager.h"
- +
- +#include <memory>
- +#include <utility>
- +
- +#include "base/auto_reset.h"
- +#include "base/bind.h"
- +#include "base/feature_list.h"
- +#include "base/memory/weak_ptr.h"
- +#include "base/threading/thread_task_runner_handle.h"
- +#include "base/values.h"
- +#include "base/logging.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_frame_observer.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "extension_frame_helper.h"
- +#include "../common/host_id.h"
- +#include "script_injection.h"
- +#include "scripts_run_info.h"
- +#include "web_ui_injection_host.h"
- +#include "ipc/ipc_message_macros.h"
- +#include "third_party/blink/public/platform/web_url_error.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_frame.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "third_party/blink/public/web/web_view.h"
- +#include "url/gurl.h"
- +#include "../common/user_scripts_features.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +// The length of time to wait after the DOM is complete to try and run user
- +// scripts.
- +const int kScriptIdleTimeoutInMs = 200;
- +
- +// Returns the RunLocation that follows |run_location|.
- +UserScript::RunLocation NextRunLocation(UserScript::RunLocation run_location) {
- + switch (run_location) {
- + case UserScript::DOCUMENT_START:
- + return UserScript::DOCUMENT_END;
- + case UserScript::DOCUMENT_END:
- + return UserScript::DOCUMENT_IDLE;
- + case UserScript::DOCUMENT_IDLE:
- + return UserScript::RUN_LOCATION_LAST;
- + case UserScript::UNDEFINED:
- + case UserScript::RUN_DEFERRED:
- + case UserScript::BROWSER_DRIVEN:
- + case UserScript::RUN_LOCATION_LAST:
- + break;
- + }
- + NOTREACHED();
- + return UserScript::RUN_LOCATION_LAST;
- +}
- +
- +} // namespace
- +
- +class ScriptInjectionManager::RFOHelper : public content::RenderFrameObserver {
- + public:
- + RFOHelper(content::RenderFrame* render_frame,
- + ScriptInjectionManager* manager);
- + ~RFOHelper() override;
- +
- + // commit @9f2aac4
- + void Initialize();
- +
- + private:
- + // RenderFrameObserver implementation.
- + void DidCreateNewDocument() override;
- + void DidCreateDocumentElement() override;
- + void DidFailProvisionalLoad() override;
- + void DidDispatchDOMContentLoadedEvent() override;
- + void WillDetach() override;
- + void OnDestruct() override;
- + void OnStop() override;
- +
- + // Tells the ScriptInjectionManager to run tasks associated with
- + // document_idle.
- + void RunIdle();
- +
- + void StartInjectScripts(UserScript::RunLocation run_location);
- +
- + // Indicate that the frame is no longer valid because it is starting
- + // a new load or closing.
- + void InvalidateAndResetFrame(bool force_reset);
- +
- + // The owning ScriptInjectionManager.
- + ScriptInjectionManager* manager_;
- +
- + bool should_run_idle_ = true; // commit @9f2aac4
- +
- + base::WeakPtrFactory<RFOHelper> weak_factory_{this};
- +};
- +
- +ScriptInjectionManager::RFOHelper::RFOHelper(content::RenderFrame* render_frame,
- + ScriptInjectionManager* manager)
- + : content::RenderFrameObserver(render_frame),
- + manager_(manager),
- + should_run_idle_(true) {}
- +
- +ScriptInjectionManager::RFOHelper::~RFOHelper() {
- +}
- +
- +
- +void ScriptInjectionManager::RFOHelper::Initialize() {
- + // Set up for the initial empty document, for which the Document created
- + // events do not happen as it's already present.
- + DidCreateNewDocument();
- + // The initial empty document for a main frame may have scripts attached to it
- + // but we do not want to invalidate the frame and lose them when the next
- + // document loads. For example the IncognitoApiTest.IncognitoSplitMode test
- + // does `chrome.tabs.create()` with a script to be run, which is added to the
- + // frame before it navigates, so it needs to be preserved. However scripts in
- + // child frames are expected to be run inside the initial empty document. For
- + // example the ExecuteScriptApiTest.FrameWithHttp204 test creates a child
- + // frame at about:blank and expects to run injected scripts inside it.
- + // This is all quite inconsistent however tests both depend on us queuing and
- + // not queueing the DOCUMENT_START events in the initial empty document.
- + if (!render_frame()->IsMainFrame()) {
- + DidCreateDocumentElement();
- + }
- +}
- +
- +void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() {
- + // A new document is going to be shown, so invalidate the old document state.
- + // Don't force-reset the frame, because it is possible that a script injection
- + // was scheduled before the page was loaded, e.g. by navigating to a
- + // javascript: URL before the page has loaded.
- + constexpr bool kForceReset = false;
- + InvalidateAndResetFrame(kForceReset);
- +}
- +
- +void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: DidCreateDocumentElement -> DOCUMENT_START";
- +
- + ExtensionFrameHelper::Get(render_frame())
- + ->ScheduleAtDocumentStart(
- + base::BindOnce(&ScriptInjectionManager::RFOHelper::StartInjectScripts,
- + weak_factory_.GetWeakPtr(), UserScript::DOCUMENT_START));
- +}
- +
- +void ScriptInjectionManager::RFOHelper::DidFailProvisionalLoad() {
- + auto it = manager_->frame_statuses_.find(render_frame());
- + if (it != manager_->frame_statuses_.end() &&
- + it->second == UserScript::DOCUMENT_START) {
- + // Since the provisional load failed, the frame stays at its previous loaded
- + // state and origin (or the parent's origin for new/about:blank frames).
- + // Reset the frame to DOCUMENT_IDLE in order to reflect that the frame is
- + // done loading, and avoid any deadlock in the system.
- + //
- + // We skip injection of DOCUMENT_END and DOCUMENT_IDLE scripts, because the
- + // injections closely follow the DOMContentLoaded (and onload) events, which
- + // are not triggered after a failed provisional load.
- + // This assumption is verified in the checkDOMContentLoadedEvent subtest of
- + // ExecuteScriptApiTest.FrameWithHttp204 (browser_tests).
- + constexpr bool kForceReset = true;
- + InvalidateAndResetFrame(kForceReset);
- + should_run_idle_ = false;
- + manager_->frame_statuses_[render_frame()] = UserScript::DOCUMENT_IDLE;
- + }
- +}
- +
- +void ScriptInjectionManager::RFOHelper::DidDispatchDOMContentLoadedEvent() {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: DidDispatchDOMContentLoadedEvent -> DOCUMENT_END";
- +
- + DCHECK(content::RenderThread::Get());
- + ExtensionFrameHelper::Get(render_frame())
- + ->ScheduleAtDocumentEnd(
- + base::BindOnce(&ScriptInjectionManager::RFOHelper::StartInjectScripts,
- + weak_factory_.GetWeakPtr(), UserScript::DOCUMENT_END));
- +
- + // We try to run idle in two places: a delayed task here and in response to
- + // ContentRendererClient::RunScriptsAtDocumentIdle(). DidDispatchDOMContentLoadedEvent()
- + // corresponds to completing the document's load, whereas
- + // RunScriptsAtDocumentIdle() corresponds to completing the document and all
- + // subresources' load (but before the window.onload event). We don't want to
- + // hold up script injection for a particularly slow subresource, so we set a
- + // delayed task from here - but if we finish everything before that point
- + // (i.e., RunScriptsAtDocumentIdle() is triggered), then there's no reason to
- + // keep waiting.
- + render_frame()
- + ->GetTaskRunner(blink::TaskType::kInternalDefault)
- + ->PostDelayedTask(
- + FROM_HERE,
- + base::BindOnce(&ScriptInjectionManager::RFOHelper::RunIdle,
- + weak_factory_.GetWeakPtr()),
- + base::Milliseconds(kScriptIdleTimeoutInMs));
- +
- + ExtensionFrameHelper::Get(render_frame())
- + ->ScheduleAtDocumentIdle(
- + base::BindOnce(&ScriptInjectionManager::RFOHelper::RunIdle,
- + weak_factory_.GetWeakPtr()));
- +}
- +
- +void ScriptInjectionManager::RFOHelper::WillDetach() {
- + // The frame is closing - invalidate.
- + constexpr bool kForceReset = true;
- + InvalidateAndResetFrame(kForceReset);
- +}
- +
- +void ScriptInjectionManager::RFOHelper::OnDestruct() {
- + manager_->RemoveObserver(this);
- +}
- +
- +void ScriptInjectionManager::RFOHelper::OnStop() {
- + // If the navigation request fails (e.g. 204/205/downloads), notify the
- + // extension to avoid keeping the frame in a START state indefinitely which
- + // leads to deadlocks.
- + DidFailProvisionalLoad();
- +}
- +
- +void ScriptInjectionManager::RFOHelper::RunIdle() {
- + // Only notify the manager if the frame hasn't already had idle run since the
- + // task to RunIdle() was posted.
- + if (should_run_idle_) {
- + should_run_idle_ = false;
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: RunIdle -> DOCUMENT_IDLE";
- + manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE);
- + }
- +}
- +
- +void ScriptInjectionManager::RFOHelper::StartInjectScripts(
- + UserScript::RunLocation run_location) {
- + manager_->StartInjectScripts(render_frame(), run_location);
- +}
- +
- +void ScriptInjectionManager::RFOHelper::InvalidateAndResetFrame(
- + bool force_reset) {
- + // Invalidate any pending idle injections, and reset the frame inject on idle.
- + weak_factory_.InvalidateWeakPtrs();
- + // We reset to inject on idle, because the frame can be reused (in the case of
- + // navigation).
- + should_run_idle_ = true;
- +
- + // Reset the frame if either |force_reset| is true, or if the manager is
- + // keeping track of the state of the frame (in which case we need to clean it
- + // up).
- + if (force_reset || manager_->frame_statuses_.count(render_frame()) != 0)
- + manager_->InvalidateForFrame(render_frame());
- +}
- +
- +ScriptInjectionManager::ScriptInjectionManager(
- + UserScriptSetManager* user_script_set_manager)
- + : user_script_set_manager_(user_script_set_manager),
- + user_script_set_manager_observation_(this) {
- + user_script_set_manager_observation_.Observe(user_script_set_manager_);
- +}
- +
- +ScriptInjectionManager::~ScriptInjectionManager() {
- + for (const auto& injection : pending_injections_)
- + injection->invalidate_render_frame();
- + for (const auto& injection : running_injections_)
- + injection->invalidate_render_frame();
- +}
- +
- +void ScriptInjectionManager::OnRenderFrameCreated(
- + content::RenderFrame* render_frame) {
- + rfo_helpers_.push_back(std::make_unique<RFOHelper>(render_frame, this));
- + rfo_helpers_.back()->Initialize(); // commit @9f2aac4
- +}
- +
- +void ScriptInjectionManager::OnInjectionFinished(
- + ScriptInjection* injection) {
- + auto iter =
- + std::find_if(running_injections_.begin(), running_injections_.end(),
- + [injection](const std::unique_ptr<ScriptInjection>& mode) {
- + return injection == mode.get();
- + });
- + if (iter != running_injections_.end())
- + running_injections_.erase(iter);
- +}
- +
- +void ScriptInjectionManager::OnUserScriptsUpdated(
- + const std::set<HostID>& changed_hosts) {
- + for (auto iter = pending_injections_.begin();
- + iter != pending_injections_.end();) {
- + if (changed_hosts.count((*iter)->host_id()) > 0)
- + iter = pending_injections_.erase(iter);
- + else
- + ++iter;
- + }
- +}
- +
- +void ScriptInjectionManager::RemoveObserver(RFOHelper* helper) {
- + for (auto iter = rfo_helpers_.begin(); iter != rfo_helpers_.end(); ++iter) {
- + if (iter->get() == helper) {
- + rfo_helpers_.erase(iter);
- + break;
- + }
- + }
- +}
- +
- +void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) {
- + // If the frame invalidated is the frame being injected into, we need to
- + // note it.
- + active_injection_frames_.erase(frame);
- +
- + for (auto iter = pending_injections_.begin();
- + iter != pending_injections_.end();) {
- + if ((*iter)->render_frame() == frame)
- + iter = pending_injections_.erase(iter);
- + else
- + ++iter;
- + }
- +
- + frame_statuses_.erase(frame);
- +}
- +
- +void ScriptInjectionManager::StartInjectScripts(
- + content::RenderFrame* frame,
- + UserScript::RunLocation run_location) {
- + auto iter = frame_statuses_.find(frame);
- + // We also don't execute if we detect that the run location is somehow out of
- + // order. This can happen if:
- + // - The first run location reported for the frame isn't DOCUMENT_START, or
- + // - The run location reported doesn't immediately follow the previous
- + // reported run location.
- + // We don't want to run because extensions may have requirements that scripts
- + // running in an earlier run location have run by the time a later script
- + // runs. Better to just not run.
- + // Note that we check run_location > NextRunLocation() in the second clause
- + // (as opposed to !=) because earlier signals (like DidCreateDocumentElement)
- + // can happen multiple times, so we can receive earlier/equal run locations.
- + if ((iter == frame_statuses_.end() &&
- + run_location != UserScript::DOCUMENT_START) ||
- + (iter != frame_statuses_.end() &&
- + run_location > NextRunLocation(iter->second))) {
- + // We also invalidate the frame, because the run order of pending injections
- + // may also be bad.
- + InvalidateForFrame(frame);
- + return;
- + } else if (iter != frame_statuses_.end() && iter->second >= run_location) {
- + // Certain run location signals (like DidCreateDocumentElement) can happen
- + // multiple times. Ignore the subsequent signals.
- + return;
- + }
- +
- + // Otherwise, all is right in the world, and we can get on with the
- + // injections!
- + frame_statuses_[frame] = run_location;
- + InjectScripts(frame, run_location);
- +}
- +
- +void ScriptInjectionManager::InjectScripts(
- + content::RenderFrame* frame,
- + UserScript::RunLocation run_location) {
- + // Find any injections that want to run on the given frame.
- + ScriptInjectionVector frame_injections;
- + for (auto iter = pending_injections_.begin();
- + iter != pending_injections_.end();) {
- + if ((*iter)->render_frame() == frame) {
- + frame_injections.push_back(std::move(*iter));
- + iter = pending_injections_.erase(iter);
- + } else {
- + ++iter;
- + }
- + }
- +
- + // Add any injections for user scripts.
- + int tab_id = ExtensionFrameHelper::Get(frame)->tab_id();
- + user_script_set_manager_->GetAllInjections(&frame_injections, frame, tab_id,
- + run_location);
- +
- + // Note that we are running in |frame|.
- + active_injection_frames_.insert(frame);
- +
- + ScriptsRunInfo scripts_run_info(frame, run_location);
- +
- + for (auto iter = frame_injections.begin(); iter != frame_injections.end();) {
- + // It's possible for thScriptsRunInfoe frame to be invalidated in the course of injection
- + // (if a script removes its own frame, for example). If this happens, abort.
- + if (!active_injection_frames_.count(frame))
- + break;
- + std::unique_ptr<ScriptInjection> injection(std::move(*iter));
- + iter = frame_injections.erase(iter);
- + TryToInject(std::move(injection), run_location, &scripts_run_info);
- + }
- +
- + // We are done running in the frame.
- + active_injection_frames_.erase(frame);
- +
- + scripts_run_info.LogRun(activity_logging_enabled_);
- +}
- +
- +void ScriptInjectionManager::TryToInject(
- + std::unique_ptr<ScriptInjection> injection,
- + UserScript::RunLocation run_location,
- + ScriptsRunInfo* scripts_run_info) {
- + // Try to inject the script. If the injection is waiting (i.e., for
- + // permission), add it to the list of pending injections. If the injection
- + // has blocked, add it to the list of running injections.
- + // The Unretained below is safe because this object owns all the
- + // ScriptInjections, so is guaranteed to outlive them.
- + switch (injection->TryToInject(
- + run_location, scripts_run_info,
- + base::BindOnce(&ScriptInjectionManager::OnInjectionFinished,
- + base::Unretained(this)))) {
- + case ScriptInjection::INJECTION_WAITING:
- + pending_injections_.push_back(std::move(injection));
- + break;
- + case ScriptInjection::INJECTION_BLOCKED:
- + running_injections_.push_back(std::move(injection));
- + break;
- + case ScriptInjection::INJECTION_FINISHED:
- + break;
- + }
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/script_injection_manager.h b/components/user_scripts/renderer/script_injection_manager.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injection_manager.h
- @@ -0,0 +1,101 @@
- +#include <stdint.h>
- +
- +#include <map>
- +#include <set>
- +#include <string>
- +#include <vector>
- +
- +#include "base/callback.h"
- +#include "base/scoped_observation.h"
- +#include "../common/user_script.h"
- +#include "script_injection.h"
- +#include "user_script_set_manager.h"
- +
- +namespace user_scripts {
- +
- +// The ScriptInjectionManager manages extensions injecting scripts into frames
- +// via both content/user scripts and tabs.executeScript(). It is responsible for
- +// maintaining any pending injections awaiting permission or the appropriate
- +// load point, and injecting them when ready.
- +class ScriptInjectionManager : public UserScriptSetManager::Observer {
- + public:
- + ScriptInjectionManager(const ScriptInjectionManager&) = delete;
- + ScriptInjectionManager& operator=(const ScriptInjectionManager&) = delete;
- + explicit ScriptInjectionManager(
- + UserScriptSetManager* user_script_set_manager);
- + virtual ~ScriptInjectionManager();
- +
- + // Notifies that a new render view has been created.
- + void OnRenderFrameCreated(content::RenderFrame* render_frame);
- +
- + // Removes pending injections of the unloaded extension.
- + //void OnExtensionUnloaded(const std::string& extension_id);
- +
- + void set_activity_logging_enabled(bool enabled) {
- + activity_logging_enabled_ = enabled;
- + }
- +
- + private:
- + // A RenderFrameObserver implementation which watches the various render
- + // frames in order to notify the ScriptInjectionManager of different
- + // document load states and IPCs.
- + class RFOHelper;
- +
- + using FrameStatusMap =
- + std::map<content::RenderFrame*, UserScript::RunLocation>;
- +
- + using ScriptInjectionVector = std::vector<std::unique_ptr<ScriptInjection>>;
- +
- + // Notifies that an injection has been finished.
- + void OnInjectionFinished(ScriptInjection* injection);
- +
- + // UserScriptSetManager::Observer implementation.
- + void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) override;
- +
- + // Notifies that an RFOHelper should be removed.
- + void RemoveObserver(RFOHelper* helper);
- +
- + // Invalidate any pending tasks associated with |frame|.
- + void InvalidateForFrame(content::RenderFrame* frame);
- +
- + // Starts the process to inject appropriate scripts into |frame|.
- + void StartInjectScripts(content::RenderFrame* frame,
- + UserScript::RunLocation run_location);
- +
- + // Actually injects the scripts into |frame|.
- + void InjectScripts(content::RenderFrame* frame,
- + UserScript::RunLocation run_location);
- +
- + // Try to inject and store injection if it has not finished.
- + void TryToInject(std::unique_ptr<ScriptInjection> injection,
- + UserScript::RunLocation run_location,
- + ScriptsRunInfo* scripts_run_info);
- +
- + // The map of active web frames to their corresponding statuses. The
- + // RunLocation of the frame corresponds to the last location that has ran.
- + FrameStatusMap frame_statuses_;
- +
- + // The frames currently being injected into, so long as that frame is valid.
- + std::set<content::RenderFrame*> active_injection_frames_;
- +
- + // The collection of RFOHelpers.
- + std::vector<std::unique_ptr<RFOHelper>> rfo_helpers_;
- +
- + // The set of UserScripts associated with extensions. Owned by the Dispatcher.
- + UserScriptSetManager* user_script_set_manager_;
- +
- + // Pending injections which are waiting for either the proper run location or
- + // user consent.
- + ScriptInjectionVector pending_injections_;
- +
- + // Running injections which are waiting for async callbacks from blink.
- + ScriptInjectionVector running_injections_;
- +
- + // Whether or not dom activity should be logged for scripts injected.
- + bool activity_logging_enabled_ = false;
- +
- + base::ScopedObservation<UserScriptSetManager, UserScriptSetManager::Observer>
- + user_script_set_manager_observation_{this};
- +};
- +
- +}
- diff --git a/components/user_scripts/renderer/script_injector.h b/components/user_scripts/renderer/script_injector.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/script_injector.h
- @@ -0,0 +1,96 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
- +#define USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
- +
- +#include <memory>
- +#include <vector>
- +
- +#include "../common/constants.h"
- +#include "../common/user_script.h"
- +#include "third_party/blink/public/web/web_script_source.h"
- +
- +class InjectionHost;
- +
- +namespace blink {
- +class WebLocalFrame;
- +}
- +
- +namespace user_scripts {
- +
- +// The pseudo-delegate class for a ScriptInjection that provides all necessary
- +// information about how to inject the script, including what code to inject and
- +// when (run location), but without any injection logic.
- +class ScriptInjector {
- + public:
- + // The possible reasons for not injecting the script.
- + enum InjectFailureReason {
- + EXTENSION_REMOVED, // The extension was removed before injection.
- + NOT_ALLOWED, // The script is not allowed to inject.
- + WONT_INJECT // The injection won't inject because the user rejected
- + // (or just did not accept) the injection.
- + };
- +
- + virtual ~ScriptInjector() {}
- +
- + // Returns the script type of this particular injection.
- + virtual UserScript::InjectionType script_type() const = 0;
- +
- + // Returns true if the script is running inside a user gesture.
- + virtual bool IsUserGesture() const = 0;
- +
- + // Returns the CSS origin of this injection.
- + virtual absl::optional<CSSOrigin> GetCssOrigin() const = 0;
- +
- + // Returns the key for this injection, if it's a CSS injection.
- + virtual const absl::optional<std::string> GetInjectionKey() const = 0;
- +
- + // Returns true if the script expects results.
- + virtual bool ExpectsResults() const = 0;
- +
- + // Returns true if the script should inject JS source at the given
- + // |run_location|.
- + virtual bool ShouldInjectJs(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& executing_scripts) const = 0;
- +
- + // Returns true if the script should inject CSS at the given |run_location|.
- + virtual bool ShouldInjectCss(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& injected_stylesheets) const = 0;
- +
- + // Returns the javascript sources to inject at the given |run_location|.
- + // Only called if ShouldInjectJs() is true.
- + virtual std::vector<blink::WebScriptSource> GetJsSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* executing_scripts,
- + size_t* num_injected_js_scripts) const = 0;
- +
- + // Returns the css to inject at the given |run_location|.
- + // Only called if ShouldInjectCss() is true.
- + virtual std::vector<blink::WebString> GetCssSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* injected_stylesheets,
- + size_t* num_injected_stylesheets) const = 0;
- +
- + // Notifies the script that injection has completed, with a possibly-populated
- + // list of results (depending on whether or not ExpectsResults() was true).
- + // |render_frame| contains the render frame, or null if the frame was
- + // invalidated.
- + virtual void OnInjectionComplete(
- + std::unique_ptr<base::Value> execution_result,
- + UserScript::RunLocation run_location,
- + content::RenderFrame* render_frame) = 0;
- +
- + // Notifies the script that injection will never occur.
- + // |render_frame| contains the render frame, or null if the frame was
- + // invalidated.
- + virtual void OnWillNotInject(InjectFailureReason reason,
- + content::RenderFrame* render_frame) = 0;
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
- diff --git a/components/user_scripts/renderer/scripts_run_info.cc b/components/user_scripts/renderer/scripts_run_info.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/scripts_run_info.cc
- @@ -0,0 +1,31 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "scripts_run_info.h"
- +
- +#include "base/metrics/histogram_macros.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "script_context.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +
- +namespace user_scripts {
- +
- +ScriptsRunInfo::ScriptsRunInfo(content::RenderFrame* render_frame,
- + UserScript::RunLocation location)
- + : num_css(0u),
- + num_js(0u),
- + num_blocking_js(0u),
- + routing_id_(render_frame->GetRoutingID()),
- + run_location_(location),
- + frame_url_(ScriptContext::GetDocumentLoaderURLForFrame(
- + render_frame->GetWebFrame())) {}
- +
- +ScriptsRunInfo::~ScriptsRunInfo() {
- +}
- +
- +void ScriptsRunInfo::LogRun(bool send_script_activity) {
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/scripts_run_info.h b/components/user_scripts/renderer/scripts_run_info.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/scripts_run_info.h
- @@ -0,0 +1,69 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
- +#define USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
- +
- +#include <stddef.h>
- +
- +#include <map>
- +#include <set>
- +#include <string>
- +
- +#include "base/timer/elapsed_timer.h"
- +#include "../common/user_script.h"
- +
- +namespace content {
- +class RenderFrame;
- +}
- +
- +namespace user_scripts {
- +
- +// A struct containing information about a script run.
- +struct ScriptsRunInfo {
- + ScriptsRunInfo(const ScriptsRunInfo&) = delete;
- + ScriptsRunInfo& operator=(const ScriptsRunInfo&) = delete;
- + // Map of extensions IDs to the executing script paths.
- + typedef std::map<std::string, std::set<std::string> > ExecutingScriptsMap;
- +
- + ScriptsRunInfo(content::RenderFrame* render_frame,
- + UserScript::RunLocation location);
- + ~ScriptsRunInfo();
- +
- + // The number of CSS scripts injected.
- + size_t num_css;
- + // The number of JS scripts injected.
- + size_t num_js;
- + // The number of blocked JS scripts injected.
- + size_t num_blocking_js;
- + // A map of extension ids to executing script paths.
- + ExecutingScriptsMap executing_scripts;
- + // A map of extension ids to injected stylesheet paths.
- + ExecutingScriptsMap injected_stylesheets;
- + // The elapsed time since the ScriptsRunInfo was constructed.
- + base::ElapsedTimer timer;
- +
- + // Log information about a given script run. If |send_script_activity| is
- + // true, this also informs the browser of the script run.
- + void LogRun(bool send_script_activity);
- +
- + static void LogLongInjectionTaskTime(UserScript::RunLocation run_location,
- + const base::TimeDelta& elapsed);
- +
- + private:
- + // The routinig id to use to notify the browser of any injections. Since the
- + // frame may be deleted in injection, we don't hold on to a reference to it
- + // directly.
- + int routing_id_;
- +
- + // The run location at which injection is happening.
- + UserScript::RunLocation run_location_;
- +
- + // The url of the frame, preserved for the same reason as the routing id.
- + GURL frame_url_;
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
- diff --git a/components/user_scripts/renderer/user_script_injector.cc b/components/user_scripts/renderer/user_script_injector.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_injector.cc
- @@ -0,0 +1,228 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "user_script_injector.h"
- +
- +#include <tuple>
- +#include <vector>
- +
- +#include "base/logging.h"
- +#include "base/lazy_instance.h"
- +#include "content/public/common/url_constants.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "content/public/renderer/render_view.h"
- +#include "components/user_scripts/renderer/grit/user_scripts_renderer_resources.h"
- +#include "injection_host.h"
- +#include "script_context.h"
- +#include "scripts_run_info.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "third_party/blink/public/web/web_script_source.h"
- +#include "ui/base/resource/resource_bundle.h"
- +#include "url/gurl.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +struct RoutingInfoKey {
- + int routing_id;
- + int script_id;
- +
- + RoutingInfoKey(int routing_id, int script_id)
- + : routing_id(routing_id), script_id(script_id) {}
- +
- + bool operator<(const RoutingInfoKey& other) const {
- + return std::tie(routing_id, script_id) <
- + std::tie(other.routing_id, other.script_id);
- + }
- +};
- +
- +using RoutingInfoMap = std::map<RoutingInfoKey, bool>;
- +
- +// A map records whether a given |script_id| from a webview-added user script
- +// is allowed to inject on the render of given |routing_id|.
- +// Once a script is added, the decision of whether or not allowed to inject
- +// won't be changed.
- +// After removed by the webview, the user scipt will also be removed
- +// from the render. Therefore, there won't be any query from the same
- +// |script_id| and |routing_id| pair.
- +// base::LazyInstance<RoutingInfoMap>::DestructorAtExit g_routing_info_map =
- +// LAZY_INSTANCE_INITIALIZER;
- +
- +// Greasemonkey API source that is injected with the scripts.
- +struct GreasemonkeyApiJsString {
- + GreasemonkeyApiJsString();
- + blink::WebScriptSource GetSource() const;
- +
- + private:
- + blink::WebString source_;
- +};
- +
- +// The below constructor, monstrous as it is, just makes a WebScriptSource from
- +// the GreasemonkeyApiJs resource.
- +GreasemonkeyApiJsString::GreasemonkeyApiJsString() {
- + std::string greasemonky_api_js(
- + ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
- + IDR_GREASEMONKEY_API_JS));
- + source_ = blink::WebString::FromUTF8(greasemonky_api_js);
- +}
- +
- +blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
- + return blink::WebScriptSource(source_);
- +}
- +
- +base::LazyInstance<GreasemonkeyApiJsString>::Leaky g_greasemonkey_api =
- + LAZY_INSTANCE_INITIALIZER;
- +
- +bool ShouldInjectScripts(const UserScript::FileList& scripts,
- + const std::set<std::string>& injected_files) {
- + for (const std::unique_ptr<UserScript::File>& file : scripts) {
- + // Check if the script is already injected.
- + if (injected_files.count(file->url().path()) == 0) {
- + return true;
- + }
- + }
- + return false;
- +}
- +
- +} // namespace
- +
- +UserScriptInjector::UserScriptInjector(const UserScript* script,
- + UserScriptSet* script_list)
- + : script_(script),
- + user_script_set_(script_list),
- + script_id_(script_->id()),
- + user_script_set_observer_(this) {
- + user_script_set_observer_.Observe(script_list);
- +}
- +
- +UserScriptInjector::~UserScriptInjector() {
- +}
- +
- +void UserScriptInjector::OnUserScriptsUpdated(
- + const std::set<HostID>& changed_hosts,
- + const UserScriptList& scripts) {
- + // When user scripts are updated, all the old script pointers are invalidated.
- + script_ = nullptr;
- + // If the host causing this injection changed, then this injection
- + // will be removed, and there's no guarantee the backing script still exists.
- + // if (changed_hosts.count(host_id_) > 0)
- + // return;
- +
- + for (const std::unique_ptr<UserScript>& script : scripts) {
- + if (script->id() == script_id_) {
- + script_ = script.get();
- + break;
- + }
- + }
- + // If |host_id_| wasn't in |changed_hosts|, then the script for this injection
- + // should be guaranteed to exist.
- + DCHECK(script_);
- +}
- +
- +UserScript::InjectionType UserScriptInjector::script_type() const {
- + return UserScript::CONTENT_SCRIPT;
- +}
- +
- +bool UserScriptInjector::IsUserGesture() const {
- + return false;
- +}
- +
- +bool UserScriptInjector::ExpectsResults() const {
- + return false;
- +}
- +
- +absl::optional<CSSOrigin> UserScriptInjector::GetCssOrigin() const {
- + return absl::nullopt;
- +}
- +
- +const absl::optional<std::string> UserScriptInjector::GetInjectionKey() const {
- + return absl::nullopt;
- +}
- +
- +bool UserScriptInjector::ShouldInjectJs(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& executing_scripts) const {
- + return script_ && script_->run_location() == run_location &&
- + !script_->js_scripts().empty() &&
- + ShouldInjectScripts(script_->js_scripts(), executing_scripts);
- +}
- +
- +bool UserScriptInjector::ShouldInjectCss(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& injected_stylesheets) const {
- + return script_ && run_location == UserScript::DOCUMENT_START &&
- + !script_->css_scripts().empty() &&
- + ShouldInjectScripts(script_->css_scripts(), injected_stylesheets);
- +}
- +
- +std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* executing_scripts,
- + size_t* num_injected_js_scripts) const {
- + DCHECK(script_);
- + std::vector<blink::WebScriptSource> sources;
- +
- + DCHECK_EQ(script_->run_location(), run_location);
- +
- + const UserScript::FileList& js_scripts = script_->js_scripts();
- + sources.reserve(js_scripts.size() +
- + (script_->emulate_greasemonkey() ? 1 : 0));
- + // Emulate Greasemonkey API for scripts that were converted to extension
- + // user scripts.
- + if (script_->emulate_greasemonkey())
- + sources.push_back(g_greasemonkey_api.Get().GetSource());
- + for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
- + const GURL& script_url = file->url();
- + // Check if the script is already injected.
- + if (executing_scripts->count(script_url.path()) != 0)
- + continue;
- +
- + sources.push_back(blink::WebScriptSource(
- + user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
- + script_url));
- +
- + (*num_injected_js_scripts) += 1;
- + executing_scripts->insert(script_url.path());
- + }
- +
- + return sources;
- +}
- +
- +std::vector<blink::WebString> UserScriptInjector::GetCssSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* injected_stylesheets,
- + size_t* num_injected_stylesheets) const {
- + DCHECK(script_);
- + DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
- +
- + std::vector<blink::WebString> sources;
- +
- + const UserScript::FileList& css_scripts = script_->css_scripts();
- + sources.reserve(css_scripts.size());
- + for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts()) {
- + const std::string& stylesheet_path = file->url().path();
- + // Check if the stylesheet is already injected.
- + if (injected_stylesheets->count(stylesheet_path) != 0)
- + continue;
- +
- + sources.push_back(user_script_set_->GetCssSource(*file));
- + (*num_injected_stylesheets) += 1;
- + injected_stylesheets->insert(stylesheet_path);
- + }
- + return sources;
- +}
- +
- +void UserScriptInjector::OnInjectionComplete(
- + std::unique_ptr<base::Value> execution_result,
- + UserScript::RunLocation run_location,
- + content::RenderFrame* render_frame) {}
- +
- +void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
- + content::RenderFrame* render_frame) {
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/user_script_injector.h b/components/user_scripts/renderer/user_script_injector.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_injector.h
- @@ -0,0 +1,86 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
- +#define USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
- +
- +#include <memory>
- +#include <string>
- +
- +#include "base/values.h"
- +#include "base/scoped_observation.h"
- +#include "../common/user_script.h"
- +#include "script_injection.h"
- +#include "user_script_set.h"
- +
- +class InjectionHost;
- +
- +namespace blink {
- +class WebLocalFrame;
- +}
- +
- +namespace user_scripts {
- +
- +// A ScriptInjector for UserScripts.
- +class UserScriptInjector : public ScriptInjector,
- + public UserScriptSet::Observer {
- + public:
- + UserScriptInjector(const UserScriptInjector&) = delete;
- + UserScriptInjector& operator=(const UserScriptInjector&) = delete;
- + UserScriptInjector(const UserScript* user_script,
- + UserScriptSet* user_script_set);
- + ~UserScriptInjector() override;
- +
- + private:
- + // UserScriptSet::Observer implementation.
- + void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts,
- + const UserScriptList& scripts) override;
- +
- + // ScriptInjector implementation.
- + UserScript::InjectionType script_type() const override;
- + bool IsUserGesture() const override;
- + absl::optional<CSSOrigin> GetCssOrigin() const override;
- + const absl::optional<std::string> GetInjectionKey() const override;
- + bool ExpectsResults() const override;
- + bool ShouldInjectJs(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& executing_scripts) const override;
- + bool ShouldInjectCss(
- + UserScript::RunLocation run_location,
- + const std::set<std::string>& injected_stylesheets) const override;
- + std::vector<blink::WebScriptSource> GetJsSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* executing_scripts,
- + size_t* num_injected_js_scripts) const override;
- + std::vector<blink::WebString> GetCssSources(
- + UserScript::RunLocation run_location,
- + std::set<std::string>* injected_stylesheets,
- + size_t* num_injected_stylesheets) const override;
- + void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
- + UserScript::RunLocation run_location,
- + content::RenderFrame* render_frame) override;
- + void OnWillNotInject(InjectFailureReason reason,
- + content::RenderFrame* render_frame) override;
- +
- + // The associated user script. Owned by the UserScriptInjector that created
- + // this object.
- + const UserScript* script_;
- +
- + // The UserScriptSet that eventually owns the UserScript this
- + // UserScriptInjector points to.
- + // Outlives |this|.
- + UserScriptSet* const user_script_set_;
- +
- + // The id of the associated user script. We cache this because when we update
- + // the |script_| associated with this injection, the old referance may be
- + // deleted.
- + int script_id_;
- +
- + base::ScopedObservation<UserScriptSet, UserScriptSet::Observer>
- + user_script_set_observer_{this};
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
- diff --git a/components/user_scripts/renderer/user_script_set.cc b/components/user_scripts/renderer/user_script_set.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_set.cc
- @@ -0,0 +1,262 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "user_script_set.h"
- +
- +#include <stddef.h>
- +
- +#include <utility>
- +
- +#include "base/logging.h"
- +#include "base/debug/alias.h"
- +#include "base/memory/ref_counted.h"
- +#include "base/strings/strcat.h"
- +#include "content/public/common/url_constants.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "injection_host.h"
- +#include "script_context.h"
- +#include "script_injection.h"
- +#include "user_script_injector.h"
- +#include "web_ui_injection_host.h"
- +#include "third_party/blink/public/web/web_document.h"
- +#include "third_party/blink/public/web/web_local_frame.h"
- +#include "url/gurl.h"
- +#include "../common/user_scripts_features.h"
- +
- +namespace user_scripts {
- +
- +namespace {
- +
- +// These two strings are injected before and after the Greasemonkey API and
- +// user script to wrap it in an anonymous scope.
- +const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
- +const char kUserScriptTail[] = "\n})(window);";
- +// Maximum number of total content scripts we allow (across all extensions).
- +// The limit exists to diagnose https://crbug.com/723381. The number is
- +// arbitrarily chosen.
- +// TODO(lazyboy): Remove when the bug is fixed.
- +const uint32_t kNumScriptsArbitraryMax = 100000u;
- +
- +GURL GetDocumentUrlForFrame(blink::WebLocalFrame* frame) {
- + GURL data_source_url = ScriptContext::GetDocumentLoaderURLForFrame(frame);
- + if (!data_source_url.is_empty() && frame->IsViewSourceModeEnabled()) {
- + data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
- + data_source_url.spec());
- + }
- +
- + return data_source_url;
- +}
- +
- +} // namespace
- +
- +UserScriptSet::UserScriptSet() {}
- +
- +UserScriptSet::~UserScriptSet() {
- +}
- +
- +void UserScriptSet::AddObserver(Observer* observer) {
- + observers_.AddObserver(observer);
- +}
- +
- +void UserScriptSet::RemoveObserver(Observer* observer) {
- + observers_.RemoveObserver(observer);
- +}
- +
- +void UserScriptSet::GetInjections(
- + std::vector<std::unique_ptr<ScriptInjection>>* injections,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location,
- + bool log_activity) {
- + GURL document_url = GetDocumentUrlForFrame(render_frame->GetWebFrame());
- + for (const std::unique_ptr<UserScript>& script : scripts_) {
- + std::unique_ptr<ScriptInjection> injection = GetInjectionForScript(
- + script.get(), render_frame, tab_id, run_location, document_url,
- + /* is_declarative, */ log_activity);
- + if (injection.get())
- + injections->push_back(std::move(injection));
- + }
- +}
- +
- +bool UserScriptSet::UpdateUserScripts(
- + base::ReadOnlySharedMemoryRegion shared_memory,
- + const std::set<HostID>& changed_hosts,
- + bool whitelisted_only) {
- + bool only_inject_incognito = false;
- + //ExtensionsRendererClient::Get()->IsIncognitoProcess();
- +
- + // Create the shared memory mapping.
- + shared_memory_mapping_ = shared_memory.Map();
- + if (!shared_memory.IsValid())
- + return false;
- +
- + // First get the size of the memory block.
- + const base::Pickle::Header* pickle_header =
- + shared_memory_mapping_.GetMemoryAs<base::Pickle::Header>();
- + if (!pickle_header)
- + return false;
- +
- + // Now read in the rest of the block.
- + size_t pickle_size =
- + sizeof(base::Pickle::Header) + pickle_header->payload_size;
- +
- + // Unpickle scripts.
- + uint32_t num_scripts = 0;
- + auto memory = shared_memory_mapping_.GetMemoryAsSpan<char>(pickle_size);
- + if (!memory.size())
- + return false;
- +
- + base::Pickle pickle(memory.data(), pickle_size);
- + base::PickleIterator iter(pickle);
- + base::debug::Alias(&pickle_size);
- + CHECK(iter.ReadUInt32(&num_scripts));
- +
- + // Sometimes the shared memory contents seem to be corrupted
- + // (https://crbug.com/723381). Set an arbitrary max limit to the number of
- + // scripts so that we don't add OOM noise to crash reports.
- + CHECK_LT(num_scripts, kNumScriptsArbitraryMax);
- +
- + scripts_.clear();
- + script_sources_.clear();
- + scripts_.reserve(num_scripts);
- + for (uint32_t i = 0; i < num_scripts; ++i) {
- + std::unique_ptr<UserScript> script(new UserScript());
- + script->Unpickle(pickle, &iter);
- +
- + // Note that this is a pointer into shared memory. We don't own it. It gets
- + // cleared up when the last renderer or browser process drops their
- + // reference to the shared memory.
- + for (size_t j = 0; j < script->js_scripts().size(); ++j) {
- + const char* body = NULL;
- + int body_length = 0;
- + CHECK(iter.ReadData(&body, &body_length));
- + script->js_scripts()[j]->set_external_content(
- + base::StringPiece(body, body_length));
- + }
- + for (size_t j = 0; j < script->css_scripts().size(); ++j) {
- + const char* body = NULL;
- + int body_length = 0;
- + CHECK(iter.ReadData(&body, &body_length));
- + script->css_scripts()[j]->set_external_content(
- + base::StringPiece(body, body_length));
- + }
- +
- + if (only_inject_incognito && !script->is_incognito_enabled())
- + continue; // This script shouldn't run in an incognito tab.
- +
- + scripts_.push_back(std::move(script));
- + }
- +
- + for (auto& observer : observers_)
- + observer.OnUserScriptsUpdated(changed_hosts, scripts_);
- + return true;
- +}
- +
- +void UserScriptSet::AddScript(std::unique_ptr<UserScript> script) {
- + scripts_.push_back(std::move(script));
- +}
- +
- +std::unique_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript(
- + const UserScript* script,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location,
- + const GURL& document_url,
- + //bool is_declarative,
- + bool log_activity) {
- + std::unique_ptr<ScriptInjection> injection;
- + std::unique_ptr<const InjectionHost> injection_host;
- + blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
- +
- + const HostID& host_id = script->host_id();
- + injection_host.reset(new WebUIInjectionHost(host_id));
- +
- + GURL effective_document_url =
- + ScriptContext::GetEffectiveDocumentURLForInjection(
- + web_frame, document_url, script->match_origin_as_fallback());
- +
- + bool is_subframe = web_frame->Parent();
- + if (!script->MatchesDocument(effective_document_url, is_subframe)) {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: No Match name=" << script->name() <<
- + " id=" << script->host_id().id() <<
- + " url=" << effective_document_url.spec();
- + return injection;
- + }
- +
- + std::unique_ptr<ScriptInjector> injector(
- + new UserScriptInjector(script, this));
- +
- + bool inject_css = !script->css_scripts().empty() &&
- + run_location == UserScript::DOCUMENT_START;
- + bool inject_js =
- + !script->js_scripts().empty() && script->run_location() == run_location;
- + if (inject_css || inject_js) {
- + injection.reset(new ScriptInjection(std::move(injector), render_frame,
- + std::move(injection_host), run_location,
- + log_activity));
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: Match name=" << script->name() << " " <<
- + "url=" << effective_document_url.spec();
- + } else {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts)) {
- + if (script->run_location() != run_location)
- + LOG(INFO) << "UserScripts: run location for name=" << script->name() <<
- + " id=" << script->host_id().id() <<
- + " current " << run_location <<
- + " need " << script->run_location();
- + else
- + LOG(INFO) << "UserScripts: Match but no run name=" << script->name() <<
- + " id=" << script->host_id().id() <<
- + " url=" << effective_document_url.spec();
- + }
- + }
- + return injection;
- +}
- +
- +blink::WebString UserScriptSet::GetJsSource(const UserScript::File& file,
- + bool emulate_greasemonkey) {
- + const GURL& url = file.url();
- + auto iter = script_sources_.find(url);
- + if (iter != script_sources_.end()) {
- + return iter->second;
- + }
- +
- + base::StringPiece script_content = file.GetContent();
- + blink::WebString source;
- + if (emulate_greasemonkey) {
- + // We add this dumb function wrapper for user scripts to emulate what
- + // Greasemonkey does. |script_content| becomes:
- + // concat(kUserScriptHead, script_content, kUserScriptTail).
- + std::string content =
- + base::StrCat({kUserScriptHead, script_content, kUserScriptTail});
- + source = blink::WebString::FromUTF8(content);
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: Injecting w/greasemonkey " << file.url();
- + } else {
- + source = blink::WebString::FromUTF8(script_content.data(),
- + script_content.length());
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: Injecting " << file.url();
- + }
- + script_sources_[url] = source;
- + return source;
- +}
- +
- +blink::WebString UserScriptSet::GetCssSource(const UserScript::File& file) {
- + const GURL& url = file.url();
- + auto iter = script_sources_.find(url);
- + if (iter != script_sources_.end())
- + return iter->second;
- +
- + base::StringPiece script_content = file.GetContent();
- + return script_sources_
- + .insert(std::make_pair(
- + url, blink::WebString::FromUTF8(script_content.data(),
- + script_content.length())))
- + .first->second;
- +}
- +
- +} // namespace extensions
- diff --git a/components/user_scripts/renderer/user_script_set.h b/components/user_scripts/renderer/user_script_set.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_set.h
- @@ -0,0 +1,101 @@
- +// Copyright 2014 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
- +#define USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
- +
- +#include <map>
- +#include <memory>
- +#include <set>
- +#include <string>
- +#include <vector>
- +
- +#include "base/memory/read_only_shared_memory_region.h"
- +#include "base/observer_list.h"
- +#include "../common/user_script.h"
- +#include "third_party/blink/public/platform/web_string.h"
- +
- +class GURL;
- +
- +namespace content {
- +class RenderFrame;
- +}
- +
- +namespace user_scripts {
- +class ScriptInjection;
- +
- +// The UserScriptSet is a collection of UserScripts which knows how to update
- +// itself from SharedMemory and create ScriptInjections for UserScripts to
- +// inject on a page.
- +class UserScriptSet {
- + public:
- + UserScriptSet(const UserScriptSet&) = delete;
- + UserScriptSet& operator=(const UserScriptSet&) = delete;
- + class Observer {
- + public:
- + // Called when the set of user scripts is updated. |changed_hosts| contains
- + // the hosts whose scripts have been altered. Note that *all* script objects
- + // are invalidated, even if they aren't in |changed_hosts|.
- + virtual void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts,
- + const UserScriptList& scripts) = 0;
- + };
- +
- + UserScriptSet();
- + ~UserScriptSet();
- +
- + // Adds or removes observers.
- + void AddObserver(Observer* observer);
- + void RemoveObserver(Observer* observer);
- + void AddScript(std::unique_ptr<UserScript> script);
- +
- + // Append any ScriptInjections that should run on the given |render_frame| and
- + // |tab_id|, at the given |run_location|, to |injections|.
- + // |extensions| is passed in to verify the corresponding extension is still
- + // valid.
- + void GetInjections(std::vector<std::unique_ptr<ScriptInjection>>* injections,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location,
- + bool log_activity);
- +
- + // Updates scripts given the shared memory region containing user scripts.
- + // Returns true if the scripts were successfully updated.
- + bool UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
- + const std::set<HostID>& changed_hosts,
- + bool whitelisted_only);
- +
- + // Returns the contents of a script file.
- + // Note that copying is cheap as this uses WebString.
- + blink::WebString GetJsSource(const UserScript::File& file,
- + bool emulate_greasemonkey);
- + blink::WebString GetCssSource(const UserScript::File& file);
- +
- + private:
- + // Returns a new ScriptInjection for the given |script| to execute in the
- + // |render_frame|, or NULL if the script should not execute.
- + std::unique_ptr<ScriptInjection> GetInjectionForScript(
- + const UserScript* script,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location,
- + const GURL& document_url,
- + //bool is_declarative,
- + bool log_activity);
- +
- + // Shared memory mapping containing raw script data.
- + base::ReadOnlySharedMemoryMapping shared_memory_mapping_;
- +
- + // The UserScripts this injector manages.
- + UserScriptList scripts_;
- +
- + // Map of user script file url -> source.
- + std::map<GURL, blink::WebString> script_sources_;
- +
- + // The associated observers.
- + base::ObserverList<Observer>::Unchecked observers_;
- +};
- +
- +} // namespace extensions
- +
- +#endif // USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
- diff --git a/components/user_scripts/renderer/user_script_set_manager.cc b/components/user_scripts/renderer/user_script_set_manager.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_set_manager.cc
- @@ -0,0 +1,77 @@
- +#include "user_script_set_manager.h"
- +
- +#include "base/logging.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "../common/host_id.h"
- +#include "../common/extension_messages.h"
- +#include "../common/user_scripts_features.h"
- +#include "user_script_set.h"
- +
- +namespace user_scripts {
- +
- +UserScriptSetManager::UserScriptSetManager() {
- + content::RenderThread::Get()->AddObserver(this);
- +}
- +
- +UserScriptSetManager::~UserScriptSetManager() {
- +}
- +
- +void UserScriptSetManager::AddObserver(Observer* observer) {
- + observers_.AddObserver(observer);
- +}
- +
- +void UserScriptSetManager::RemoveObserver(Observer* observer) {
- + observers_.RemoveObserver(observer);
- +}
- +
- +bool UserScriptSetManager::OnControlMessageReceived(
- + const IPC::Message& message) {
- + bool handled = true;
- + IPC_BEGIN_MESSAGE_MAP(UserScriptSetManager, message)
- + IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
- + IPC_MESSAGE_UNHANDLED(handled = false)
- + IPC_END_MESSAGE_MAP()
- + return handled;
- +}
- +
- +void UserScriptSetManager::GetAllInjections(
- + std::vector<std::unique_ptr<ScriptInjection>>* injections,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location) {
- +
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: GetAllInjections";
- +
- + // static_scripts_ is UserScriptSet
- + static_scripts_.GetInjections(injections, render_frame, tab_id, run_location,
- + activity_logging_enabled_);
- +}
- +
- +void UserScriptSetManager::OnUpdateUserScripts(
- + base::ReadOnlySharedMemoryRegion shared_memory) {
- + if (!shared_memory.IsValid()) {
- + NOTREACHED() << "Bad scripts handle";
- + return;
- + }
- +
- + UserScriptSet* scripts = NULL;
- + scripts = &static_scripts_;
- +
- + DCHECK(scripts);
- +
- + // If no hosts are included in the set, that indicates that all
- + // hosts were updated. Add them all to the set so that observers and
- + // individual UserScriptSets don't need to know this detail.
- + //const std::set<HostID>* effective_hosts = &changed_hosts;
- + std::set<HostID> all_hosts;
- + const std::set<HostID>* effective_hosts = &all_hosts;
- +
- + if (scripts->UpdateUserScripts(std::move(shared_memory), *effective_hosts,
- + false /*whitelisted_only*/)) {
- + for (auto& observer : observers_)
- + observer.OnUserScriptsUpdated(all_hosts /* *effective_hosts*/);
- + }
- +}
- +
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/renderer/user_script_set_manager.h b/components/user_scripts/renderer/user_script_set_manager.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_script_set_manager.h
- @@ -0,0 +1,61 @@
- +#ifndef USERSCRIPTS_RENDER_SET_MANAGER_H_
- +#define USERSCRIPTS_RENDER_SET_MANAGER_H_
- +
- +#include <map>
- +#include <set>
- +#include <string>
- +#include <vector>
- +
- +#include "base/memory/read_only_shared_memory_region.h"
- +#include "base/observer_list.h"
- +#include "content/public/renderer/render_thread_observer.h"
- +#include "../common/host_id.h"
- +#include "user_script_set.h"
- +#include "script_injection.h"
- +
- +namespace user_scripts {
- +
- +class UserScriptSetManager : public content::RenderThreadObserver {
- + public:
- + class Observer {
- + public:
- + virtual void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) = 0;
- + };
- +
- + UserScriptSetManager();
- +
- + ~UserScriptSetManager() override;
- +
- + void AddObserver(Observer* observer);
- + void RemoveObserver(Observer* observer);
- +
- + // Append all injections from |static_scripts| and each of
- + // |programmatic_scripts_| to |injections|.
- + void GetAllInjections(
- + std::vector<std::unique_ptr<ScriptInjection>>* injections,
- + content::RenderFrame* render_frame,
- + int tab_id,
- + UserScript::RunLocation run_location);
- +
- +private:
- + // content::RenderThreadObserver implementation.
- + bool OnControlMessageReceived(const IPC::Message& message) override;
- +
- + base::ObserverList<Observer>::Unchecked observers_;
- +
- + // Handle the UpdateUserScripts extension message.
- + void OnUpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory);
- + //, const HostID& host_id,
- + //const std::set<HostID>& changed_hosts,
- + //bool whitelisted_only);
- +
- + // Scripts statically defined in extension manifests.
- + UserScriptSet static_scripts_;
- +
- + // Whether or not dom activity should be logged for scripts injected.
- + bool activity_logging_enabled_ = false;
- +};
- +
- +}
- +
- +#endif
- diff --git a/components/user_scripts/renderer/user_scripts_dispatcher.cc b/components/user_scripts/renderer/user_scripts_dispatcher.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_scripts_dispatcher.cc
- @@ -0,0 +1,36 @@
- +#include "user_scripts_dispatcher.h"
- +
- +#include <stddef.h>
- +
- +#include <algorithm>
- +#include <memory>
- +#include <utility>
- +
- +#include "content/public/renderer/render_thread.h"
- +#include "extension_frame_helper.h"
- +
- +namespace user_scripts {
- +
- +// ex ChromeExtensionsDispatcherDelegate
- +UserScriptsDispatcher::UserScriptsDispatcher()
- + : user_script_set_manager_observer_(this) {
- + user_script_set_manager_.reset(new UserScriptSetManager());
- + script_injection_manager_.reset(
- + new ScriptInjectionManager(user_script_set_manager_.get()));
- + user_script_set_manager_observer_.Observe(user_script_set_manager_.get());
- +}
- +
- +UserScriptsDispatcher::~UserScriptsDispatcher() {
- +}
- +
- +void UserScriptsDispatcher::OnRenderThreadStarted(content::RenderThread* thread) {
- +}
- +
- +void UserScriptsDispatcher::OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) {
- +}
- +
- +void UserScriptsDispatcher::OnRenderFrameCreated(content::RenderFrame* render_frame) {
- + script_injection_manager_->OnRenderFrameCreated(render_frame);
- +}
- +
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/renderer/user_scripts_dispatcher.h b/components/user_scripts/renderer/user_scripts_dispatcher.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_scripts_dispatcher.h
- @@ -0,0 +1,49 @@
- +#ifndef USERSCRIPTS_RENDER_DISPATCHER_H_
- +#define USERSCRIPTS_RENDER_DISPATCHER_H_
- +
- +#include "user_script_set_manager.h"
- +#include "script_injection_manager.h"
- +
- +#include <stdint.h>
- +
- +#include <map>
- +#include <memory>
- +#include <set>
- +#include <string>
- +#include <utility>
- +#include <vector>
- +
- +#include "base/scoped_observation.h"
- +#include "content/public/renderer/render_thread_observer.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "../common/host_id.h"
- +#include "user_script_set_manager.h"
- +#include "script_injection.h"
- +
- +namespace user_scripts {
- +
- +class UserScriptsDispatcher : public content::RenderThreadObserver,
- + public UserScriptSetManager::Observer {
- +
- + public:
- + UserScriptsDispatcher(const UserScriptsDispatcher&) = delete;
- + UserScriptsDispatcher& operator=(const UserScriptsDispatcher&) = delete;
- + explicit UserScriptsDispatcher();
- + ~UserScriptsDispatcher() override;
- +
- + void OnRenderThreadStarted(content::RenderThread* thread);
- + void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) override;
- + void OnRenderFrameCreated(content::RenderFrame* render_frame);
- +
- + private:
- + std::unique_ptr<UserScriptSetManager> user_script_set_manager_;
- +
- + std::unique_ptr<ScriptInjectionManager> script_injection_manager_;
- +
- + base::ScopedObservation<UserScriptSetManager, UserScriptSetManager::Observer>
- + user_script_set_manager_observer_{this};
- +};
- +
- +}
- +
- +#endif
- diff --git a/components/user_scripts/renderer/user_scripts_renderer_client.cc b/components/user_scripts/renderer/user_scripts_renderer_client.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_scripts_renderer_client.cc
- @@ -0,0 +1,105 @@
- +#include "user_scripts_renderer_client.h"
- +
- +#include <memory>
- +#include <utility>
- +
- +#include "base/logging.h"
- +#include "base/lazy_instance.h"
- +#include "content/public/renderer/render_frame.h"
- +#include "content/public/renderer/render_thread.h"
- +#include "content/public/renderer/render_frame_visitor.h"
- +#include "chrome/renderer/chrome_render_thread_observer.h"
- +
- +#include "../common/user_scripts_features.h"
- +#include "user_scripts_dispatcher.h"
- +#include "extension_frame_helper.h"
- +
- +namespace user_scripts {
- +
- +// was ChromeExtensionsRendererClient
- +UserScriptsRendererClient::UserScriptsRendererClient() {}
- +
- +UserScriptsRendererClient::~UserScriptsRendererClient() {}
- +
- +// static
- +UserScriptsRendererClient* UserScriptsRendererClient::GetInstance() {
- + static base::LazyInstance<UserScriptsRendererClient>::Leaky client =
- + LAZY_INSTANCE_INITIALIZER;
- + return client.Pointer();
- +}
- +
- +void UserScriptsRendererClient::RenderThreadStarted() {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: RenderThreadStarted";
- +
- + content::RenderThread* thread = content::RenderThread::Get();
- + dispatcher_ = std::make_unique<UserScriptsDispatcher>();
- +
- + dispatcher_->OnRenderThreadStarted(thread);
- + thread->AddObserver(dispatcher_.get());
- +}
- +
- +void UserScriptsRendererClient::ConfigurationUpdated() {
- + if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
- + LOG(INFO) << "UserScripts: Configuration Updated";
- +
- + struct WatchFrame : public content::RenderFrameVisitor {
- + bool Visit(content::RenderFrame* frame) override {
- + if (frame)
- + UserScriptsRendererClient::GetInstance()->RenderFrameCreated(frame, NULL);
- + return true; // Continue visiting.
- + }
- + };
- + WatchFrame visitor = {};
- + content::RenderFrame::ForEach(&visitor);
- +}
- +
- +void UserScriptsRendererClient::RenderFrameCreated(
- + content::RenderFrame* render_frame,
- + service_manager::BinderRegistry* registry) {
- +
- + auto params = ChromeRenderThreadObserver::GetDynamicParams();
- + enabled_ = params.allow_userscript;
- + if (!enabled_) return;
- +
- + if (loaded_ == false) {
- + loaded_ = true;
- + new user_scripts::ExtensionFrameHelper(render_frame);
- + dispatcher_->OnRenderFrameCreated(render_frame);
- + }
- +}
- +
- +void UserScriptsRendererClient::RunScriptsAtDocumentStart(content::RenderFrame* render_frame) {
- + if (!enabled_ || !loaded_) return;
- +
- + ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
- + if (!frame_helper)
- + return; // The frame is invisible to user scripts.
- +
- + frame_helper->RunScriptsAtDocumentStart();
- + // |frame_helper| and |render_frame| might be dead by now.
- +}
- +
- +void UserScriptsRendererClient::RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) {
- + if (!enabled_ || !loaded_) return;
- +
- + ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
- + if (!frame_helper)
- + return; // The frame is invisible to user scripts.
- +
- + frame_helper->RunScriptsAtDocumentEnd();
- + // |frame_helper| and |render_frame| might be dead by now.
- +}
- +
- +void UserScriptsRendererClient::RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) {
- + if (!enabled_ || !loaded_) return;
- +
- + ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
- + if (!frame_helper)
- + return; // The frame is invisible to user scripts.
- +
- + frame_helper->RunScriptsAtDocumentIdle();
- + // |frame_helper| and |render_frame| might be dead by now.
- +}
- +
- +}
- \ No newline at end of file
- diff --git a/components/user_scripts/renderer/user_scripts_renderer_client.h b/components/user_scripts/renderer/user_scripts_renderer_client.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/user_scripts_renderer_client.h
- @@ -0,0 +1,37 @@
- +#ifndef USERSCRIPTS_RENDER_CLIENT_H_
- +#define USERSCRIPTS_RENDER_CLIENT_H_
- +
- +#include <memory>
- +#include <string>
- +
- +#include "user_scripts_dispatcher.h"
- +#include "services/service_manager/public/cpp/binder_registry.h"
- +
- +namespace user_scripts {
- +
- +class UserScriptsRendererClient {
- + public:
- + UserScriptsRendererClient(const UserScriptsRendererClient&) = delete;
- + UserScriptsRendererClient& operator=(const UserScriptsRendererClient&) = delete;
- + UserScriptsRendererClient();
- + ~UserScriptsRendererClient();
- +
- + static UserScriptsRendererClient* GetInstance();
- +
- + void RenderThreadStarted();
- + void ConfigurationUpdated();
- + void RenderFrameCreated(content::RenderFrame* render_frame,
- + service_manager::BinderRegistry* registry);
- + void RunScriptsAtDocumentStart(content::RenderFrame* render_frame);
- + void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame);
- + void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame);
- +
- + private:
- + std::unique_ptr<UserScriptsDispatcher> dispatcher_;
- + bool enabled_ = false;
- + bool loaded_ = false;
- +};
- +
- +}
- +
- +#endif
- diff --git a/components/user_scripts/renderer/web_ui_injection_host.cc b/components/user_scripts/renderer/web_ui_injection_host.cc
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/web_ui_injection_host.cc
- @@ -0,0 +1,40 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#include "web_ui_injection_host.h"
- +#include "base/no_destructor.h"
- +
- +namespace {
- +
- +// The default secure CSP to be used in order to prevent remote scripts.
- +const char kDefaultSecureCSP[] = "script-src 'self'; object-src 'self';";
- +
- +}
- +
- +WebUIInjectionHost::WebUIInjectionHost(const HostID& host_id)
- + : InjectionHost(host_id),
- + url_(host_id.id()) {
- +}
- +
- +WebUIInjectionHost::~WebUIInjectionHost() {
- +}
- +
- +const std::string* WebUIInjectionHost::GetContentSecurityPolicy() const {
- + // Use the main world CSP.
- + // return nullptr;
- +
- + // The isolated world will use its own CSP which blocks remotely hosted
- + // code.
- + static const base::NoDestructor<std::string> default_isolated_world_csp(
- + kDefaultSecureCSP);
- + return default_isolated_world_csp.get();
- +}
- +
- +const GURL& WebUIInjectionHost::url() const {
- + return url_;
- +}
- +
- +const std::string& WebUIInjectionHost::name() const {
- + return id().id();
- +}
- diff --git a/components/user_scripts/renderer/web_ui_injection_host.h b/components/user_scripts/renderer/web_ui_injection_host.h
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/renderer/web_ui_injection_host.h
- @@ -0,0 +1,27 @@
- +// Copyright 2015 The Chromium Authors. All rights reserved.
- +// Use of this source code is governed by a BSD-style license that can be
- +// found in the LICENSE file.
- +
- +#ifndef USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
- +#define USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
- +
- +#include "injection_host.h"
- +
- +class WebUIInjectionHost : public InjectionHost {
- + public:
- + WebUIInjectionHost(const WebUIInjectionHost&) = delete;
- + WebUIInjectionHost& operator=(const WebUIInjectionHost&) = delete;
- + WebUIInjectionHost(const HostID& host_id);
- + ~WebUIInjectionHost() override;
- +
- + private:
- + // InjectionHost:
- + const std::string* GetContentSecurityPolicy() const override;
- + const GURL& url() const override;
- + const std::string& name() const override;
- +
- + private:
- + GURL url_;
- +};
- +
- +#endif // USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
- diff --git a/components/user_scripts/strings/userscripts_strings.grdp b/components/user_scripts/strings/userscripts_strings.grdp
- new file mode 100755
- --- /dev/null
- +++ b/components/user_scripts/strings/userscripts_strings.grdp
- @@ -0,0 +1,57 @@
- +<?xml version="1.0" encoding="utf-8"?>
- +<grit-part>
- +
- + <!-- Preferences -->
- + <message name="IDS_PREFS_USERSCRIPTS_SETTINGS"
- + desc="."
- + formatter_data="android_java">
- + User Scripts
- + </message>
- +
- + <message name="IDS_OPTION_USERSCRIPT_FLAG" desc="." formatter_data="android_java">
- + Activate User Scripts
- + </message>
- +
- + <message name="IDS_OPTION_USERSCRIPT_FLAG_ON" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + ON
- + </message>
- + <message name="IDS_OPTION_USERSCRIPT_FLAG_OFF" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + OFF
- + </message>
- +
- + <message name="IDS_ADD_SCRIPT" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + Add script
- + </message>
- + <message name="IDS_SCRIPTS_LIST_DESCRIPTION" desc="." formatter_data="android_java">
- + Experimental support for Greasemonkey-style user scripts.
- + </message>
- +
- + <message name="IDS_SCRIPTS_ITEM_VERSION" desc="." formatter_data="android_java">
- + Version:
- + </message>
- + <message name="IDS_SCRIPTS_ITEM_FILENAME" desc="." formatter_data="android_java">
- + File:
- + </message>
- + <message name="IDS_SCRIPTS_ITEM_URL" desc="." formatter_data="android_java">
- + Url:
- + </message>
- +
- + <message name="IDS_SCRIPTS_VIEW_SOURCE" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + View source
- + </message>
- +
- + <message name="IDS_ASK_TO_INSTALL" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + Do you want to install this user script from following location?
- +
- +<ph name="FILE">%s</ph>
- +
- +NOTE: only install user scripts that you have verified are secure, user scripts can steal your credentials and data.
- + </message>
- + <message name="IDS_YES" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + Yes
- + </message>
- + <message name="IDS_NO" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
- + No
- + </message>
- +
- +</grit-part>
- diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
- --- a/tools/gritsettings/resource_ids.spec
- +++ b/tools/gritsettings/resource_ids.spec
- @@ -642,6 +642,12 @@
- "components/autofill/core/browser/autofill_address_rewriter_resources.grd":{
- "includes": [3720]
- },
- + "components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd": {
- + "includes": [6000],
- + },
- + "components/user_scripts/browser/resources/browser_resources.grd": {
- + "includes": [6020],
- + },
- # END components/ section.
-
- # START ios/ section.
- --
- 2.25.1
|