| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496 |
- (function () {
- 'use strict';
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser */
- const STATE_DOWNLOAD_COMPLETE = "complete";
- const STATE_DOWNLOAD_INTERRUPTED = "interrupted";
- const STATE_ERROR_CANCELED_CHROMIUM = "USER_CANCELED";
- const ERROR_DOWNLOAD_CANCELED_GECKO = "canceled";
- const ERROR_CONFLICT_ACTION_GECKO = "conflictaction prompt not yet implemented";
- const ERROR_INCOGNITO_GECKO = "'incognito'";
- const ERROR_INCOGNITO_GECKO_ALT = "\"incognito\"";
- const ERROR_INVALID_FILENAME_GECKO = "illegal characters";
- const ERROR_INVALID_FILENAME_CHROMIUM = "invalid filename";
- async function download(downloadInfo, replacementCharacter) {
- let downloadId;
- try {
- downloadId = await browser.downloads.download(downloadInfo);
- } catch (error) {
- if (error.message) {
- const errorMessage = error.message.toLowerCase();
- const invalidFilename = errorMessage.includes(ERROR_INVALID_FILENAME_GECKO) || errorMessage.includes(ERROR_INVALID_FILENAME_CHROMIUM);
- if (invalidFilename && downloadInfo.filename.startsWith(".")) {
- downloadInfo.filename = replacementCharacter + downloadInfo.filename;
- return download(downloadInfo, replacementCharacter);
- } else if (invalidFilename && downloadInfo.filename.includes(",")) {
- downloadInfo.filename = downloadInfo.filename.replace(/,/g, replacementCharacter);
- return download(downloadInfo, replacementCharacter);
- } else if (invalidFilename && downloadInfo.filename.match(/\u200C|\u200D|\u200E|\u200F/)) {
- downloadInfo.filename = downloadInfo.filename.replace(/\u200C|\u200D|\u200E|\u200F/g, replacementCharacter);
- return download(downloadInfo, replacementCharacter);
- } else if (invalidFilename && !downloadInfo.filename.match(/^[\x00-\x7F]+$/)) { // eslint-disable-line no-control-regex
- downloadInfo.filename = downloadInfo.filename.replace(/[^\x00-\x7F]+/g, replacementCharacter); // eslint-disable-line no-control-regex
- return download(downloadInfo, replacementCharacter);
- } else if ((errorMessage.includes(ERROR_INCOGNITO_GECKO) || errorMessage.includes(ERROR_INCOGNITO_GECKO_ALT)) && downloadInfo.incognito) {
- delete downloadInfo.incognito;
- return download(downloadInfo, replacementCharacter);
- } else if (errorMessage == ERROR_CONFLICT_ACTION_GECKO && downloadInfo.conflictAction) {
- delete downloadInfo.conflictAction;
- return download(downloadInfo, replacementCharacter);
- } else if (errorMessage.includes(ERROR_DOWNLOAD_CANCELED_GECKO)) {
- return {};
- } else {
- throw error;
- }
- } else {
- throw error;
- }
- }
- return new Promise((resolve, reject) => {
- browser.downloads.onChanged.addListener(onChanged);
- function onChanged(event) {
- if (event.id == downloadId && event.state) {
- if (event.state.current == STATE_DOWNLOAD_COMPLETE) {
- browser.downloads.search({ id: downloadId })
- .then(downloadItems => resolve({ filename: downloadItems[0] && downloadItems[0].filename }))
- .catch(() => resolve({}));
- browser.downloads.onChanged.removeListener(onChanged);
- }
- if (event.state.current == STATE_DOWNLOAD_INTERRUPTED) {
- if (event.error && event.error.current == STATE_ERROR_CANCELED_CHROMIUM) {
- resolve({});
- } else {
- reject(new Error(event.state.current));
- }
- browser.downloads.onChanged.removeListener(onChanged);
- }
- }
- }
- });
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, setTimeout */
- let persistentData, temporaryData, cleanedUp;
- setTimeout(() => getPersistent().then(tabsData => persistentData = tabsData), 0);
- function onMessage$e(message) {
- if (message.method.endsWith(".get")) {
- return getPersistent();
- }
- if (message.method.endsWith(".set")) {
- return setPersistent(message.tabsData);
- }
- }
- async function onTabReplaced$3(addedTabId, removedTabId) {
- let tabsData = await getPersistent();
- await updateTabsData(tabsData, addedTabId, removedTabId);
- setPersistent(tabsData);
- await updateTabsData(temporaryData, addedTabId, removedTabId);
- }
- async function updateTabsData(tabsData, addedTabId, removedTabId) {
- if (tabsData[removedTabId] && !tabsData[addedTabId]) {
- tabsData[addedTabId] = tabsData[removedTabId];
- delete tabsData[removedTabId];
- }
- }
- async function remove(tabId) {
- if (temporaryData) {
- delete temporaryData[tabId];
- }
- const tabsData = await getPersistent();
- if (tabsData[tabId]) {
- const autoSave = tabsData[tabId].autoSave;
- tabsData[tabId] = { autoSave };
- await setPersistent(tabsData);
- }
- }
- function getTemporary(desiredTabId) {
- if (!temporaryData) {
- temporaryData = {};
- }
- if (desiredTabId !== undefined && !temporaryData[desiredTabId]) {
- temporaryData[desiredTabId] = {};
- }
- return temporaryData;
- }
- async function getPersistent(desiredTabId) {
- if (!persistentData) {
- const config = await browser.storage.local.get();
- persistentData = config.tabsData || {};
- }
- cleanup();
- if (desiredTabId !== undefined && !persistentData[desiredTabId]) {
- persistentData[desiredTabId] = {};
- }
- return persistentData;
- }
- async function setPersistent(tabsData) {
- persistentData = tabsData;
- await browser.storage.local.set({ tabsData });
- }
- async function cleanup() {
- if (!cleanedUp) {
- cleanedUp = true;
- const tabs = await browser.tabs.query({ currentWindow: true, highlighted: true });
- Object.keys(persistentData).filter(key => {
- if (key != "autoSaveAll" && key != "autoSaveUnpinned" && key != "profileName") {
- return !tabs.find(tab => tab.id == key);
- }
- }).forEach(tabId => delete persistentData[tabId]);
- await browser.storage.local.set({ tabsData: persistentData });
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const CURRENT_PROFILE_NAME = "-";
- const DEFAULT_PROFILE_NAME = "__Default_Settings__";
- const DISABLED_PROFILE_NAME = "__Disabled_Settings__";
- const REGEXP_RULE_PREFIX = "regexp:";
- const PROFILE_NAME_PREFIX = "profile_";
- const IS_NOT_SAFARI = !/Safari/.test(navigator.userAgent) || /Chrome/.test(navigator.userAgent) || /Vivaldi/.test(navigator.userAgent) || /OPR/.test(navigator.userAgent);
- const BACKGROUND_SAVE_SUPPORTED = !(/Mobile.*Firefox/.test(navigator.userAgent) || /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/Vivaldi/.test(navigator.userAgent) && !/OPR/.test(navigator.userAgent));
- const BADGE_COLOR_SUPPORTED = IS_NOT_SAFARI;
- const AUTO_SAVE_SUPPORTED = IS_NOT_SAFARI;
- const SELECTABLE_TABS_SUPPORTED = IS_NOT_SAFARI;
- const AUTO_OPEN_EDITOR_SUPPORTED = IS_NOT_SAFARI;
- const INFOBAR_SUPPORTED = IS_NOT_SAFARI;
- const BOOKMARKS_API_SUPPORTED = IS_NOT_SAFARI;
- const IDENTITY_API_SUPPORTED = IS_NOT_SAFARI;
- const CLIPBOARD_API_SUPPORTED = IS_NOT_SAFARI;
- const NATIVE_API_API_SUPPORTED = IS_NOT_SAFARI;
- const WEB_BLOCKING_API_SUPPORTED = IS_NOT_SAFARI;
- const SHARE_API_SUPPORTED = navigator.canShare && navigator.canShare({ files: [new File([new Blob([""], { type: "text/html" })], "test.html")] });
- const DEFAULT_CONFIG = {
- removeHiddenElements: true,
- removeUnusedStyles: true,
- removeUnusedFonts: true,
- removeFrames: false,
- compressHTML: true,
- compressCSS: false,
- loadDeferredImages: true,
- loadDeferredImagesMaxIdleTime: 1500,
- loadDeferredImagesBlockCookies: false,
- loadDeferredImagesBlockStorage: false,
- loadDeferredImagesKeepZoomLevel: false,
- loadDeferredImagesDispatchScrollEvent: false,
- loadDeferredImagesBeforeFrames: false,
- filenameTemplate: "%if-empty<{page-title}|No title> ({date-locale} {time-locale}).{filename-extension}",
- infobarTemplate: "",
- includeInfobar: !IS_NOT_SAFARI,
- confirmInfobarContent: false,
- autoClose: false,
- confirmFilename: false,
- filenameConflictAction: "uniquify",
- filenameMaxLength: 192,
- filenameMaxLengthUnit: "bytes",
- filenameReplacedCharacters: ["~", "+", "\\\\", "?", "%", "*", ":", "|", "\"", "<", ">", "\x00-\x1f", "\x7F"],
- filenameReplacementCharacter: "_",
- replaceEmojisInFilename: false,
- saveFilenameTemplateData: false,
- contextMenuEnabled: true,
- tabMenuEnabled: true,
- browserActionMenuEnabled: true,
- shadowEnabled: true,
- logsEnabled: true,
- progressBarEnabled: true,
- maxResourceSizeEnabled: false,
- maxResourceSize: 10,
- displayInfobar: true,
- displayStats: false,
- backgroundSave: BACKGROUND_SAVE_SUPPORTED,
- defaultEditorMode: "normal",
- applySystemTheme: true,
- autoSaveDelay: 1,
- autoSaveLoad: false,
- autoSaveUnload: false,
- autoSaveLoadOrUnload: true,
- autoSaveDiscard: false,
- autoSaveRemove: false,
- autoSaveRepeat: false,
- autoSaveRepeatDelay: 10,
- removeAlternativeFonts: true,
- removeAlternativeMedias: true,
- removeAlternativeImages: true,
- groupDuplicateImages: true,
- maxSizeDuplicateImages: 512 * 1024,
- saveRawPage: false,
- saveToClipboard: false,
- addProof: false,
- saveToGDrive: false,
- saveToDropbox: false,
- saveWithWebDAV: false,
- webDAVURL: "",
- webDAVUser: "",
- webDAVPassword: "",
- saveToGitHub: false,
- saveToRestFormApi: false,
- githubToken: "",
- githubUser: "",
- githubRepository: "SingleFile-Archives",
- githubBranch: "main",
- saveWithCompanion: false,
- sharePage: false,
- forceWebAuthFlow: false,
- resolveFragmentIdentifierURLs: false,
- userScriptEnabled: false,
- openEditor: false,
- openSavedPage: false,
- autoOpenEditor: false,
- saveCreatedBookmarks: false,
- allowedBookmarkFolders: [],
- ignoredBookmarkFolders: [],
- replaceBookmarkURL: true,
- saveFavicon: true,
- includeBOM: false,
- warnUnsavedPage: true,
- displayInfobarInEditor: false,
- compressContent: false,
- createRootDirectory: false,
- selfExtractingArchive: true,
- extractDataFromPage: true,
- preventAppendedData: false,
- insertEmbeddedImage: false,
- insertEmbeddedScreenshotImage: false,
- insertTextBody: false,
- autoSaveExternalSave: false,
- insertMetaNoIndex: false,
- insertMetaCSP: true,
- passReferrerOnError: false,
- password: "",
- insertSingleFileComment: true,
- removeSavedDate: false,
- blockMixedContent: false,
- saveOriginalURLs: false,
- acceptHeaders: {
- font: "application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8",
- image: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
- stylesheet: "text/css,*/*;q=0.1",
- script: "*/*",
- document: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
- video: "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
- audio: "audio/webm,audio/ogg,audio/wav,audio/*;q=0.9,application/ogg;q=0.7,video/*;q=0.6,*/*;q=0.5"
- },
- moveStylesInHead: false,
- networkTimeout: 0,
- woleetKey: "",
- blockImages: false,
- blockStylesheets: false,
- blockFonts: false,
- blockScripts: true,
- blockVideos: true,
- blockAudios: true,
- delayBeforeProcessing: 0,
- _migratedTemplateFormat: true,
- saveToRestFormApiUrl: "",
- saveToRestFormApiFileFieldName: "",
- saveToRestFormApiUrlFieldName: "",
- saveToRestFormApiToken: "",
- };
- const DEFAULT_RULES = [{
- "url": "file:",
- "profile": "__Default_Settings__",
- "autoSaveProfile": "__Disabled_Settings__"
- }];
- const MIGRATION_DEFAULT_VARIABLES_VALUES = {
- "page-title": "No title",
- "page-heading": "No heading",
- "page-language": "No language",
- "page-description": "No description",
- "page-author": "No author",
- "page-creator": "No creator",
- "page-publisher": "No publisher",
- "url-hash": "No hash",
- "url-host": "No host",
- "url-hostname": "No hostname",
- "url-href": "No href",
- "url-href-digest-sha-1": "No hash",
- "url-href-flat": "No href",
- "url-referrer": "No referrer",
- "url-referrer-flat": "No referrer",
- "url-password": "No password",
- "url-pathname": "No pathname",
- "url-pathname-flat": "No pathname",
- "url-port": "No port",
- "url-protocol": "No protocol",
- "url-search": "No search",
- "url-username": "No username",
- "tab-id": "No tab id",
- "tab-index": "No tab index",
- "url-last-segment": "No last segment"
- };
- let configStorage;
- let pendingUpgradePromise = upgrade();
- async function upgrade() {
- const { sync } = await browser.storage.local.get();
- if (sync) {
- configStorage = browser.storage.sync;
- } else {
- configStorage = browser.storage.local;
- }
- const config = await configStorage.get();
- if (!config[PROFILE_NAME_PREFIX + DEFAULT_PROFILE_NAME]) {
- if (config.profiles) {
- const profileNames = Object.keys(config.profiles);
- for (const profileName of profileNames) {
- await setProfile(profileName, config.profiles[profileName]);
- }
- } else {
- await setProfile(DEFAULT_PROFILE_NAME, DEFAULT_CONFIG);
- }
- } else if (config.profiles) {
- await configStorage.remove(["profiles"]);
- }
- if (!config.rules) {
- await configStorage.set({ rules: DEFAULT_RULES });
- }
- if (!config.maxParallelWorkers) {
- await configStorage.set({ maxParallelWorkers: navigator.hardwareConcurrency || 4 });
- }
- if (!config.processInForeground) {
- await configStorage.set({ processInForeground: false });
- }
- const profileNames = await getProfileNames();
- profileNames.map(async profileName => {
- const profile = await getProfile(profileName);
- if (!profile._migratedTemplateFormat) {
- profile.filenameTemplate = updateFilenameTemplate(profile.filenameTemplate);
- profile._migratedTemplateFormat = true;
- }
- for (const key of Object.keys(DEFAULT_CONFIG)) {
- if (profile[key] === undefined) {
- profile[key] = DEFAULT_CONFIG[key];
- }
- }
- await setProfile(profileName, profile);
- });
- }
- function updateFilenameTemplate(template) {
- try {
- Object.keys(MIGRATION_DEFAULT_VARIABLES_VALUES).forEach(variable => {
- const value = MIGRATION_DEFAULT_VARIABLES_VALUES[variable];
- template = template.replaceAll(`{${variable}}`, `%if-empty<{${variable}}|${value}>`);
- });
- return template;
- } catch (_error) {
- // ignored
- }
- }
- async function getRule(url, ignoreWildcard) {
- const { rules } = await configStorage.get(["rules"]);
- const regExpRules = rules.filter(rule => testRegExpRule(rule));
- let rule = regExpRules.sort(sortRules).find(rule => url && url.match(new RegExp(rule.url.split(REGEXP_RULE_PREFIX)[1])));
- if (!rule) {
- const normalRules = rules.filter(rule => !testRegExpRule(rule));
- rule = normalRules.sort(sortRules).find(rule => (!ignoreWildcard && rule.url == "*") || (url && url.includes(rule.url)));
- }
- return rule;
- }
- async function getConfig() {
- await pendingUpgradePromise;
- const { maxParallelWorkers, processInForeground } = await configStorage.get(["maxParallelWorkers", "processInForeground"]);
- const rules = await getRules();
- const profiles = await getProfiles();
- return { profiles, rules, maxParallelWorkers, processInForeground };
- }
- function sortRules(ruleLeft, ruleRight) {
- return ruleRight.url.length - ruleLeft.url.length;
- }
- function testRegExpRule(rule) {
- return rule.url.toLowerCase().startsWith(REGEXP_RULE_PREFIX);
- }
- async function onMessage$d(message) {
- if (message.method.endsWith(".deleteRules")) {
- await deleteRules(message.profileName);
- }
- if (message.method.endsWith(".deleteRule")) {
- await deleteRule(message.url);
- }
- if (message.method.endsWith(".addRule")) {
- await addRule(message.url, message.profileName, message.autoSaveProfileName);
- }
- if (message.method.endsWith(".createProfile")) {
- await createProfile(message.profileName, message.fromProfileName || DEFAULT_PROFILE_NAME);
- }
- if (message.method.endsWith(".renameProfile")) {
- await renameProfile(message.profileName, message.newProfileName);
- }
- if (message.method.endsWith(".deleteProfile")) {
- await deleteProfile(message.profileName);
- }
- if (message.method.endsWith(".resetProfiles")) {
- await resetProfiles();
- }
- if (message.method.endsWith(".resetProfile")) {
- await resetProfile(message.profileName);
- }
- if (message.method.endsWith(".importConfig")) {
- await importConfig(message.config);
- }
- if (message.method.endsWith(".updateProfile")) {
- await updateProfile(message.profileName, message.profile);
- }
- if (message.method.endsWith(".updateRule")) {
- await updateRule(message.url, message.newUrl, message.profileName, message.autoSaveProfileName);
- }
- if (message.method.endsWith(".getConstants")) {
- return {
- DISABLED_PROFILE_NAME,
- DEFAULT_PROFILE_NAME,
- CURRENT_PROFILE_NAME,
- BACKGROUND_SAVE_SUPPORTED,
- BADGE_COLOR_SUPPORTED,
- AUTO_SAVE_SUPPORTED,
- SELECTABLE_TABS_SUPPORTED,
- AUTO_OPEN_EDITOR_SUPPORTED,
- INFOBAR_SUPPORTED,
- BOOKMARKS_API_SUPPORTED,
- IDENTITY_API_SUPPORTED,
- CLIPBOARD_API_SUPPORTED,
- NATIVE_API_API_SUPPORTED,
- WEB_BLOCKING_API_SUPPORTED,
- SHARE_API_SUPPORTED
- };
- }
- if (message.method.endsWith(".getRules")) {
- return getRules();
- }
- if (message.method.endsWith(".getProfiles")) {
- return getProfiles();
- }
- if (message.method.endsWith(".exportConfig")) {
- return exportConfig();
- }
- if (message.method.endsWith(".enableSync")) {
- await browser.storage.local.set({ sync: true });
- const syncConfig = await browser.storage.sync.get();
- if (!syncConfig || !syncConfig.rules) {
- const profileKeyNames = await getProfileKeyNames();
- const localConfig = await browser.storage.local.get(["rules", "maxParallelWorkers", "processInForeground", ...profileKeyNames]);
- await browser.storage.sync.set(localConfig);
- }
- configStorage = browser.storage.sync;
- await upgrade();
- return {};
- }
- if (message.method.endsWith(".disableSync")) {
- await browser.storage.local.set({ sync: false });
- const syncConfig = await browser.storage.sync.get();
- const localConfig = await browser.storage.local.get();
- if (syncConfig && syncConfig.rules && (!localConfig || !localConfig.rules)) {
- await browser.storage.local.set({ rules: syncConfig.rules, maxParallelWorkers: syncConfig.maxParallelWorkers, processInForeground: syncConfig.processInForeground });
- const profiles = {};
- // syncConfig.profileNames.forEach(profileKeyName => profiles[PROFILE_NAME_PREFIX + profileKeyName] = syncConfig[profileKeyName]);
- await browser.storage.local.set(profiles);
- }
- configStorage = browser.storage.local;
- await upgrade();
- return {};
- }
- if (message.method.endsWith(".isSync")) {
- return { sync: (await browser.storage.local.get()).sync };
- }
- return {};
- }
- async function createProfile(profileName, fromProfileName) {
- const profileNames = await getProfileNames();
- if (profileNames.includes(profileName)) {
- throw new Error("Duplicate profile name");
- }
- const profileFrom = await getProfile(fromProfileName);
- const profile = JSON.parse(JSON.stringify(profileFrom));
- await setProfile(profileName, profile);
- }
- async function getProfiles() {
- await pendingUpgradePromise;
- const profileKeyNames = await getProfileKeyNames();
- const profiles = await configStorage.get(profileKeyNames);
- const result = {};
- Object.keys(profiles).forEach(profileName => result[profileName.substring(PROFILE_NAME_PREFIX.length)] = profiles[profileName]);
- return result;
- }
- async function getOptions(url, autoSave) {
- await pendingUpgradePromise;
- const [rule, allTabsData] = await Promise.all([getRule(url), getPersistent()]);
- const tabProfileName = allTabsData.profileName || DEFAULT_PROFILE_NAME;
- let selectedProfileName;
- if (rule) {
- const profileName = rule[autoSave ? "autoSaveProfile" : "profile"];
- selectedProfileName = profileName == CURRENT_PROFILE_NAME ? tabProfileName : profileName;
- } else {
- selectedProfileName = tabProfileName;
- }
- const profile = await getProfile(selectedProfileName);
- return Object.assign({ profileName: selectedProfileName }, profile);
- }
- async function updateProfile(profileName, profile) {
- const profileNames = await getProfileNames();
- if (!profileNames.includes(profileName)) {
- throw new Error("Profile not found");
- }
- const previousProfile = await getProfile(profileName);
- Object.keys(previousProfile).forEach(key => {
- profile[key] = profile[key] === undefined ? previousProfile[key] : profile[key];
- });
- await setProfile(profileName, profile);
- }
- async function renameProfile(oldProfileName, profileName) {
- const profileNames = await getProfileNames();
- const allTabsData = await getPersistent();
- const rules = await getRules();
- if (!profileNames.includes(oldProfileName)) {
- throw new Error("Profile not found");
- }
- if (profileNames.includes(profileName)) {
- throw new Error("Duplicate profile name");
- }
- if (oldProfileName == DEFAULT_PROFILE_NAME) {
- throw new Error("Default settings cannot be renamed");
- }
- if (allTabsData.profileName == oldProfileName) {
- allTabsData.profileName = profileName;
- await setPersistent(allTabsData);
- }
- rules.forEach(rule => {
- if (rule.profile == oldProfileName) {
- rule.profile = profileName;
- }
- if (rule.autoSaveProfile == oldProfileName) {
- rule.autoSaveProfile = profileName;
- }
- });
- const profile = await getProfile(oldProfileName);
- await configStorage.remove([PROFILE_NAME_PREFIX + oldProfileName]);
- await configStorage.set({ [PROFILE_NAME_PREFIX + profileName]: profile, rules });
- }
- async function deleteProfile(profileName) {
- const profileNames = await getProfileNames();
- const allTabsData = await getPersistent();
- const rules = await getRules();
- if (!profileNames.includes(profileName)) {
- throw new Error("Profile not found");
- }
- if (profileName == DEFAULT_PROFILE_NAME) {
- throw new Error("Default settings cannot be deleted");
- }
- if (allTabsData.profileName == profileName) {
- delete allTabsData.profileName;
- await setPersistent(allTabsData);
- }
- rules.forEach(rule => {
- if (rule.profile == profileName) {
- rule.profile = DEFAULT_PROFILE_NAME;
- }
- if (rule.autoSaveProfile == profileName) {
- rule.autoSaveProfile = DEFAULT_PROFILE_NAME;
- }
- });
- configStorage.remove([PROFILE_NAME_PREFIX + profileName]);
- await configStorage.set({ rules });
- }
- async function getRules() {
- return (await configStorage.get(["rules"])).rules;
- }
- async function getProfileNames() {
- return Object.keys(await configStorage.get()).filter(key => key.startsWith(PROFILE_NAME_PREFIX)).map(key => key.substring(PROFILE_NAME_PREFIX.length));
- }
- async function getProfileKeyNames() {
- return Object.keys(await configStorage.get()).filter(key => key.startsWith(PROFILE_NAME_PREFIX));
- }
- async function getProfile(profileName) {
- const profileKey = PROFILE_NAME_PREFIX + profileName;
- const data = await configStorage.get([profileKey]);
- return data[profileKey];
- }
- async function setProfile(profileName, profileData) {
- const profileKey = PROFILE_NAME_PREFIX + profileName;
- await configStorage.set({ [profileKey]: profileData });
- }
- async function addRule(url, profile, autoSaveProfile) {
- if (!url) {
- throw new Error("URL is empty");
- }
- const rules = await getRules();
- if (rules.find(rule => rule.url == url)) {
- throw new Error("URL already exists");
- }
- rules.push({
- url,
- profile,
- autoSaveProfile
- });
- await configStorage.set({ rules });
- }
- async function deleteRule(url) {
- if (!url) {
- throw new Error("URL is empty");
- }
- const rules = await getRules();
- await configStorage.set({ rules: rules.filter(rule => rule.url != url) });
- }
- async function deleteRules(profileName) {
- const rules = await getRules();
- await configStorage.set({ rules: profileName ? rules.filter(rule => rule.autoSaveProfile != profileName && rule.profile != profileName) : [] });
- }
- async function updateRule(url, newURL, profile, autoSaveProfile) {
- if (!url || !newURL) {
- throw new Error("URL is empty");
- }
- const rules = await getRules();
- const urlConfig = rules.find(rule => rule.url == url);
- if (!urlConfig) {
- throw new Error("URL not found");
- }
- if (rules.find(rule => rule.url == newURL && rule.url != url)) {
- throw new Error("New URL already exists");
- }
- urlConfig.url = newURL;
- urlConfig.profile = profile;
- urlConfig.autoSaveProfile = autoSaveProfile;
- await configStorage.set({ rules });
- }
- async function getAuthInfo$1() {
- return (await configStorage.get()).authInfo;
- }
- async function getDropboxAuthInfo$1() {
- return (await configStorage.get()).dropboxAuthInfo;
- }
- async function setAuthInfo(authInfo) {
- await configStorage.set({ authInfo });
- }
- async function setDropboxAuthInfo(authInfo) {
- await configStorage.set({ dropboxAuthInfo: authInfo });
- }
- async function removeAuthInfo() {
- let authInfo = getAuthInfo$1();
- if (authInfo.revokableAccessToken) {
- setAuthInfo({ revokableAccessToken: authInfo.revokableAccessToken });
- } else {
- await configStorage.remove(["authInfo"]);
- }
- }
- async function removeDropboxAuthInfo() {
- let authInfo = getDropboxAuthInfo$1();
- if (authInfo.revokableAccessToken) {
- setDropboxAuthInfo({ revokableAccessToken: authInfo.revokableAccessToken });
- } else {
- await configStorage.remove(["dropboxAuthInfo"]);
- }
- }
- async function resetProfiles() {
- await pendingUpgradePromise;
- const allTabsData = await getPersistent();
- delete allTabsData.profileName;
- await setPersistent(allTabsData);
- let profileKeyNames = await getProfileKeyNames();
- await configStorage.remove([...profileKeyNames, "rules", "maxParallelWorkers", "processInForeground"]);
- await upgrade();
- }
- async function resetProfile(profileName) {
- const profileNames = await getProfileNames();
- if (!profileNames.includes(profileName)) {
- throw new Error("Profile not found");
- }
- await setProfile(profileName, DEFAULT_CONFIG);
- }
- async function exportConfig() {
- const config = await getConfig();
- const textContent = JSON.stringify({ profiles: config.profiles, rules: config.rules, maxParallelWorkers: config.maxParallelWorkers, processInForeground: config.processInForeground }, null, 2);
- const filename = `singlefile-settings-${(new Date()).toISOString().replace(/:/g, "_")}.json`;
- if (BACKGROUND_SAVE_SUPPORTED) {
- const url = URL.createObjectURL(new Blob([textContent], { type: "text/json" }));
- try {
- await download({
- url,
- filename,
- saveAs: true
- }, "_");
- } finally {
- URL.revokeObjectURL(url);
- }
- return {};
- } else {
- return {
- filename,
- textContent
- };
- }
- }
- async function importConfig(config) {
- const profileNames = await getProfileNames();
- const profileKeyNames = await getProfileKeyNames();
- const allTabsData = await getPersistent();
- if (profileNames.includes(allTabsData.profileName)) {
- delete allTabsData.profileName;
- await setPersistent(allTabsData);
- }
- await configStorage.remove([...profileKeyNames, "rules", "maxParallelWorkers", "processInForeground"]);
- const newConfig = { rules: config.rules, maxParallelWorkers: config.maxParallelWorkers, processInForeground: config.processInForeground };
- Object.keys(config.profiles).forEach(profileName => newConfig[PROFILE_NAME_PREFIX + profileName] = config.profiles[profileName]);
- await configStorage.set(newConfig);
- await upgrade();
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- async function autoSaveIsEnabled(tab) {
- if (tab) {
- const [allTabsData, rule] = await Promise.all([getPersistent(), getRule(tab.url)]);
- return Boolean(allTabsData.autoSaveAll ||
- (allTabsData.autoSaveUnpinned && !tab.pinned) ||
- (allTabsData[tab.id] && allTabsData[tab.id].autoSave)) &&
- (!rule || rule.autoSaveProfile != DISABLED_PROFILE_NAME);
- }
- }
- async function refreshAutoSaveTabs() {
- const tabs = (await browser.tabs.query({}));
- return Promise.all(tabs.map(async tab => {
- const [options, autoSaveEnabled] = await Promise.all([getOptions(tab.url, true), autoSaveIsEnabled(tab)]);
- try {
- await browser.tabs.sendMessage(tab.id, { method: "content.init", autoSaveEnabled, options });
- } catch (error) {
- // ignored
- }
- }));
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- async function onMessage$c(message, sender) {
- if (message.method.endsWith(".init")) {
- const [optionsAutoSave, options, autoSaveEnabled] = await Promise.all([getOptions(sender.tab.url, true), getOptions(sender.tab.url), autoSaveIsEnabled(sender.tab)]);
- return { optionsAutoSave, options, autoSaveEnabled, tabId: sender.tab.id, tabIndex: sender.tab.index };
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const MAX_CONTENT_SIZE$1 = 32 * (1024 * 1024);
- const EDITOR_PAGE_URL = "/src/ui/pages/editor.html";
- const tabsData = new Map();
- const partialContents$1 = new Map();
- const EDITOR_URL = browser.runtime.getURL(EDITOR_PAGE_URL);
- async function open({ tabIndex, content, filename, compressContent, selfExtractingArchive, extractDataFromPage, insertTextBody, insertMetaCSP, embeddedImage }) {
- const createTabProperties = { active: true, url: EDITOR_PAGE_URL };
- if (tabIndex != null) {
- createTabProperties.index = tabIndex;
- }
- const tab = await browser.tabs.create(createTabProperties);
- tabsData.set(tab.id, {
- content,
- filename,
- compressContent,
- selfExtractingArchive,
- extractDataFromPage,
- insertTextBody,
- insertMetaCSP,
- embeddedImage
- });
- }
- function onTabRemoved$2(tabId) {
- tabsData.delete(tabId);
- }
- function isEditor(tab) {
- return tab.url == EDITOR_URL;
- }
- async function onMessage$b(message, sender) {
- if (message.method.endsWith(".getTabData")) {
- const tab = sender.tab;
- const tabData = tabsData.get(tab.id);
- if (tabData) {
- const options = await getOptions(tabData.url);
- const content = JSON.stringify(tabData);
- for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE$1 < content.length; blockIndex++) {
- const message = {
- method: "editor.setTabData",
- compressContent: tabData.compressContent
- };
- message.truncated = content.length > MAX_CONTENT_SIZE$1;
- if (message.truncated) {
- message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE$1 > content.length;
- message.content = content.substring(blockIndex * MAX_CONTENT_SIZE$1, (blockIndex + 1) * MAX_CONTENT_SIZE$1);
- if (message.finished) {
- message.options = options;
- }
- } else {
- message.content = content;
- options.embeddedImage = tabData.embeddedImage;
- message.options = options;
- }
- await browser.tabs.sendMessage(tab.id, message);
- }
- }
- return {};
- }
- if (message.method.endsWith(".open")) {
- let contents;
- const tab = sender.tab;
- if (message.truncated) {
- contents = partialContents$1.get(tab.id);
- if (!contents) {
- contents = [];
- partialContents$1.set(tab.id, contents);
- }
- contents.push(message.content);
- if (message.finished) {
- partialContents$1.delete(tab.id);
- }
- } else if (message.content) {
- contents = [message.content];
- }
- if (!message.truncated || message.finished) {
- const updateTabProperties = { url: EDITOR_PAGE_URL };
- await browser.tabs.update(tab.id, updateTabProperties);
- const content = message.compressContent ? contents.flat() : contents.join("");
- tabsData.set(tab.id, {
- url: tab.url,
- content,
- filename: message.filename,
- compressContent: message.compressContent,
- selfExtractingArchive: message.selfExtractingArchive,
- extractDataFromPageTags: message.extractDataFromPageTags,
- insertTextBody: message.insertTextBody,
- insertMetaCSP: message.insertMetaCSP,
- embeddedImage: message.embeddedImage
- });
- }
- return {};
- }
- if (message.method.endsWith(".ping")) {
- return {};
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, XMLHttpRequest */
- const referrers = new Map();
- const REQUEST_ID_HEADER_NAME = "x-single-file-request-id";
- const MAX_CONTENT_SIZE = 8 * (1024 * 1024);
- browser.runtime.onMessage.addListener((message, sender) => {
- if (message.method && message.method.startsWith("singlefile.fetch")) {
- return new Promise(resolve => {
- onRequest(message, sender)
- .then(resolve)
- .catch(error => resolve({ error: error && error.toString() }));
- });
- }
- });
- async function onRequest(message, sender) {
- if (message.method == "singlefile.fetch") {
- try {
- const response = await fetchResource$1(message.url, { referrer: message.referrer, headers: message.headers });
- return sendResponse(sender.tab.id, message.requestId, response);
- } catch (error) {
- return sendResponse(sender.tab.id, message.requestId, { error: error.message, array: [] });
- }
- } else if (message.method == "singlefile.fetchFrame") {
- return browser.tabs.sendMessage(sender.tab.id, message);
- }
- }
- async function sendResponse(tabId, requestId, response) {
- for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE <= response.array.length; blockIndex++) {
- const message = {
- method: "singlefile.fetchResponse",
- requestId,
- headers: response.headers,
- status: response.status,
- error: response.error
- };
- message.truncated = response.array.length > MAX_CONTENT_SIZE;
- if (message.truncated) {
- message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > response.array.length;
- message.array = response.array.slice(blockIndex * MAX_CONTENT_SIZE, (blockIndex + 1) * MAX_CONTENT_SIZE);
- } else {
- message.array = response.array;
- }
- await browser.tabs.sendMessage(tabId, message);
- }
- return {};
- }
- function fetchResource$1(url, options = {}, includeRequestId) {
- return new Promise((resolve, reject) => {
- const xhrRequest = new XMLHttpRequest();
- xhrRequest.withCredentials = true;
- xhrRequest.responseType = "arraybuffer";
- xhrRequest.onerror = event => reject(new Error(event.detail));
- xhrRequest.onreadystatechange = () => {
- if (xhrRequest.readyState == XMLHttpRequest.DONE) {
- if (xhrRequest.status || xhrRequest.response.byteLength) {
- if ((xhrRequest.status == 401 || xhrRequest.status == 403 || xhrRequest.status == 404) && !includeRequestId) {
- fetchResource$1(url, options, true)
- .then(resolve)
- .catch(reject);
- } else {
- resolve({
- arrayBuffer: xhrRequest.response,
- array: Array.from(new Uint8Array(xhrRequest.response)),
- headers: { "content-type": xhrRequest.getResponseHeader("Content-Type") },
- status: xhrRequest.status
- });
- }
- } else {
- reject(new Error("Empty response"));
- }
- }
- };
- xhrRequest.open("GET", url, true);
- if (options.headers) {
- for (const entry of Object.entries(options.headers)) {
- xhrRequest.setRequestHeader(entry[0], entry[1]);
- }
- }
- if (includeRequestId) {
- const randomId = String(Math.random()).substring(2);
- setReferrer(randomId, options.referrer);
- xhrRequest.setRequestHeader(REQUEST_ID_HEADER_NAME, randomId);
- }
- xhrRequest.send();
- });
- }
- function setReferrer(requestId, referrer) {
- referrers.set(requestId, referrer);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- let referrerOnErrorEnabled = false;
- function onMessage$a(message) {
- if (message.method.endsWith(".enableReferrerOnError")) {
- enableReferrerOnError();
- return {};
- }
- if (message.method.endsWith(".disableReferrerOnError")) {
- disableReferrerOnError();
- return {};
- }
- }
- function injectRefererHeader(details) {
- if (referrerOnErrorEnabled) {
- let requestIdHeader = details.requestHeaders.find(header => header.name === REQUEST_ID_HEADER_NAME);
- if (requestIdHeader) {
- details.requestHeaders = details.requestHeaders.filter(header => header.name !== REQUEST_ID_HEADER_NAME);
- const referrer = referrers.get(requestIdHeader.value);
- if (referrer) {
- referrers.delete(requestIdHeader.value);
- const header = details.requestHeaders.find(header => header.name.toLowerCase() === "referer");
- if (!header) {
- details.requestHeaders.push({ name: "Referer", value: referrer });
- return { requestHeaders: details.requestHeaders };
- }
- }
- }
- }
- }
- function enableReferrerOnError() {
- if (!referrerOnErrorEnabled) {
- try {
- browser.webRequest.onBeforeSendHeaders.addListener(injectRefererHeader, { urls: ["<all_urls>"] }, ["blocking", "requestHeaders", "extraHeaders"]);
- } catch (error) {
- browser.webRequest.onBeforeSendHeaders.addListener(injectRefererHeader, { urls: ["<all_urls>"] }, ["blocking", "requestHeaders"]);
- }
- referrerOnErrorEnabled = true;
- }
- }
- function disableReferrerOnError() {
- try {
- browser.webRequest.onBeforeSendHeaders.removeListener(injectRefererHeader);
- } catch (error) {
- // ignored
- }
- referrerOnErrorEnabled = false;
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- async function queryTabs$1(options) {
- const tabs = await browser.tabs.query(options);
- return tabs.sort((tab1, tab2) => tab1.index - tab2.index);
- }
- function extractAuthCode(authURL) {
- return new Promise((resolve, reject) => {
- browser.tabs.onUpdated.addListener(onTabUpdated);
- function onTabUpdated(tabId, changeInfo) {
- if (changeInfo && changeInfo.url && changeInfo.url.startsWith(authURL)) {
- browser.tabs.onUpdated.removeListener(onTabUpdated);
- const code = new URLSearchParams(new URL(changeInfo.url).search).get("code");
- if (code) {
- browser.tabs.remove(tabId);
- resolve(code);
- } else {
- reject();
- }
- }
- }
- });
- }
- async function launchWebAuthFlow(options) {
- const tab = await browser.tabs.create({ url: options.url, active: true });
- return new Promise((resolve, reject) => {
- browser.tabs.onRemoved.addListener(onTabRemoved);
- function onTabRemoved(tabId) {
- if (tabId == tab.id) {
- browser.tabs.onRemoved.removeListener(onTabRemoved);
- reject(new Error("code_required"));
- }
- }
- });
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const DEFAULT_ICON_PATH = "/src/ui/resources/icon_128.png";
- const WAIT_ICON_PATH_PREFIX = "/src/ui/resources/icon_128_wait";
- const BUTTON_DEFAULT_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonDefaultTooltip");
- const BUTTON_BLOCKED_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonBlockedTooltip");
- const BUTTON_DEFAULT_BADGE_MESSAGE = "";
- const BUTTON_INITIALIZING_BADGE_MESSAGE = browser.i18n.getMessage("buttonInitializingBadge");
- const BUTTON_INITIALIZING_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonInitializingTooltip");
- const BUTTON_ERROR_BADGE_MESSAGE = browser.i18n.getMessage("buttonErrorBadge");
- const BUTTON_BLOCKED_BADGE_MESSAGE = browser.i18n.getMessage("buttonBlockedBadge");
- const BUTTON_OK_BADGE_MESSAGE = browser.i18n.getMessage("buttonOKBadge");
- const BUTTON_SAVE_PROGRESS_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonSaveProgressTooltip");
- const BUTTON_UPLOAD_PROGRESS_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonUploadProgressTooltip");
- const BUTTON_AUTOSAVE_ACTIVE_BADGE_MESSAGE = browser.i18n.getMessage("buttonAutoSaveActiveBadge");
- const BUTTON_AUTOSAVE_ACTIVE_TOOLTIP_MESSAGE = browser.i18n.getMessage("buttonAutoSaveActiveTooltip");
- const DEFAULT_COLOR = [2, 147, 20, 192];
- const ACTIVE_COLOR = [4, 229, 36, 192];
- const FORBIDDEN_COLOR = [255, 255, 255, 1];
- const ERROR_COLOR = [229, 4, 12, 192];
- const AUTOSAVE_DEFAULT_COLOR = [208, 208, 208, 192];
- const AUTOSAVE_INITIALIZING_COLOR = [64, 64, 64, 192];
- const INJECT_SCRIPTS_STEP$1 = 1;
- const BUTTON_STATES = {
- default: {
- setBadgeBackgroundColor: { color: DEFAULT_COLOR },
- setBadgeText: { text: BUTTON_DEFAULT_BADGE_MESSAGE },
- setTitle: { title: BUTTON_DEFAULT_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- inject: {
- setBadgeBackgroundColor: { color: DEFAULT_COLOR },
- setBadgeText: { text: BUTTON_INITIALIZING_BADGE_MESSAGE },
- setTitle: { title: BUTTON_INITIALIZING_TOOLTIP_MESSAGE },
- },
- execute: {
- setBadgeBackgroundColor: { color: ACTIVE_COLOR },
- setBadgeText: { text: BUTTON_INITIALIZING_BADGE_MESSAGE },
- },
- progress: {
- setBadgeBackgroundColor: { color: ACTIVE_COLOR },
- setBadgeText: { text: BUTTON_DEFAULT_BADGE_MESSAGE }
- },
- edit: {
- setBadgeBackgroundColor: { color: DEFAULT_COLOR },
- setBadgeText: { text: BUTTON_DEFAULT_BADGE_MESSAGE },
- setTitle: { title: BUTTON_DEFAULT_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- end: {
- setBadgeBackgroundColor: { color: ACTIVE_COLOR },
- setBadgeText: { text: BUTTON_OK_BADGE_MESSAGE },
- setTitle: { title: BUTTON_DEFAULT_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- error: {
- setBadgeBackgroundColor: { color: ERROR_COLOR },
- setBadgeText: { text: BUTTON_ERROR_BADGE_MESSAGE },
- setTitle: { title: BUTTON_DEFAULT_BADGE_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- forbidden: {
- setBadgeBackgroundColor: { color: FORBIDDEN_COLOR },
- setBadgeText: { text: BUTTON_BLOCKED_BADGE_MESSAGE },
- setTitle: { title: BUTTON_BLOCKED_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- autosave: {
- inject: {
- setBadgeBackgroundColor: { color: AUTOSAVE_INITIALIZING_COLOR },
- setBadgeText: { text: BUTTON_AUTOSAVE_ACTIVE_BADGE_MESSAGE },
- setTitle: { title: BUTTON_AUTOSAVE_ACTIVE_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- },
- default: {
- setBadgeBackgroundColor: { color: AUTOSAVE_DEFAULT_COLOR },
- setBadgeText: { text: BUTTON_AUTOSAVE_ACTIVE_BADGE_MESSAGE },
- setTitle: { title: BUTTON_AUTOSAVE_ACTIVE_TOOLTIP_MESSAGE },
- setIcon: { path: DEFAULT_ICON_PATH }
- }
- }
- };
- let business$2;
- browser.browserAction.onClicked.addListener(async tab => {
- const highlightedTabs = await queryTabs$1({ currentWindow: true, highlighted: true });
- if (highlightedTabs.length <= 1) {
- toggleSaveTab(tab);
- } else {
- business$2.saveTabs(highlightedTabs);
- }
- function toggleSaveTab(tab) {
- if (business$2.isSavingTab(tab)) {
- business$2.cancelTab(tab.id);
- } else {
- business$2.saveTabs([tab]);
- }
- }
- });
- function init$3(businessApi) {
- business$2 = businessApi;
- }
- function onMessage$9(message, sender) {
- if (message.method.endsWith(".processInit")) {
- const allTabsData = getTemporary(sender.tab.id);
- delete allTabsData[sender.tab.id].button;
- refreshTab$2(sender.tab);
- }
- if (message.method.endsWith(".processProgress")) {
- if (message.maxIndex) {
- onSaveProgress(sender.tab.id, message.index, message.maxIndex);
- }
- }
- if (message.method.endsWith(".processEnd")) {
- onEnd$1(sender.tab.id);
- }
- if (message.method.endsWith(".processError")) {
- if (message.error) {
- console.error("Initialization error", message.error); // eslint-disable-line no-console
- }
- onError$1(sender.tab.id);
- }
- if (message.method.endsWith(".processCancelled")) {
- onCancelled$1(sender.tab);
- }
- return Promise.resolve({});
- }
- function onStart$1(tabId, step, autoSave) {
- let state;
- if (autoSave) {
- state = getButtonState("inject", true);
- } else {
- state = step == INJECT_SCRIPTS_STEP$1 ? getButtonState("inject") : getButtonState("execute");
- state.setTitle = { title: BUTTON_INITIALIZING_TOOLTIP_MESSAGE + " (" + step + "/2)" };
- state.setIcon = { path: WAIT_ICON_PATH_PREFIX + "0.png" };
- }
- refresh(tabId, state);
- }
- function onError$1(tabId) {
- refresh(tabId, getButtonState("error"));
- }
- function onEdit$1(tabId) {
- refresh(tabId, getButtonState("edit"));
- }
- function onEnd$1(tabId, autoSave) {
- refresh(tabId, autoSave ? getButtonState("default", true) : getButtonState("end"));
- }
- function onForbiddenDomain$1(tab) {
- refresh(tab.id, getButtonState("forbidden"));
- }
- function onCancelled$1(tab) {
- refreshTab$2(tab);
- }
- function onSaveProgress(tabId, index, maxIndex) {
- onProgress(tabId, index, maxIndex, BUTTON_SAVE_PROGRESS_TOOLTIP_MESSAGE);
- }
- function onUploadProgress$1(tabId, index, maxIndex) {
- onProgress(tabId, index, maxIndex, BUTTON_UPLOAD_PROGRESS_TOOLTIP_MESSAGE);
- }
- function onProgress(tabId, index, maxIndex, tooltipMessage) {
- const progress = Math.max(Math.min(20, Math.floor((index / maxIndex) * 20)), 0);
- const barProgress = Math.min(Math.floor((index / maxIndex) * 8), 8);
- const path = WAIT_ICON_PATH_PREFIX + barProgress + ".png";
- const state = getButtonState("progress");
- state.setTitle = { title: tooltipMessage + (progress * 5) + "%" };
- state.setIcon = { path };
- refresh(tabId, state);
- }
- async function refreshTab$2(tab) {
- const autoSave = await autoSaveIsEnabled(tab);
- const state = getButtonState("default", autoSave);
- await refresh(tab.id, state);
- }
- async function refresh(tabId, state) {
- try {
- const allTabsData = getTemporary(tabId);
- if (state) {
- if (!allTabsData[tabId].button) {
- allTabsData[tabId].button = { lastState: null };
- }
- const lastState = allTabsData[tabId].button.lastState || {};
- const newState = {};
- Object.keys(state).forEach(property => {
- if (state[property] !== undefined && (JSON.stringify(lastState[property]) != JSON.stringify(state[property]))) {
- newState[property] = state[property];
- }
- });
- if (Object.keys(newState).length) {
- allTabsData[tabId].button.lastState = state;
- await refreshAsync(tabId, newState);
- }
- }
- } catch (error) {
- // ignored
- }
- }
- async function refreshAsync(tabId, state) {
- for (const browserActionMethod of Object.keys(state)) {
- await refreshProperty(tabId, browserActionMethod, state[browserActionMethod]);
- }
- }
- async function refreshProperty(tabId, browserActionMethod, browserActionParameter) {
- const actionMethodSupported = browserActionMethod != "setBadgeBackgroundColor" || BADGE_COLOR_SUPPORTED;
- if (browser.browserAction[browserActionMethod] && actionMethodSupported) {
- const parameter = JSON.parse(JSON.stringify(browserActionParameter));
- parameter.tabId = tabId;
- await browser.browserAction[browserActionMethod](parameter);
- }
- }
- function getButtonState(name, autoSave) {
- return JSON.parse(JSON.stringify(autoSave ? BUTTON_STATES.autosave[name] : BUTTON_STATES[name]));
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const menus = browser.menus;
- const BROWSER_MENUS_API_SUPPORTED = menus && menus.onClicked && menus.create && menus.update && menus.removeAll;
- const MENU_ID_SAVE_PAGE = "save-page";
- const MENU_ID_EDIT_AND_SAVE_PAGE = "edit-and-save-page";
- const MENU_ID_SAVE_WITH_PROFILE = "save-with-profile";
- const MENU_ID_SAVE_SELECTED_LINKS = "save-selected-links";
- const MENU_ID_VIEW_PENDINGS = "view-pendings";
- const MENU_ID_SELECT_PROFILE = "select-profile";
- const MENU_ID_SAVE_WITH_PROFILE_PREFIX = "wasve-with-profile-";
- const MENU_ID_SELECT_PROFILE_PREFIX = "select-profile-";
- const MENU_ID_ASSOCIATE_WITH_PROFILE = "associate-with-profile";
- const MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX = "associate-with-profile-";
- const MENU_ID_SAVE_SELECTED = "save-selected";
- const MENU_ID_SAVE_FRAME = "save-frame";
- const MENU_ID_SAVE_TABS = "save-tabs";
- const MENU_ID_SAVE_SELECTED_TABS = "save-selected-tabs";
- const MENU_ID_SAVE_UNPINNED_TABS = "save-unpinned-tabs";
- const MENU_ID_SAVE_ALL_TABS = "save-all-tabs";
- const MENU_ID_BATCH_SAVE_URLS = "batch-save-urls";
- const MENU_ID_BUTTON_SAVE_SELECTED_TABS = "button-" + MENU_ID_SAVE_SELECTED_TABS;
- const MENU_ID_BUTTON_SAVE_UNPINNED_TABS = "button-" + MENU_ID_SAVE_UNPINNED_TABS;
- const MENU_ID_BUTTON_SAVE_ALL_TABS = "button-" + MENU_ID_SAVE_ALL_TABS;
- const MENU_ID_AUTO_SAVE = "auto-save";
- const MENU_ID_AUTO_SAVE_DISABLED = "auto-save-disabled";
- const MENU_ID_AUTO_SAVE_TAB = "auto-save-tab";
- const MENU_ID_AUTO_SAVE_UNPINNED = "auto-save-unpinned";
- const MENU_ID_AUTO_SAVE_ALL = "auto-save-all";
- const MENU_CREATE_DOMAIN_RULE_MESSAGE = browser.i18n.getMessage("menuCreateDomainRule");
- const MENU_UPDATE_RULE_MESSAGE = browser.i18n.getMessage("menuUpdateRule");
- const MENU_SAVE_PAGE_MESSAGE = browser.i18n.getMessage("menuSavePage");
- const MENU_SAVE_WITH_PROFILE = browser.i18n.getMessage("menuSaveWithProfile");
- const MENU_SAVE_SELECTED_LINKS = browser.i18n.getMessage("menuSaveSelectedLinks");
- const MENU_EDIT_PAGE_MESSAGE = browser.i18n.getMessage("menuEditPage");
- const MENU_EDIT_AND_SAVE_PAGE_MESSAGE = browser.i18n.getMessage("menuEditAndSavePage");
- const MENU_VIEW_PENDINGS_MESSAGE = browser.i18n.getMessage("menuViewPendingSaves");
- const MENU_SAVE_SELECTION_MESSAGE = browser.i18n.getMessage("menuSaveSelection");
- const MENU_SAVE_FRAME_MESSAGE = browser.i18n.getMessage("menuSaveFrame");
- const MENU_SAVE_TABS_MESSAGE = browser.i18n.getMessage("menuSaveTabs");
- const MENU_SAVE_SELECTED_TABS_MESSAGE = browser.i18n.getMessage("menuSaveSelectedTabs");
- const MENU_SAVE_UNPINNED_TABS_MESSAGE = browser.i18n.getMessage("menuSaveUnpinnedTabs");
- const MENU_SAVE_ALL_TABS_MESSAGE = browser.i18n.getMessage("menuSaveAllTabs");
- const MENU_BATCH_SAVE_URLS_MESSAGE = browser.i18n.getMessage("menuBatchSaveUrls");
- const MENU_SELECT_PROFILE_MESSAGE = browser.i18n.getMessage("menuSelectProfile");
- const PROFILE_DEFAULT_SETTINGS_MESSAGE = browser.i18n.getMessage("profileDefaultSettings");
- const MENU_AUTOSAVE_MESSAGE = browser.i18n.getMessage("menuAutoSave");
- const MENU_AUTOSAVE_DISABLED_MESSAGE = browser.i18n.getMessage("menuAutoSaveDisabled");
- const MENU_AUTOSAVE_TAB_MESSAGE = browser.i18n.getMessage("menuAutoSaveTab");
- const MENU_AUTOSAVE_UNPINNED_TABS_MESSAGE = browser.i18n.getMessage("menuAutoSaveUnpinnedTabs");
- const MENU_AUTOSAVE_ALL_TABS_MESSAGE = browser.i18n.getMessage("menuAutoSaveAllTabs");
- const MENU_TOP_VISIBLE_ENTRIES = [
- MENU_ID_EDIT_AND_SAVE_PAGE,
- MENU_ID_SAVE_SELECTED_LINKS,
- MENU_ID_SAVE_SELECTED,
- MENU_ID_SAVE_FRAME,
- MENU_ID_AUTO_SAVE,
- MENU_ID_ASSOCIATE_WITH_PROFILE
- ];
- const menusCheckedState = new Map();
- const menusTitleState = new Map();
- let contextMenuVisibleState = true;
- let allMenuVisibleState = true;
- let profileIndexes = new Map();
- let menusCreated, pendingRefresh, business$1;
- Promise.resolve().then(initialize);
- function init$2(businessApi) {
- business$1 = businessApi;
- }
- function onMessage$8(message) {
- if (message.method.endsWith("refreshMenu")) {
- createMenus();
- return Promise.resolve({});
- }
- }
- async function createMenus(tab) {
- const [profiles, allTabsData] = await Promise.all([getProfiles(), getPersistent()]);
- const options = await getOptions(tab && tab.url);
- if (BROWSER_MENUS_API_SUPPORTED && options) {
- const pageContextsEnabled = ["page", "frame", "image", "link", "video", "audio", "selection"];
- const defaultContextsDisabled = [];
- if (options.browserActionMenuEnabled) {
- defaultContextsDisabled.push("browser_action");
- }
- if (options.tabMenuEnabled) {
- try {
- await menus.create({
- id: "temporary-id",
- contexts: ["tab"],
- title: "title"
- });
- defaultContextsDisabled.push("tab");
- } catch (error) {
- options.tabMenuEnabled = false;
- }
- }
- await menus.removeAll();
- const defaultContextsEnabled = defaultContextsDisabled.concat(...pageContextsEnabled);
- const defaultContexts = options.contextMenuEnabled ? defaultContextsEnabled : defaultContextsDisabled;
- menus.create({
- id: MENU_ID_SAVE_PAGE,
- contexts: defaultContexts,
- title: MENU_SAVE_PAGE_MESSAGE
- });
- menus.create({
- id: MENU_ID_EDIT_AND_SAVE_PAGE,
- contexts: defaultContexts,
- title: MENU_EDIT_AND_SAVE_PAGE_MESSAGE
- });
- menus.create({
- id: MENU_ID_SAVE_SELECTED_LINKS,
- contexts: options.contextMenuEnabled ? defaultContextsDisabled.concat(["selection"]) : defaultContextsDisabled,
- title: MENU_SAVE_SELECTED_LINKS
- });
- if (Object.keys(profiles).length > 1) {
- menus.create({
- id: MENU_ID_SAVE_WITH_PROFILE,
- contexts: defaultContexts,
- title: MENU_SAVE_WITH_PROFILE
- });
- }
- if (options.contextMenuEnabled) {
- menus.create({
- id: "separator-1",
- contexts: pageContextsEnabled,
- type: "separator"
- });
- }
- menus.create({
- id: MENU_ID_SAVE_SELECTED,
- contexts: defaultContexts,
- title: MENU_SAVE_SELECTION_MESSAGE
- });
- if (options.contextMenuEnabled) {
- menus.create({
- id: MENU_ID_SAVE_FRAME,
- contexts: ["frame"],
- title: MENU_SAVE_FRAME_MESSAGE
- });
- }
- menus.create({
- id: MENU_ID_SAVE_TABS,
- contexts: defaultContextsDisabled,
- title: MENU_SAVE_TABS_MESSAGE
- });
- menus.create({
- id: MENU_ID_BUTTON_SAVE_SELECTED_TABS,
- contexts: defaultContextsDisabled,
- title: MENU_SAVE_SELECTED_TABS_MESSAGE,
- parentId: MENU_ID_SAVE_TABS
- });
- menus.create({
- id: MENU_ID_BUTTON_SAVE_UNPINNED_TABS,
- contexts: defaultContextsDisabled,
- title: MENU_SAVE_UNPINNED_TABS_MESSAGE,
- parentId: MENU_ID_SAVE_TABS
- });
- menus.create({
- id: MENU_ID_BUTTON_SAVE_ALL_TABS,
- contexts: defaultContextsDisabled,
- title: MENU_SAVE_ALL_TABS_MESSAGE,
- parentId: MENU_ID_SAVE_TABS
- });
- if (options.contextMenuEnabled) {
- if (SELECTABLE_TABS_SUPPORTED) {
- menus.create({
- id: MENU_ID_SAVE_SELECTED_TABS,
- contexts: pageContextsEnabled,
- title: MENU_SAVE_SELECTED_TABS_MESSAGE
- });
- }
- menus.create({
- id: MENU_ID_SAVE_UNPINNED_TABS,
- contexts: pageContextsEnabled,
- title: MENU_SAVE_UNPINNED_TABS_MESSAGE
- });
- menus.create({
- id: MENU_ID_SAVE_ALL_TABS,
- contexts: pageContextsEnabled,
- title: MENU_SAVE_ALL_TABS_MESSAGE
- });
- menus.create({
- id: "separator-2",
- contexts: pageContextsEnabled,
- type: "separator"
- });
- }
- if (Object.keys(profiles).length > 1) {
- menus.create({
- id: MENU_ID_SELECT_PROFILE,
- title: MENU_SELECT_PROFILE_MESSAGE,
- contexts: defaultContexts,
- });
- menus.create({
- id: MENU_ID_SAVE_WITH_PROFILE_PREFIX + "default",
- contexts: defaultContexts,
- title: PROFILE_DEFAULT_SETTINGS_MESSAGE,
- parentId: MENU_ID_SAVE_WITH_PROFILE
- });
- const defaultProfileId = MENU_ID_SELECT_PROFILE_PREFIX + "default";
- const defaultProfileChecked = !allTabsData.profileName || allTabsData.profileName == DEFAULT_PROFILE_NAME;
- menus.create({
- id: defaultProfileId,
- type: "radio",
- contexts: defaultContexts,
- title: PROFILE_DEFAULT_SETTINGS_MESSAGE,
- checked: defaultProfileChecked,
- parentId: MENU_ID_SELECT_PROFILE
- });
- menusCheckedState.set(defaultProfileId, defaultProfileChecked);
- menus.create({
- id: MENU_ID_ASSOCIATE_WITH_PROFILE,
- title: MENU_CREATE_DOMAIN_RULE_MESSAGE,
- contexts: defaultContexts,
- });
- menusTitleState.set(MENU_ID_ASSOCIATE_WITH_PROFILE, MENU_CREATE_DOMAIN_RULE_MESSAGE);
- let rule;
- if (tab && tab.url) {
- rule = await getRule(tab.url, true);
- }
- const currentProfileId = MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + "current";
- const currentProfileChecked = !rule || (rule.profile == CURRENT_PROFILE_NAME);
- menus.create({
- id: currentProfileId,
- type: "radio",
- contexts: defaultContexts,
- title: CURRENT_PROFILE_NAME,
- checked: currentProfileChecked,
- parentId: MENU_ID_ASSOCIATE_WITH_PROFILE
- });
- menusCheckedState.set(currentProfileId, currentProfileChecked);
- const associatedDefaultProfileId = MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + "default";
- const associatedDefaultProfileChecked = Boolean(rule) && (rule.profile == DEFAULT_PROFILE_NAME);
- menus.create({
- id: associatedDefaultProfileId,
- type: "radio",
- contexts: defaultContexts,
- title: PROFILE_DEFAULT_SETTINGS_MESSAGE,
- checked: associatedDefaultProfileChecked,
- parentId: MENU_ID_ASSOCIATE_WITH_PROFILE
- });
- menusCheckedState.set(associatedDefaultProfileId, associatedDefaultProfileChecked);
- profileIndexes = new Map();
- Object.keys(profiles).forEach((profileName, profileIndex) => {
- if (profileName != DEFAULT_PROFILE_NAME) {
- let profileId = MENU_ID_SAVE_WITH_PROFILE_PREFIX + profileIndex;
- menus.create({
- id: profileId,
- contexts: defaultContexts,
- title: profileName,
- parentId: MENU_ID_SAVE_WITH_PROFILE
- });
- profileId = MENU_ID_SELECT_PROFILE_PREFIX + profileIndex;
- let profileChecked = allTabsData.profileName == profileName;
- menus.create({
- id: profileId,
- type: "radio",
- contexts: defaultContexts,
- title: profileName,
- checked: profileChecked,
- parentId: MENU_ID_SELECT_PROFILE
- });
- menusCheckedState.set(profileId, profileChecked);
- profileId = MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + profileIndex;
- profileChecked = Boolean(rule) && rule.profile == profileName;
- menus.create({
- id: profileId,
- type: "radio",
- contexts: defaultContexts,
- title: profileName,
- checked: profileChecked,
- parentId: MENU_ID_ASSOCIATE_WITH_PROFILE
- });
- menusCheckedState.set(profileId, profileChecked);
- profileIndexes.set(profileName, profileIndex);
- }
- });
- if (options.contextMenuEnabled) {
- menus.create({
- id: "separator-3",
- contexts: pageContextsEnabled,
- type: "separator"
- });
- }
- }
- if (AUTO_SAVE_SUPPORTED) {
- menus.create({
- id: MENU_ID_AUTO_SAVE,
- contexts: defaultContexts,
- title: MENU_AUTOSAVE_MESSAGE
- });
- menus.create({
- id: MENU_ID_AUTO_SAVE_DISABLED,
- type: "radio",
- title: MENU_AUTOSAVE_DISABLED_MESSAGE,
- contexts: defaultContexts,
- checked: true,
- parentId: MENU_ID_AUTO_SAVE
- });
- menusCheckedState.set(MENU_ID_AUTO_SAVE_DISABLED, true);
- menus.create({
- id: MENU_ID_AUTO_SAVE_TAB,
- type: "radio",
- title: MENU_AUTOSAVE_TAB_MESSAGE,
- contexts: defaultContexts,
- checked: false,
- parentId: MENU_ID_AUTO_SAVE
- });
- menusCheckedState.set(MENU_ID_AUTO_SAVE_TAB, false);
- menus.create({
- id: MENU_ID_AUTO_SAVE_UNPINNED,
- type: "radio",
- title: MENU_AUTOSAVE_UNPINNED_TABS_MESSAGE,
- contexts: defaultContexts,
- checked: false,
- parentId: MENU_ID_AUTO_SAVE
- });
- menusCheckedState.set(MENU_ID_AUTO_SAVE_UNPINNED, false);
- menus.create({
- id: MENU_ID_AUTO_SAVE_ALL,
- type: "radio",
- title: MENU_AUTOSAVE_ALL_TABS_MESSAGE,
- contexts: defaultContexts,
- checked: false,
- parentId: MENU_ID_AUTO_SAVE
- });
- menusCheckedState.set(MENU_ID_AUTO_SAVE_ALL, false);
- menus.create({
- id: "separator-4",
- contexts: defaultContexts,
- type: "separator"
- });
- }
- menus.create({
- id: MENU_ID_BATCH_SAVE_URLS,
- contexts: defaultContexts,
- title: MENU_BATCH_SAVE_URLS_MESSAGE
- });
- menus.create({
- id: MENU_ID_VIEW_PENDINGS,
- contexts: defaultContexts,
- title: MENU_VIEW_PENDINGS_MESSAGE
- });
- }
- menusCreated = true;
- if (pendingRefresh) {
- pendingRefresh = false;
- (await browser.tabs.query({})).forEach(async tab => await refreshTab$1(tab));
- }
- }
- async function initialize() {
- if (BROWSER_MENUS_API_SUPPORTED) {
- createMenus();
- menus.onClicked.addListener(async (event, tab) => {
- if (event.menuItemId == MENU_ID_SAVE_PAGE) {
- if (event.linkUrl) {
- business$1.saveUrls([event.linkUrl]);
- } else {
- business$1.saveTabs([tab]);
- }
- }
- if (event.menuItemId == MENU_ID_EDIT_AND_SAVE_PAGE) {
- const allTabsData = await getPersistent(tab.id);
- if (allTabsData[tab.id].savedPageDetected) {
- business$1.openEditor(tab);
- } else {
- if (event.linkUrl) {
- business$1.saveUrls([event.linkUrl], { openEditor: true });
- } else {
- business$1.saveTabs([tab], { openEditor: true });
- }
- }
- }
- if (event.menuItemId == MENU_ID_SAVE_SELECTED_LINKS) {
- business$1.saveSelectedLinks(tab);
- }
- if (event.menuItemId == MENU_ID_VIEW_PENDINGS) {
- await browser.tabs.create({ active: true, url: "/src/ui/pages/pendings.html" });
- }
- if (event.menuItemId == MENU_ID_SAVE_SELECTED) {
- business$1.saveTabs([tab], { selected: true });
- }
- if (event.menuItemId == MENU_ID_SAVE_FRAME) {
- business$1.saveTabs([tab], { frameId: event.frameId });
- }
- if (event.menuItemId == MENU_ID_SAVE_SELECTED_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_SELECTED_TABS) {
- const tabs = await queryTabs$1({ currentWindow: true, highlighted: true });
- business$1.saveTabs(tabs);
- }
- if (event.menuItemId == MENU_ID_SAVE_UNPINNED_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_UNPINNED_TABS) {
- const tabs = await queryTabs$1({ currentWindow: true, pinned: false });
- business$1.saveTabs(tabs);
- }
- if (event.menuItemId == MENU_ID_SAVE_ALL_TABS || event.menuItemId == MENU_ID_BUTTON_SAVE_ALL_TABS) {
- const tabs = await queryTabs$1({ currentWindow: true });
- business$1.saveTabs(tabs);
- }
- if (event.menuItemId == MENU_ID_BATCH_SAVE_URLS) {
- business$1.batchSaveUrls();
- }
- if (event.menuItemId == MENU_ID_AUTO_SAVE_TAB) {
- const allTabsData = await getPersistent(tab.id);
- allTabsData[tab.id].autoSave = true;
- await setPersistent(allTabsData);
- refreshExternalComponents(tab);
- }
- if (event.menuItemId == MENU_ID_AUTO_SAVE_DISABLED) {
- const allTabsData = await getPersistent();
- Object.keys(allTabsData).forEach(tabId => {
- if (typeof allTabsData[tabId] == "object" && allTabsData[tabId].autoSave) {
- allTabsData[tabId].autoSave = false;
- }
- });
- allTabsData.autoSaveUnpinned = allTabsData.autoSaveAll = false;
- await setPersistent(allTabsData);
- refreshExternalComponents(tab);
- }
- if (event.menuItemId == MENU_ID_AUTO_SAVE_ALL) {
- const allTabsData = await getPersistent();
- allTabsData.autoSaveAll = event.checked;
- await setPersistent(allTabsData);
- refreshExternalComponents(tab);
- }
- if (event.menuItemId == MENU_ID_AUTO_SAVE_UNPINNED) {
- const allTabsData = await getPersistent();
- allTabsData.autoSaveUnpinned = event.checked;
- await setPersistent(allTabsData);
- refreshExternalComponents(tab);
- }
- if (event.menuItemId.startsWith(MENU_ID_SAVE_WITH_PROFILE_PREFIX)) {
- const profiles = await getProfiles();
- const profileId = event.menuItemId.split(MENU_ID_SAVE_WITH_PROFILE_PREFIX)[1];
- let profileName;
- if (profileId == "default") {
- profileName = DEFAULT_PROFILE_NAME;
- } else {
- const profileIndex = Number(profileId);
- profileName = Object.keys(profiles)[profileIndex];
- }
- profiles[profileName].profileName = profileName;
- business$1.saveTabs([tab], profiles[profileName]);
- }
- if (event.menuItemId.startsWith(MENU_ID_SELECT_PROFILE_PREFIX)) {
- const [profiles, allTabsData] = await Promise.all([getProfiles(), getPersistent()]);
- const profileId = event.menuItemId.split(MENU_ID_SELECT_PROFILE_PREFIX)[1];
- if (profileId == "default") {
- allTabsData.profileName = DEFAULT_PROFILE_NAME;
- } else {
- const profileIndex = Number(profileId);
- allTabsData.profileName = Object.keys(profiles)[profileIndex];
- }
- await setPersistent(allTabsData);
- refreshExternalComponents(tab);
- }
- if (event.menuItemId.startsWith(MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX)) {
- const [profiles, rule] = await Promise.all([getProfiles(), getRule(tab.url, true)]);
- const profileId = event.menuItemId.split(MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX)[1];
- let profileName;
- if (profileId == "default") {
- profileName = DEFAULT_PROFILE_NAME;
- } else if (profileId == "current") {
- profileName = CURRENT_PROFILE_NAME;
- } else {
- const profileIndex = Number(profileId);
- profileName = Object.keys(profiles)[profileIndex];
- }
- if (rule) {
- await updateRule(rule.url, rule.url, profileName, profileName);
- } else {
- await updateTitleValue(MENU_ID_ASSOCIATE_WITH_PROFILE, MENU_UPDATE_RULE_MESSAGE);
- await addRule(new URL(tab.url).hostname, profileName, profileName);
- }
- }
- });
- if (menusCreated) {
- pendingRefresh = true;
- } else {
- (await browser.tabs.query({})).forEach(async tab => await refreshTab$1(tab));
- }
- }
- }
- async function refreshExternalComponents(tab) {
- const allTabsData = await getPersistent(tab.id);
- await refreshAutoSaveTabs();
- await refreshTab$2(tab);
- try {
- await browser.runtime.sendMessage({ method: "options.refresh", profileName: allTabsData.profileName });
- } catch (error) {
- // ignored
- }
- }
- async function refreshTab$1(tab) {
- if (BROWSER_MENUS_API_SUPPORTED && menusCreated) {
- const promises = [];
- const allTabsData = await getPersistent(tab.id);
- if (allTabsData[tab.id].editorDetected) {
- updateAllVisibleValues(false);
- } else {
- updateAllVisibleValues(true);
- if (AUTO_SAVE_SUPPORTED) {
- promises.push(updateCheckedValue(MENU_ID_AUTO_SAVE_DISABLED, !allTabsData[tab.id].autoSave));
- promises.push(updateCheckedValue(MENU_ID_AUTO_SAVE_TAB, allTabsData[tab.id].autoSave));
- promises.push(updateCheckedValue(MENU_ID_AUTO_SAVE_UNPINNED, Boolean(allTabsData.autoSaveUnpinned)));
- promises.push(updateCheckedValue(MENU_ID_AUTO_SAVE_ALL, Boolean(allTabsData.autoSaveAll)));
- }
- if (tab && tab.url) {
- const options = await getOptions(tab.url);
- promises.push(updateVisibleValue(tab, options.contextMenuEnabled));
- promises.push(updateTitleValue(MENU_ID_EDIT_AND_SAVE_PAGE, allTabsData[tab.id].savedPageDetected ? MENU_EDIT_PAGE_MESSAGE : MENU_EDIT_AND_SAVE_PAGE_MESSAGE));
- if (SELECTABLE_TABS_SUPPORTED) {
- promises.push(menus.update(MENU_ID_SAVE_SELECTED, { visible: !options.saveRawPage }));
- }
- promises.push(menus.update(MENU_ID_EDIT_AND_SAVE_PAGE, { visible: !options.openEditor || allTabsData[tab.id].savedPageDetected }));
- let selectedEntryId = MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + "default";
- let title = MENU_CREATE_DOMAIN_RULE_MESSAGE;
- const [profiles, rule] = await Promise.all([getProfiles(), getRule(tab.url)]);
- if (rule) {
- const profileIndex = profileIndexes.get(rule.profile);
- if (profileIndex) {
- selectedEntryId = MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + profileIndex;
- title = MENU_UPDATE_RULE_MESSAGE;
- }
- }
- if (Object.keys(profiles).length > 1) {
- Object.keys(profiles).forEach((profileName, profileIndex) => {
- if (profileName == DEFAULT_PROFILE_NAME) {
- promises.push(updateCheckedValue(MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + "default", selectedEntryId == MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + "default"));
- } else {
- promises.push(updateCheckedValue(MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + profileIndex, selectedEntryId == MENU_ID_ASSOCIATE_WITH_PROFILE_PREFIX + profileIndex));
- }
- });
- promises.push(updateTitleValue(MENU_ID_ASSOCIATE_WITH_PROFILE, title));
- }
- }
- }
- await Promise.all(promises);
- }
- }
- async function updateAllVisibleValues(visible) {
- const lastVisibleState = allMenuVisibleState;
- allMenuVisibleState = visible;
- if (lastVisibleState === undefined || lastVisibleState != visible) {
- const promises = [];
- try {
- MENU_TOP_VISIBLE_ENTRIES.forEach(id => promises.push(menus.update(id, { visible })));
- await Promise.all(promises);
- } catch (error) {
- // ignored
- }
- }
- }
- async function updateVisibleValue(tab, visible) {
- const lastVisibleState = contextMenuVisibleState;
- contextMenuVisibleState = visible;
- if (lastVisibleState === undefined || lastVisibleState != visible) {
- await createMenus(tab);
- }
- }
- function updateTitleValue(id, title) {
- const lastTitleValue = menusTitleState.get(id);
- menusTitleState.set(id, title);
- if (lastTitleValue === undefined) {
- return menus.update(id, { title });
- } else if (lastTitleValue != title) {
- return menus.update(id, { title });
- }
- }
- async function updateCheckedValue(id, checked) {
- checked = Boolean(checked);
- menusCheckedState.set(id, checked);
- await menus.update(id, { checked });
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const commands = browser.commands;
- const BROWSER_COMMANDS_API_SUPPORTED = commands && commands.onCommand && commands.onCommand.addListener;
- let business;
- function init$1(businessApi) {
- business = businessApi;
- }
- if (BROWSER_COMMANDS_API_SUPPORTED) {
- commands.onCommand.addListener(async command => {
- if (command == "save-selected-tabs") {
- const highlightedTabs = await queryTabs$1({ currentWindow: true, highlighted: true });
- business.saveTabs(highlightedTabs, { optionallySelected: true });
- }
- if (command == "save-all-tabs") {
- const tabs = await queryTabs$1({ currentWindow: true });
- business.saveTabs(tabs);
- }
- });
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- function init(businessApi) {
- init$2(businessApi);
- init$3(businessApi);
- init$1(businessApi);
- }
- function onMessage$7(message, sender) {
- if (message.method.endsWith(".refreshMenu")) {
- return onMessage$8(message);
- } else {
- return onMessage$9(message, sender);
- }
- }
- async function refreshTab(tab) {
- return Promise.all([createMenus(tab), refreshTab$2(tab)]);
- }
- function onForbiddenDomain(tab) {
- onForbiddenDomain$1(tab);
- }
- function onStart(tabId, step, autoSave) {
- onStart$1(tabId, step, autoSave);
- }
- async function onError(tabId, message, link) {
- onError$1(tabId);
- try {
- if (message) {
- await browser.tabs.sendMessage(tabId, { method: "content.error", error: message.toString(), link });
- }
- } catch (error) {
- // ignored
- }
- }
- function onEdit(tabId) {
- onEdit$1(tabId);
- }
- function onEnd(tabId, autoSave) {
- onEnd$1(tabId, autoSave);
- }
- function onCancelled(tabId) {
- onCancelled$1(tabId);
- }
- function onUploadProgress(tabId, index, maxIndex) {
- onUploadProgress$1(tabId, index, maxIndex);
- }
- function onTabCreated$1(tab) {
- refreshTab$1(tab);
- }
- function onTabActivated$1(tab) {
- refreshTab$1(tab);
- }
- function onInit$3(tab) {
- refreshTab$1(tab);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, fetch, TextDecoder */
- let contentScript, frameScript;
- const contentScriptFiles = [
- "lib/web-stream.js",
- "lib/chrome-browser-polyfill.js",
- "lib/single-file.js"
- ];
- const frameScriptFiles = [
- "lib/chrome-browser-polyfill.js",
- "lib/single-file-frames.js"
- ];
- const basePath = "../../../";
- async function inject(tabId, options) {
- await initScripts(options);
- let scriptsInjected;
- if (!options.removeFrames) {
- try {
- await browser.tabs.executeScript(tabId, { code: frameScript, allFrames: true, matchAboutBlank: true, runAt: "document_start" });
- } catch (error) {
- // ignored
- }
- }
- try {
- await browser.tabs.executeScript(tabId, { code: contentScript, allFrames: false, runAt: "document_idle" });
- scriptsInjected = true;
- } catch (error) {
- // ignored
- }
- if (scriptsInjected) {
- if (options.frameId) {
- await browser.tabs.executeScript(tabId, { code: "document.documentElement.dataset.requestedFrameId = true", frameId: options.frameId, matchAboutBlank: true, runAt: "document_start" });
- }
- }
- return scriptsInjected;
- }
- async function initScripts(options) {
- const extensionScriptFiles = options.extensionScriptFiles || [];
- if (!contentScript && !frameScript) {
- [contentScript, frameScript] = await Promise.all([
- getScript(contentScriptFiles.concat(extensionScriptFiles)),
- getScript(frameScriptFiles)
- ]);
- }
- }
- async function getScript(scriptFiles) {
- const scriptsPromises = scriptFiles.map(async scriptFile => {
- if (typeof scriptFile == "function") {
- return "(" + scriptFile.toString() + ")();";
- } else {
- const scriptResource = await fetch(browser.runtime.getURL(basePath + scriptFile));
- return new TextDecoder().decode(await scriptResource.arrayBuffer());
- }
- });
- let content = "";
- for (const scriptPromise of scriptsPromises) {
- content += await scriptPromise;
- }
- return content;
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, window, document, CustomEvent, setTimeout, clearTimeout */
- const FETCH_REQUEST_EVENT = "single-file-request-fetch";
- const FETCH_ACK_EVENT = "single-file-ack-fetch";
- const FETCH_RESPONSE_EVENT = "single-file-response-fetch";
- const ERR_HOST_FETCH = "Host fetch error (SingleFile)";
- const HOST_FETCH_MAX_DELAY = 2500;
- const USE_HOST_FETCH = Boolean(window.wrappedJSObject);
- const fetch$2 = (url, options) => window.fetch(url, options);
- let requestId = 0, pendingResponses = new Map();
- browser.runtime.onMessage.addListener(message => {
- if (message.method == "singlefile.fetchFrame" && window.frameId && window.frameId == message.frameId) {
- return onFetchFrame(message);
- }
- if (message.method == "singlefile.fetchResponse") {
- return onFetchResponse(message);
- }
- });
- async function onFetchFrame(message) {
- try {
- const response = await fetch$2(message.url, { cache: "force-cache", headers: message.headers });
- return {
- status: response.status,
- headers: [...response.headers],
- array: Array.from(new Uint8Array(await response.arrayBuffer()))
- };
- } catch (error) {
- return {
- error: error && error.toString()
- };
- }
- }
- async function onFetchResponse(message) {
- const pendingResponse = pendingResponses.get(message.requestId);
- if (pendingResponse) {
- if (message.error) {
- pendingResponse.reject(new Error(message.error));
- pendingResponses.delete(message.requestId);
- } else {
- if (message.truncated) {
- if (pendingResponse.array) {
- pendingResponse.array = pendingResponse.array.concat(message.array);
- } else {
- pendingResponse.array = message.array;
- pendingResponses.set(message.requestId, pendingResponse);
- }
- if (message.finished) {
- message.array = pendingResponse.array;
- }
- }
- if (!message.truncated || message.finished) {
- pendingResponse.resolve({
- status: message.status,
- headers: { get: headerName => message.headers && message.headers[headerName] },
- arrayBuffer: async () => new Uint8Array(message.array).buffer
- });
- pendingResponses.delete(message.requestId);
- }
- }
- }
- return {};
- }
- async function hostFetch(url, options) {
- const result = new Promise((resolve, reject) => {
- document.dispatchEvent(new CustomEvent(FETCH_REQUEST_EVENT, { detail: JSON.stringify({ url, options }) }));
- document.addEventListener(FETCH_ACK_EVENT, onAckFetch, false);
- document.addEventListener(FETCH_RESPONSE_EVENT, onResponseFetch, false);
- const timeout = setTimeout(() => {
- removeListeners();
- reject(new Error(ERR_HOST_FETCH));
- }, HOST_FETCH_MAX_DELAY);
- function onResponseFetch(event) {
- if (event.detail) {
- if (event.detail.url == url) {
- removeListeners();
- if (event.detail.response) {
- resolve({
- status: event.detail.status,
- headers: new Map(event.detail.headers),
- arrayBuffer: async () => event.detail.response
- });
- } else {
- reject(event.detail.error);
- }
- }
- } else {
- reject();
- }
- }
- function onAckFetch() {
- clearTimeout(timeout);
- }
- function removeListeners() {
- document.removeEventListener(FETCH_RESPONSE_EVENT, onResponseFetch, false);
- document.removeEventListener(FETCH_ACK_EVENT, onAckFetch, false);
- }
- });
- try {
- return await result;
- } catch (error) {
- if (error && error.message == ERR_HOST_FETCH) {
- return fetch$2(url, options);
- } else {
- throw error;
- }
- }
- }
- async function fetchResource(url, options = {}) {
- try {
- const fetchOptions = { cache: "force-cache", headers: options.headers };
- return await (options.referrer && USE_HOST_FETCH ? hostFetch(url, fetchOptions) : fetch$2(url, fetchOptions));
- }
- catch (error) {
- requestId++;
- const promise = new Promise((resolve, reject) => pendingResponses.set(requestId, { resolve, reject }));
- await sendMessage({ method: "singlefile.fetch", url, requestId, referrer: options.referrer, headers: options.headers });
- return promise;
- }
- }
- async function frameFetch(url, options) {
- const response = await sendMessage({ method: "singlefile.fetchFrame", url, frameId: options.frameId, referrer: options.referrer, headers: options.headers });
- return {
- status: response.status,
- headers: new Map(response.headers),
- arrayBuffer: async () => new Uint8Array(response.array).buffer
- };
- }
- async function sendMessage(message) {
- const response = await browser.runtime.sendMessage(message);
- if (!response || response.error) {
- throw new Error(response && response.error && response.error.toString());
- } else {
- return response;
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- function injectScript(tabId, options) {
- return inject(tabId, options);
- }
- function getPageData(options, doc, win, initOptions = { fetch: fetchResource, frameFetch }) {
- return globalThis.singlefile.getPageData(options, initOptions, doc, win);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const ERROR_CONNECTION_ERROR_CHROMIUM = "Could not establish connection. Receiving end does not exist.";
- const ERROR_CONNECTION_LOST_CHROMIUM = "The message port closed before a response was received.";
- const ERROR_CONNECTION_LOST_GECKO = "Message manager disconnected";
- const ERROR_EDITOR_PAGE_CHROMIUM = "Cannot access contents of url ";
- const ERROR_CHANNEL_CLOSED_CHROMIUM = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received";
- const INJECT_SCRIPTS_STEP = 1;
- const EXECUTE_SCRIPTS_STEP = 2;
- const TASK_PENDING_STATE = "pending";
- const TASK_PROCESSING_STATE = "processing";
- const extensionScriptFiles = [
- "lib/single-file-extension.js"
- ];
- const tasks = [];
- let currentTaskId = 0, maxParallelWorkers, processInForeground;
- init({ isSavingTab, saveTabs, saveUrls, cancelTab, openEditor, saveSelectedLinks, batchSaveUrls });
- async function saveSelectedLinks(tab) {
- const tabOptions = { extensionScriptFiles, tabId: tab.id, tabIndex: tab.index };
- const scriptsInjected = await injectScript(tab.id, tabOptions);
- if (scriptsInjected) {
- const response = await browser.tabs.sendMessage(tab.id, { method: "content.getSelectedLinks" });
- if (response.urls && response.urls.length) {
- const tab = await batchSaveUrls();
- const onTabUpdated = (tabId, changeInfo) => {
- if (changeInfo.status == "complete" && tabId == tab.id) {
- browser.tabs.onUpdated.removeListener(onTabUpdated);
- browser.tabs.sendMessage(tab.id, { method: "newUrls.addURLs", urls: response.urls });
- }
- };
- browser.tabs.onUpdated.addListener(onTabUpdated);
- }
- } else {
- onForbiddenDomain(tab);
- }
- }
- async function batchSaveUrls() {
- return browser.tabs.create({ active: true, url: "/src/ui/pages/batch-save-urls.html" });
- }
- async function saveUrls(urls, options = {}) {
- await initMaxParallelWorkers();
- await Promise.all(urls.map(async url => {
- const tabOptions = await getOptions(url);
- Object.keys(options).forEach(key => tabOptions[key] = options[key]);
- tabOptions.autoClose = true;
- tabOptions.extensionScriptFiles = extensionScriptFiles;
- if (tabOptions.passReferrerOnError) {
- enableReferrerOnError();
- }
- addTask({
- tab: { url },
- status: TASK_PENDING_STATE,
- options: tabOptions,
- method: "content.save"
- });
- }));
- runTasks();
- }
- async function saveTabs(tabs, options = {}) {
- await initMaxParallelWorkers();
- await Promise.all(tabs.map(async tab => {
- const tabId = tab.id;
- const tabOptions = await getOptions(tab.url);
- Object.keys(options).forEach(key => tabOptions[key] = options[key]);
- tabOptions.tabId = tabId;
- tabOptions.tabIndex = tab.index;
- tabOptions.extensionScriptFiles = extensionScriptFiles;
- if (tabOptions.passReferrerOnError) {
- enableReferrerOnError();
- }
- const tabData = {
- id: tab.id,
- index: tab.index,
- url: tab.url,
- title: tab.title
- };
- if (options.autoSave) {
- if (autoSaveIsEnabled(tab)) {
- const taskInfo = addTask({
- status: TASK_PROCESSING_STATE,
- tab: tabData,
- options: tabOptions,
- method: "content.autosave"
- });
- runTask(taskInfo);
- }
- } else {
- onStart(tabId, INJECT_SCRIPTS_STEP);
- const scriptsInjected = await injectScript(tabId, tabOptions);
- if (scriptsInjected || isEditor(tab)) {
- onStart(tabId, EXECUTE_SCRIPTS_STEP);
- addTask({
- status: TASK_PENDING_STATE,
- tab: tabData,
- options: tabOptions,
- method: "content.save"
- });
- } else {
- onForbiddenDomain(tab);
- }
- }
- }));
- runTasks();
- }
- function addTask(info) {
- const taskInfo = {
- id: currentTaskId,
- status: info.status,
- tab: info.tab,
- options: info.options,
- method: info.method,
- done: function (runNextTasks = true) {
- const index = tasks.findIndex(taskInfo => taskInfo.id == this.id);
- if (index > -1) {
- tasks.splice(index, 1);
- if (runNextTasks) {
- runTasks();
- }
- }
- }
- };
- tasks.push(taskInfo);
- currentTaskId++;
- return taskInfo;
- }
- function openEditor(tab) {
- browser.tabs.sendMessage(tab.id, { method: "content.openEditor" });
- }
- async function initMaxParallelWorkers() {
- if (!maxParallelWorkers) {
- const configData = await getConfig();
- processInForeground = configData.processInForeground;
- maxParallelWorkers = processInForeground ? 1 : configData.maxParallelWorkers;
- }
- }
- function runTasks() {
- const processingCount = tasks.filter(taskInfo => taskInfo.status == TASK_PROCESSING_STATE).length;
- for (let index = 0; index < Math.min(tasks.length - processingCount, (maxParallelWorkers - processingCount)); index++) {
- const taskInfo = tasks.find(taskInfo => taskInfo.status == TASK_PENDING_STATE);
- if (taskInfo) {
- runTask(taskInfo);
- }
- }
- }
- async function runTask(taskInfo) {
- const taskId = taskInfo.id;
- taskInfo.status = TASK_PROCESSING_STATE;
- if (!taskInfo.tab.id) {
- let scriptsInjected;
- try {
- const tab = await createTabAndWaitUntilComplete({ url: taskInfo.tab.url, active: false });
- taskInfo.tab.id = taskInfo.options.tabId = tab.id;
- taskInfo.tab.index = taskInfo.options.tabIndex = tab.index;
- onStart(taskInfo.tab.id, INJECT_SCRIPTS_STEP);
- scriptsInjected = await injectScript(taskInfo.tab.id, taskInfo.options);
- } catch (tabId) {
- taskInfo.tab.id = tabId;
- }
- if (scriptsInjected) {
- onStart(taskInfo.tab.id, EXECUTE_SCRIPTS_STEP);
- } else {
- taskInfo.done();
- return;
- }
- }
- taskInfo.options.taskId = taskId;
- try {
- if (processInForeground) {
- await browser.tabs.update(taskInfo.tab.id, { active: true });
- }
- await browser.tabs.sendMessage(taskInfo.tab.id, { method: taskInfo.method, options: taskInfo.options });
- } catch (error) {
- if (error && (!error.message || !isIgnoredError(error))) {
- console.log(error.message ? error.message : error); // eslint-disable-line no-console
- onError(taskInfo.tab.id, error.message, error.link);
- taskInfo.done();
- }
- }
- }
- function isIgnoredError(error) {
- return error.message == ERROR_CONNECTION_LOST_CHROMIUM ||
- error.message == ERROR_CONNECTION_ERROR_CHROMIUM ||
- error.message == ERROR_CONNECTION_LOST_GECKO ||
- error.message == ERROR_CHANNEL_CLOSED_CHROMIUM ||
- error.message.startsWith(ERROR_EDITOR_PAGE_CHROMIUM + JSON.stringify(EDITOR_URL));
- }
- function isSavingTab(tab) {
- return Boolean(tasks.find(taskInfo => taskInfo.tab.id == tab.id));
- }
- function onInit$2(tab) {
- cancelTab(tab.id, false);
- }
- function onTabReplaced$2(addedTabId, removedTabId) {
- tasks.forEach(taskInfo => {
- if (taskInfo.tab.id == removedTabId) {
- taskInfo.tab.id = addedTabId;
- }
- });
- }
- function onSaveEnd(taskId) {
- const taskInfo = tasks.find(taskInfo => taskInfo.id == taskId);
- if (taskInfo) {
- if (taskInfo.options.autoClose && !taskInfo.cancelled) {
- browser.tabs.remove(taskInfo.tab.id);
- }
- taskInfo.done();
- }
- }
- async function createTabAndWaitUntilComplete(createProperties) {
- const tab = await browser.tabs.create(createProperties);
- return new Promise((resolve, reject) => {
- browser.tabs.onUpdated.addListener(onTabUpdated);
- browser.tabs.onRemoved.addListener(onTabRemoved);
- function onTabUpdated(tabId, changeInfo) {
- if (tabId == tab.id && changeInfo.status == "complete") {
- resolve(tab);
- browser.tabs.onUpdated.removeListener(onTabUpdated);
- browser.tabs.onRemoved.removeListener(onTabRemoved);
- }
- }
- function onTabRemoved(tabId) {
- if (tabId == tab.id) {
- reject(tabId);
- browser.tabs.onRemoved.removeListener(onTabRemoved);
- }
- }
- });
- }
- function setCancelCallback(taskId, cancelCallback) {
- const taskInfo = tasks.find(taskInfo => taskInfo.id == taskId);
- if (taskInfo) {
- taskInfo.cancel = cancelCallback;
- }
- }
- function cancelTab(tabId, stopProcessing = true) {
- Array.from(tasks).filter(taskInfo =>
- taskInfo.tab.id == tabId &&
- !taskInfo.options.autoSave &&
- (stopProcessing || taskInfo.status != TASK_PROCESSING_STATE)
- ).forEach(cancel);
- }
- function cancelTask(taskId) {
- cancel(tasks.find(taskInfo => taskInfo.id == taskId));
- }
- function cancelAllTasks() {
- Array.from(tasks).forEach(taskInfo => cancel(taskInfo, false));
- }
- function getTasksInfo() {
- return tasks.map(mapTaskInfo);
- }
- function getTaskInfo(taskId) {
- return tasks.find(taskInfo => taskInfo.id == taskId);
- }
- function cancel(taskInfo, runNextTasks) {
- const tabId = taskInfo.tab.id;
- taskInfo.cancelled = true;
- if (tabId) {
- browser.tabs.sendMessage(tabId, {
- method: "content.cancelSave",
- options: {
- loadDeferredImages: taskInfo.options.loadDeferredImages,
- loadDeferredImagesKeepZoomLevel: taskInfo.options.loadDeferredImagesKeepZoomLevel
- }
- }).catch(() => {
- // ignored
- });
- if (taskInfo.method == "content.autosave") {
- onEnd(tabId, true);
- }
- onCancelled(taskInfo.tab);
- }
- if (taskInfo.cancel) {
- taskInfo.cancel();
- }
- taskInfo.done(runNextTasks);
- }
- function mapTaskInfo(taskInfo) {
- return { id: taskInfo.id, tabId: taskInfo.tab.id, index: taskInfo.tab.index, url: taskInfo.tab.url, title: taskInfo.tab.title, cancelled: taskInfo.cancelled, status: taskInfo.status };
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser */
- let enabled = true;
- async function onMessage$6(message) {
- if (message.method.endsWith(".state")) {
- return { enabled };
- }
- }
- async function externalSave(pageData) {
- pageData.autoSaveExternalSave = false;
- let response;
- try {
- response = await browser.runtime.sendNativeMessage("singlefile_companion", {
- method: "externalSave",
- pageData
- });
- } catch (error) {
- if (!error.message || !error.message.includes("Native host has exited")) {
- throw error;
- }
- }
- if (response && response.error) {
- throw new Error(response.error + " (Companion)");
- }
- }
- async function save(pageData) {
- let response;
- try {
- response = await browser.runtime.sendNativeMessage("singlefile_companion", {
- method: "save",
- pageData
- });
- } catch (error) {
- if (!error.message || !error.message.includes("Native host has exited")) {
- throw error;
- }
- }
- if (response && response.error) {
- throw new Error(response.error + " (Companion)");
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const pendingSaves = new Set();
- Promise.resolve().then(enable);
- async function onMessage$5(message) {
- if (message.method.endsWith(".saveCreatedBookmarks")) {
- enable();
- return {};
- }
- if (message.method.endsWith(".disable")) {
- disable();
- return {};
- }
- }
- async function enable() {
- try {
- browser.bookmarks.onCreated.removeListener(onCreated);
- browser.bookmarks.onMoved.removeListener(onMoved);
- } catch (error) {
- // ignored
- }
- let enabled;
- const profiles = await getProfiles();
- Object.keys(profiles).forEach(profileName => {
- if (profiles[profileName].saveCreatedBookmarks) {
- enabled = true;
- }
- });
- if (enabled) {
- browser.bookmarks.onCreated.addListener(onCreated);
- browser.bookmarks.onMoved.addListener(onMoved);
- }
- }
- async function disable() {
- let disabled;
- const profiles = await getProfiles();
- Object.keys(profiles).forEach(profileName => disabled = disabled || !profiles[profileName].saveCreatedBookmarks);
- if (disabled) {
- browser.bookmarks.onCreated.removeListener(onCreated);
- browser.bookmarks.onMoved.removeListener(onMoved);
- }
- }
- async function update(id, changes) {
- try {
- await browser.bookmarks.update(id, changes);
- } catch (error) {
- // ignored
- }
- }
- async function onCreated(bookmarkId, bookmarkInfo) {
- pendingSaves.add(bookmarkId);
- await saveBookmark(bookmarkId, bookmarkInfo.url, bookmarkInfo);
- }
- async function onMoved(bookmarkId, bookmarkInfo) {
- if (pendingSaves.has(bookmarkId)) {
- const bookmarks = await browser.bookmarks.get(bookmarkId);
- if (bookmarks[0]) {
- await saveBookmark(bookmarkId, bookmarks[0].url, bookmarkInfo);
- }
- }
- }
- async function saveBookmark(bookmarkId, url, bookmarkInfo) {
- const activeTabs = await browser.tabs.query({ lastFocusedWindow: true, active: true });
- const options = await getOptions(url);
- if (options.saveCreatedBookmarks) {
- const bookmarkFolders = await getParentFolders(bookmarkInfo.parentId);
- const allowedBookmarkSet = options.allowedBookmarkFolders.toString();
- const allowedBookmark = bookmarkFolders.find(folder => options.allowedBookmarkFolders.includes(folder));
- const ignoredBookmarkSet = options.ignoredBookmarkFolders.toString();
- const ignoredBookmark = bookmarkFolders.find(folder => options.ignoredBookmarkFolders.includes(folder));
- if (
- ((allowedBookmarkSet && allowedBookmark) || !allowedBookmarkSet) &&
- ((ignoredBookmarkSet && !ignoredBookmark) || !ignoredBookmarkSet)
- ) {
- if (activeTabs.length && activeTabs[0].url == url) {
- pendingSaves.delete(bookmarkId);
- saveTabs(activeTabs, { bookmarkId, bookmarkFolders });
- } else {
- const tabs = await browser.tabs.query({});
- if (tabs.length) {
- const tab = tabs.find(tab => tab.url == url);
- if (tab) {
- pendingSaves.delete(bookmarkId);
- saveTabs([tab], { bookmarkId, bookmarkFolders });
- } else {
- if (url) {
- if (url == "about:blank") {
- browser.bookmarks.onChanged.addListener(onChanged);
- } else {
- saveUrl(url);
- }
- }
- }
- }
- }
- }
- }
- async function getParentFolders(id, folderNames = []) {
- if (id) {
- const bookmarkNode = (await browser.bookmarks.get(id))[0];
- if (bookmarkNode && bookmarkNode.title) {
- folderNames.unshift(bookmarkNode.title);
- await getParentFolders(bookmarkNode.parentId, folderNames);
- }
- }
- return folderNames;
- }
- function onChanged(id, changeInfo) {
- if (id == bookmarkId && changeInfo.url) {
- browser.bookmarks.onChanged.removeListener(onChanged);
- saveUrl(changeInfo.url);
- }
- }
- function saveUrl(url) {
- pendingSaves.delete(bookmarkId);
- saveUrls([url], { bookmarkId });
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global fetch */
- const urlService = "https://api.woleet.io/v1/anchor";
- const apiKey = "";
- async function anchor(hash, userKey) {
- let bearer = userKey || apiKey;
- const response = await fetch(urlService, {
- method: "POST",
- headers: {
- "Accept": "application/json",
- "Content-Type": "application/json",
- "Authorization": "Bearer " + bearer
- },
- body: JSON.stringify({
- "name": hash,
- "hash": hash,
- "public": true
- })
- });
- if (response.status == 401) {
- const error = new Error("Your access token on Woleet is invalid. Go to __DOC_LINK__ to create your account.");
- error.link = "https://app.woleet.io/";
- throw error;
- } else if (response.status == 402) {
- const error = new Error("You have no more credits on Woleet. Go to __DOC_LINK__ to recharge them.");
- error.link = "https://app.woleet.io/";
- throw error;
- } else if (response.status >= 400) {
- throw new Error((response.statusText || ("Error " + response.status)) + " (Woleet)");
- }
- return response.json();
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, fetch, setInterval, URLSearchParams, URL */
- const TOKEN_URL$1 = "https://oauth2.googleapis.com/token";
- const AUTH_URL$1 = "https://accounts.google.com/o/oauth2/v2/auth";
- const REVOKE_ACCESS_URL$1 = "https://accounts.google.com/o/oauth2/revoke";
- const GDRIVE_URL = "https://www.googleapis.com/drive/v3/files";
- const GDRIVE_UPLOAD_URL = "https://www.googleapis.com/upload/drive/v3/files";
- const CONFLICT_ACTION_UNIQUIFY$4 = "uniquify";
- const CONFLICT_ACTION_OVERWRITE$3 = "overwrite";
- const CONFLICT_ACTION_SKIP$4 = "skip";
- const CONFLICT_ACTION_PROMPT$3 = "prompt";
- class GDrive {
- constructor(clientId, clientKey, scopes) {
- this.clientId = clientId;
- this.clientKey = clientKey;
- this.scopes = scopes;
- this.folderIds = new Map();
- setInterval(() => this.folderIds.clear(), 60 * 1000);
- }
- async auth(options = { interactive: true }) {
- if (nativeAuth(options)) {
- this.accessToken = await browser.identity.getAuthToken({ interactive: options.interactive });
- return { revokableAccessToken: this.accessToken };
- } else {
- this.authURL = AUTH_URL$1 +
- "?client_id=" + this.clientId +
- "&response_type=code" +
- "&access_type=offline" +
- "&redirect_uri=" + browser.identity.getRedirectURL() +
- "&scope=" + this.scopes.join(" ");
- return options.code ? authFromCode$1(this, options) : initAuth$1(this, options);
- }
- }
- setAuthInfo(authInfo, options) {
- if (!nativeAuth(options)) {
- if (authInfo) {
- this.accessToken = authInfo.accessToken;
- this.refreshToken = authInfo.refreshToken;
- this.expirationDate = authInfo.expirationDate;
- } else {
- delete this.accessToken;
- delete this.refreshToken;
- delete this.expirationDate;
- }
- }
- }
- async refreshAuthToken() {
- if (this.refreshToken) {
- const httpResponse = await fetch(TOKEN_URL$1, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: "client_id=" + this.clientId +
- "&refresh_token=" + this.refreshToken +
- "&grant_type=refresh_token" +
- "&client_secret=" + this.clientKey
- });
- if (httpResponse.status == 400) {
- throw new Error("unknown_token");
- }
- const response = await getJSON$1(httpResponse);
- this.accessToken = response.access_token;
- if (response.refresh_token) {
- this.refreshToken = response.refresh_token;
- }
- if (response.expires_in) {
- this.expirationDate = Date.now() + (response.expires_in * 1000);
- }
- return { accessToken: this.accessToken, refreshToken: this.refreshToken, expirationDate: this.expirationDate };
- } else {
- try {
- if (browser.identity && browser.identity.removeCachedAuthToken && this.accessToken) {
- await browser.identity.removeCachedAuthToken({ token: this.accessToken });
- }
- this.accessToken = await browser.identity.getAuthToken({ interactive: false });
- return { revokableAccessToken: this.accessToken };
- } catch (error) {
- delete this.accessToken;
- }
- }
- }
- async revokeAuthToken(accessToken) {
- if (accessToken) {
- if (browser.identity && browser.identity.removeCachedAuthToken) {
- try {
- await browser.identity.removeCachedAuthToken({ token: accessToken });
- } catch (error) {
- // ignored
- }
- }
- const httpResponse = await fetch(REVOKE_ACCESS_URL$1, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: "token=" + accessToken
- });
- try {
- await getJSON$1(httpResponse);
- }
- catch (error) {
- if (error.message != "invalid_token") {
- throw error;
- }
- }
- finally {
- delete this.accessToken;
- delete this.refreshToken;
- delete this.expirationDate;
- }
- }
- }
- async upload(fullFilename, blob, options, setCancelCallback, retry = true) {
- const parentFolderId = await getParentFolderId(this, fullFilename);
- const fileParts = fullFilename.split("/");
- const filename = fileParts.pop();
- const uploader = new MediaUploader$1({
- token: this.accessToken,
- file: blob,
- parents: [parentFolderId],
- filename,
- onProgress: options.onProgress,
- filenameConflictAction: options.filenameConflictAction,
- prompt: options.prompt
- });
- try {
- if (setCancelCallback) {
- setCancelCallback(() => uploader.cancelled = true);
- }
- await uploader.upload();
- }
- catch (error) {
- if (error.message == "path_not_found" && retry) {
- this.folderIds.clear();
- return this.upload(fullFilename, blob, options, setCancelCallback);
- } else {
- throw error;
- }
- }
- }
- }
- class MediaUploader$1 {
- constructor(options) {
- this.file = options.file;
- this.onProgress = options.onProgress;
- this.contentType = this.file.type || "application/octet-stream";
- this.metadata = {
- name: options.filename,
- mimeType: this.contentType,
- parents: options.parents || ["root"]
- };
- this.token = options.token;
- this.offset = 0;
- this.chunkSize = options.chunkSize || 512 * 1024;
- this.filenameConflictAction = options.filenameConflictAction;
- this.prompt = options.prompt;
- }
- async upload(indexFilename = 1) {
- let method = "POST";
- let fileId;
- const httpListResponse = getResponse$1(await fetch(GDRIVE_URL + `?q=name = '${this.metadata.name}' and trashed != true and '${this.metadata.parents[0]}' in parents`, {
- headers: {
- "Authorization": "Bearer " + this.token,
- "Content-Type": "application/json"
- }
- }));
- const response = await httpListResponse.json();
- if (response.files.length) {
- if (this.filenameConflictAction == CONFLICT_ACTION_OVERWRITE$3) {
- method = "PATCH";
- fileId = response.files[0].id;
- this.metadata.parents = null;
- } else if (this.filenameConflictAction == CONFLICT_ACTION_UNIQUIFY$4) {
- let nameWithoutExtension = this.metadata.name;
- let extension = "";
- const dotIndex = this.metadata.name.lastIndexOf(".");
- if (dotIndex > -1) {
- nameWithoutExtension = this.metadata.name.substring(0, dotIndex);
- extension = this.metadata.name.substring(dotIndex + 1);
- }
- const name = nameWithoutExtension + " (" + indexFilename + ")." + extension;
- const httpResponse = getResponse$1(await fetch(GDRIVE_URL + `?q=name = '${name}' and trashed != true and '${this.metadata.parents[0]}' in parents`, {
- headers: {
- "Authorization": "Bearer " + this.token,
- "Content-Type": "application/json"
- }
- }));
- const response = await httpResponse.json();
- if (response.files.length) {
- return this.upload(indexFilename + 1);
- } else {
- this.metadata.name = name;
- }
- } else if (this.filenameConflictAction == CONFLICT_ACTION_PROMPT$3) {
- if (this.prompt) {
- const name = await this.prompt(this.metadata.name);
- if (name) {
- this.metadata.name = name;
- return this.upload(indexFilename);
- } else {
- return response;
- }
- } else {
- this.filenameConflictAction = CONFLICT_ACTION_UNIQUIFY$4;
- return this.upload(indexFilename);
- }
- } else if (this.filenameConflictAction == CONFLICT_ACTION_SKIP$4) {
- return response;
- }
- }
- const httpResponse = getResponse$1(await fetch(GDRIVE_UPLOAD_URL + (fileId ? "/" + fileId : "") + "?uploadType=resumable", {
- method,
- headers: {
- "Authorization": "Bearer " + this.token,
- "Content-Type": "application/json",
- "X-Upload-Content-Length": this.file.size,
- "X-Upload-Content-Type": this.contentType
- },
- body: JSON.stringify(this.metadata)
- }));
- const location = httpResponse.headers.get("Location");
- this.url = location;
- if (!this.cancelled) {
- if (this.onProgress) {
- this.onProgress(0, this.file.size);
- }
- return sendFile$1(this);
- }
- }
- }
- async function authFromCode$1(gdrive, options) {
- const httpResponse = await fetch(TOKEN_URL$1, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: "client_id=" + gdrive.clientId +
- "&client_secret=" + gdrive.clientKey +
- "&grant_type=authorization_code" +
- "&code=" + options.code +
- "&redirect_uri=" + browser.identity.getRedirectURL()
- });
- const response = await getJSON$1(httpResponse);
- gdrive.accessToken = response.access_token;
- gdrive.refreshToken = response.refresh_token;
- gdrive.expirationDate = Date.now() + (response.expires_in * 1000);
- return { accessToken: gdrive.accessToken, refreshToken: gdrive.refreshToken, expirationDate: gdrive.expirationDate };
- }
- async function initAuth$1(gdrive, options) {
- let code;
- try {
- if (browser.identity && browser.identity.launchWebAuthFlow && !options.forceWebAuthFlow) {
- const authURL = await browser.identity.launchWebAuthFlow({
- interactive: options.interactive,
- url: gdrive.authURL
- });
- options.code = new URLSearchParams(new URL(authURL).search).get("code");
- return await authFromCode$1(gdrive, options);
- } else if (options.launchWebAuthFlow) {
- options.extractAuthCode(browser.identity.getRedirectURL())
- .then(authCode => code = authCode)
- .catch(() => { /* ignored */ });
- return await options.launchWebAuthFlow({ url: gdrive.authURL });
- } else {
- throw new Error("auth_not_supported");
- }
- }
- catch (error) {
- if (error.message && (error.message == "code_required" || error.message.includes("access"))) {
- if (code) {
- options.code = code;
- return await authFromCode$1(gdrive, options);
- } else {
- throw new Error("code_required");
- }
- } else {
- throw error;
- }
- }
- }
- function nativeAuth(options = {}) {
- return Boolean(browser.identity && browser.identity.getAuthToken) && !options.forceWebAuthFlow;
- }
- async function getParentFolderId(gdrive, filename, retry = true) {
- const fileParts = filename.split("/");
- fileParts.pop();
- const folderId = gdrive.folderIds.get(fileParts.join("/"));
- if (folderId) {
- return folderId;
- }
- let parentFolderId = "root";
- if (fileParts.length) {
- let fullFolderName = "";
- for (const folderName of fileParts) {
- if (fullFolderName) {
- fullFolderName += "/";
- }
- fullFolderName += folderName;
- const folderId = gdrive.folderIds.get(fullFolderName);
- if (folderId) {
- parentFolderId = folderId;
- } else {
- try {
- parentFolderId = await getOrCreateFolder(gdrive, folderName, parentFolderId);
- gdrive.folderIds.set(fullFolderName, parentFolderId);
- } catch (error) {
- if (error.message == "path_not_found" && retry) {
- gdrive.folderIds.clear();
- return getParentFolderId(gdrive, filename, false);
- } else {
- throw error;
- }
- }
- }
- }
- }
- return parentFolderId;
- }
- async function getOrCreateFolder(gdrive, folderName, parentFolderId) {
- const response = await getFolder(gdrive, folderName, parentFolderId);
- if (response.files.length) {
- return response.files[0].id;
- } else {
- const response = await createFolder(gdrive, folderName, parentFolderId);
- return response.id;
- }
- }
- async function getFolder(gdrive, folderName, parentFolderId) {
- const httpResponse = await fetch(GDRIVE_URL + "?q=mimeType = 'application/vnd.google-apps.folder' and name = '" + folderName + "' and trashed != true and '" + parentFolderId + "' in parents", {
- headers: {
- "Authorization": "Bearer " + gdrive.accessToken
- }
- });
- return getJSON$1(httpResponse);
- }
- async function createFolder(gdrive, folderName, parentFolderId) {
- const httpResponse = await fetch(GDRIVE_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + gdrive.accessToken,
- "Content-Type": "application/json"
- },
- body: JSON.stringify({
- name: folderName,
- parents: [parentFolderId],
- mimeType: "application/vnd.google-apps.folder"
- })
- });
- return getJSON$1(httpResponse);
- }
- async function sendFile$1(mediaUploader) {
- let content = mediaUploader.file, end = mediaUploader.file.size;
- if (mediaUploader.offset || mediaUploader.chunkSize) {
- if (mediaUploader.chunkSize) {
- end = Math.min(mediaUploader.offset + mediaUploader.chunkSize, mediaUploader.file.size);
- }
- content = content.slice(mediaUploader.offset, end);
- }
- const httpResponse = await fetch(mediaUploader.url, {
- method: "PUT",
- headers: {
- "Authorization": "Bearer " + mediaUploader.token,
- "Content-Type": mediaUploader.contentType,
- "Content-Range": "bytes " + mediaUploader.offset + "-" + (end - 1) + "/" + mediaUploader.file.size,
- "X-Upload-Content-Type": mediaUploader.contentType
- },
- body: content
- });
- if (mediaUploader.onProgress && !mediaUploader.cancelled) {
- mediaUploader.onProgress(mediaUploader.offset + mediaUploader.chunkSize, mediaUploader.file.size);
- }
- if (httpResponse.status == 200 || httpResponse.status == 201) {
- return httpResponse.json();
- } else if (httpResponse.status == 308) {
- const range = httpResponse.headers.get("Range");
- if (range) {
- mediaUploader.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
- }
- if (mediaUploader.cancelled) {
- throw new Error("upload_cancelled");
- } else {
- return sendFile$1(mediaUploader);
- }
- } else {
- getResponse$1(httpResponse);
- }
- }
- async function getJSON$1(httpResponse) {
- httpResponse = getResponse$1(httpResponse);
- const response = await httpResponse.json();
- if (response.error) {
- throw new Error(response.error);
- } else {
- return response;
- }
- }
- function getResponse$1(httpResponse) {
- if (httpResponse.status == 200) {
- return httpResponse;
- } else if (httpResponse.status == 404) {
- throw new Error("path_not_found");
- } else if (httpResponse.status == 401) {
- throw new Error("invalid_token");
- } else {
- throw new Error("unknown_error (" + httpResponse.status + ")");
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, fetch */
- const TOKEN_URL = "https://api.dropboxapi.com/oauth2/token";
- const AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
- const REVOKE_ACCESS_URL = "https://api.dropboxapi.com/2/auth/token/revoke";
- const DROPBOX_SEARCH_URL = "https://api.dropboxapi.com/2/files/search_v2";
- const DROPBOX_UPLOAD_URL = "https://content.dropboxapi.com/2/files/upload_session/start";
- const DROPBOX_APPEND_URL = "https://content.dropboxapi.com/2/files/upload_session/append_v2";
- const DROPBOX_FINISH_URL = "https://content.dropboxapi.com/2/files/upload_session/finish";
- const CONFLICT_ACTION_UNIQUIFY$3 = "uniquify";
- const CONFLICT_ACTION_OVERWRITE$2 = "overwrite";
- const CONFLICT_ACTION_SKIP$3 = "skip";
- const CONFLICT_ACTION_PROMPT$2 = "prompt";
- const ENCODED_CHARS = /[\u007f-\uffff]/g;
- class Dropbox {
- constructor(clientId, clientKey) {
- this.clientId = clientId;
- this.clientKey = clientKey;
- }
- async auth(options = { interactive: true }) {
- this.authURL = AUTH_URL +
- "?client_id=" + this.clientId +
- "&response_type=code" +
- "&token_access_type=offline" +
- "&redirect_uri=" + browser.identity.getRedirectURL();
- return options.code ? authFromCode(this, options) : initAuth(this, options);
- }
- setAuthInfo(authInfo) {
- if (authInfo) {
- this.accessToken = authInfo.accessToken;
- this.refreshToken = authInfo.refreshToken;
- this.expirationDate = authInfo.expirationDate;
- } else {
- delete this.accessToken;
- delete this.refreshToken;
- delete this.expirationDate;
- }
- }
- async refreshAuthToken() {
- if (this.refreshToken) {
- const httpResponse = await fetch(TOKEN_URL, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: "client_id=" + this.clientId +
- "&refresh_token=" + this.refreshToken +
- "&grant_type=refresh_token" +
- "&client_secret=" + this.clientKey
- });
- if (httpResponse.status == 400) {
- throw new Error("unknown_token");
- }
- const response = await getJSON(httpResponse);
- this.accessToken = response.access_token;
- if (response.refresh_token) {
- this.refreshToken = response.refresh_token;
- }
- if (response.expires_in) {
- this.expirationDate = Date.now() + (response.expires_in * 1000);
- }
- return { accessToken: this.accessToken, refreshToken: this.refreshToken, expirationDate: this.expirationDate };
- } else {
- delete this.accessToken;
- }
- }
- async revokeAuthToken(accessToken) {
- if (accessToken) {
- const httpResponse = await fetch(REVOKE_ACCESS_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + accessToken
- }
- });
- try {
- await httpResponse.text();
- }
- catch (error) {
- if (error.message != "invalid_token") {
- throw error;
- }
- }
- finally {
- delete this.accessToken;
- delete this.refreshToken;
- delete this.expirationDate;
- }
- }
- }
- async upload(filename, blob, options, setCancelCallback) {
- const uploader = new MediaUploader({
- token: this.accessToken,
- file: blob,
- filename,
- onProgress: options.onProgress,
- filenameConflictAction: options.filenameConflictAction,
- prompt: options.prompt
- });
- if (setCancelCallback) {
- setCancelCallback(() => uploader.cancelled = true);
- }
- await uploader.upload();
- }
- }
- class MediaUploader {
- constructor(options) {
- this.file = options.file;
- this.onProgress = options.onProgress;
- this.contentType = this.file.type || "application/octet-stream";
- this.metadata = {
- name: options.filename,
- mimeType: this.contentType
- };
- this.token = options.token;
- this.offset = 0;
- this.chunkSize = options.chunkSize || 8 * 1024 * 1024;
- this.filenameConflictAction = options.filenameConflictAction;
- this.prompt = options.prompt;
- }
- async upload() {
- const httpListResponse = getResponse(await fetch(DROPBOX_SEARCH_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + this.token,
- "Content-Type": "application/json"
- },
- body: stringify({
- query: this.metadata.name,
- options: {
- filename: true
- }
- })
- }));
- const response = await getJSON(httpListResponse);
- if (response.matches.length) {
- if (this.filenameConflictAction == CONFLICT_ACTION_PROMPT$2) {
- if (this.prompt) {
- const name = await this.prompt(this.metadata.name);
- if (name) {
- this.metadata.name = name;
- } else {
- return response;
- }
- } else {
- this.filenameConflictAction = CONFLICT_ACTION_UNIQUIFY$3;
- }
- } else if (this.filenameConflictAction == CONFLICT_ACTION_SKIP$3) {
- return response;
- }
- }
- const httpResponse = getResponse(await fetch(DROPBOX_UPLOAD_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + this.token,
- "Dropbox-API-Arg": stringify({
- close: false
- }),
- "Content-Type": "application/octet-stream"
- }
- }));
- const sessionId = (await getJSON(httpResponse)).session_id;
- this.sessionId = sessionId;
- if (!this.cancelled) {
- if (this.onProgress) {
- this.onProgress(0, this.file.size);
- }
- return sendFile(this);
- }
- }
- }
- async function authFromCode(dropbox, options) {
- const httpResponse = await fetch(TOKEN_URL, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: "client_id=" + dropbox.clientId +
- "&client_secret=" + dropbox.clientKey +
- "&grant_type=authorization_code" +
- "&code=" + options.code +
- "&redirect_uri=" + browser.identity.getRedirectURL()
- });
- const response = await getJSON(httpResponse);
- dropbox.accessToken = response.access_token;
- dropbox.refreshToken = response.refresh_token;
- dropbox.expirationDate = Date.now() + (response.expires_in * 1000);
- return { accessToken: dropbox.accessToken, refreshToken: dropbox.refreshToken, expirationDate: dropbox.expirationDate };
- }
- async function initAuth(dropbox, options) {
- let code;
- try {
- options.extractAuthCode(browser.identity.getRedirectURL())
- .then(authCode => code = authCode)
- .catch(() => { /* ignored */ });
- return await options.launchWebAuthFlow({ url: dropbox.authURL });
- }
- catch (error) {
- if (error.message && (error.message == "code_required" || error.message.includes("access"))) {
- if (code) {
- options.code = code;
- return await authFromCode(dropbox, options);
- } else {
- throw new Error("code_required");
- }
- } else {
- throw error;
- }
- }
- }
- async function sendFile(mediaUploader) {
- let content = mediaUploader.file, end = mediaUploader.file.size;
- if (mediaUploader.offset || mediaUploader.chunkSize) {
- if (mediaUploader.chunkSize) {
- end = Math.min(mediaUploader.offset + mediaUploader.chunkSize, mediaUploader.file.size);
- }
- content = content.slice(mediaUploader.offset, end);
- }
- const httpAppendResponse = getResponse(await fetch(DROPBOX_APPEND_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + mediaUploader.token,
- "Content-Type": "application/octet-stream",
- "Dropbox-API-Arg": stringify({
- cursor: {
- session_id: mediaUploader.sessionId,
- offset: mediaUploader.offset
- },
- close: end == mediaUploader.file.size
- })
- },
- body: content
- }));
- if (mediaUploader.onProgress && !mediaUploader.cancelled) {
- mediaUploader.onProgress(mediaUploader.offset + mediaUploader.chunkSize, mediaUploader.file.size);
- }
- if (httpAppendResponse.status == 200) {
- mediaUploader.offset = end;
- if (mediaUploader.offset < mediaUploader.file.size) {
- return sendFile(mediaUploader);
- }
- }
- let path = mediaUploader.metadata.name;
- if (!path.startsWith("/")) {
- path = "/" + path;
- }
- const httpFinishResponse = await fetch(DROPBOX_FINISH_URL, {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + mediaUploader.token,
- "Content-Type": "application/octet-stream",
- "Dropbox-API-Arg": stringify({
- cursor: {
- session_id: mediaUploader.sessionId,
- offset: mediaUploader.offset
- },
- commit: {
- path,
- mode: mediaUploader.filenameConflictAction == CONFLICT_ACTION_OVERWRITE$2 ? "overwrite" : "add",
- autorename: mediaUploader.filenameConflictAction == CONFLICT_ACTION_UNIQUIFY$3
- }
- })
- }
- });
- if (httpFinishResponse.status == 200) {
- return getJSON(httpFinishResponse);
- } else if (httpFinishResponse.status == 409 && mediaUploader.filenameConflictAction == CONFLICT_ACTION_PROMPT$2) {
- mediaUploader.offset = 0;
- return mediaUploader.upload();
- } else {
- throw new Error("unknown_error (" + httpFinishResponse.status + ")");
- }
- }
- async function getJSON(httpResponse) {
- httpResponse = getResponse(httpResponse);
- const response = await httpResponse.json();
- if (response.error) {
- throw new Error(response.error);
- } else {
- return response;
- }
- }
- function getResponse(httpResponse) {
- if (httpResponse.status == 200) {
- return httpResponse;
- } else if (httpResponse.status == 401) {
- throw new Error("invalid_token");
- } else {
- throw new Error("unknown_error (" + httpResponse.status + ")");
- }
- }
- function stringify(value) {
- return JSON.stringify(value).replace(ENCODED_CHARS,
- function (c) {
- return "\\u" + ("000" + c.charCodeAt(0).toString(16)).slice(-4);
- }
- );
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global fetch, btoa, AbortController */
- const EMPTY_STRING$1 = "";
- const CONFLICT_ACTION_SKIP$2 = "skip";
- const CONFLICT_ACTION_UNIQUIFY$2 = "uniquify";
- const CONFLICT_ACTION_OVERWRITE$1 = "overwrite";
- const CONFLICT_ACTION_PROMPT$1 = "prompt";
- const BASIC_PREFIX_AUTHORIZATION = "Basic ";
- const AUTHORIZATION_HEADER$2 = "Authorization";
- const AUTHORIZATION_SEPARATOR = ":";
- const DIRECTORY_SEPARATOR = "/";
- const EXTENSION_SEPARATOR$1 = ".";
- const ERROR_PREFIX_MESSAGE = "Error ";
- const INDEX_FILENAME_PREFIX$1 = " (";
- const INDEX_FILENAME_SUFFIX$1 = ")";
- const INDEX_FILENAME_REGEXP$1 = /\s\((\d+)\)$/;
- const ABORT_ERROR_NAME$1 = "AbortError";
- const HEAD_METHOD = "HEAD";
- const PUT_METHOD$1 = "PUT";
- const DELETE_METHOD = "DELETE";
- const PROPFIND_METHOD = "PROPFIND";
- const MKCOL_METHOD = "MKCOL";
- const CONTENT_TYPE_HEADER = "Content-Type";
- const HTML_CONTENT_TYPE = "text/html";
- const CREDENTIALS_PARAMETER = "omit";
- const FOUND_STATUS = 200;
- const CREATED_STATUS = 201;
- const NOT_FOUND_STATUS = 404;
- const MIN_ERROR_STATUS = 400;
- class WebDAV {
- constructor(url, username, password) {
- if (!url.endsWith(DIRECTORY_SEPARATOR)) {
- url += DIRECTORY_SEPARATOR;
- }
- this.url = url;
- this.authorization = BASIC_PREFIX_AUTHORIZATION + btoa(username + AUTHORIZATION_SEPARATOR + password);
- }
- upload(filename, content, options) {
- this.controller = new AbortController();
- options.signal = this.controller.signal;
- options.authorization = this.authorization;
- options.url = this.url;
- return upload$1(filename, content, options);
- }
- abort() {
- if (this.controller) {
- this.controller.abort();
- }
- }
- }
- async function upload$1(filename, content, options) {
- const { authorization, filenameConflictAction, prompt, signal, preventRetry } = options;
- let { url } = options;
- try {
- if (filenameConflictAction == CONFLICT_ACTION_OVERWRITE$1) {
- let response = await sendRequest(filename, PUT_METHOD$1, content);
- if (response.status == CREATED_STATUS) {
- return response;
- } else if (response.status >= MIN_ERROR_STATUS) {
- response = await sendRequest(filename, DELETE_METHOD);
- if (response.status >= MIN_ERROR_STATUS) {
- throw new Error(ERROR_PREFIX_MESSAGE + response.status);
- }
- return await upload$1(filename, content, options);
- }
- } else {
- let response = await sendRequest(filename, HEAD_METHOD);
- if (response.status == FOUND_STATUS) {
- if (filenameConflictAction == CONFLICT_ACTION_UNIQUIFY$2 || (filenameConflictAction == CONFLICT_ACTION_PROMPT$1 && !prompt)) {
- const { filenameWithoutExtension, extension, indexFilename } = splitFilename(filename);
- options.indexFilename = indexFilename + 1;
- return await upload$1(getFilename(filenameWithoutExtension, extension), content, options);
- } else if (filenameConflictAction == CONFLICT_ACTION_PROMPT$1) {
- filename = await prompt(filename);
- return filename ? upload$1(filename, content, options) : response;
- } else if (filenameConflictAction == CONFLICT_ACTION_SKIP$2) {
- return response;
- }
- } else if (response.status == NOT_FOUND_STATUS) {
- response = await sendRequest(filename, PUT_METHOD$1, content);
- if (response.status >= MIN_ERROR_STATUS && !preventRetry) {
- if (filename.includes(DIRECTORY_SEPARATOR)) {
- await createDirectories();
- options.preventRetry = true;
- return await upload$1(filename, content, options);
- } else {
- throw new Error(ERROR_PREFIX_MESSAGE + response.status);
- }
- } else {
- return response;
- }
- } else if (response.status >= MIN_ERROR_STATUS) {
- throw new Error(ERROR_PREFIX_MESSAGE + response.status);
- }
- }
- } catch (error) {
- if (error.name != ABORT_ERROR_NAME$1) {
- throw error;
- }
- }
- function sendRequest(path, method, body) {
- const headers = {
- [AUTHORIZATION_HEADER$2]: authorization
- };
- if (body) {
- headers[CONTENT_TYPE_HEADER] = HTML_CONTENT_TYPE;
- }
- return fetch(url + path, { method, headers, signal, body, credentials: CREDENTIALS_PARAMETER });
- }
- function splitFilename(filename) {
- let filenameWithoutExtension = filename;
- let extension = EMPTY_STRING$1;
- const indexExtensionSeparator = filename.lastIndexOf(EXTENSION_SEPARATOR$1);
- if (indexExtensionSeparator > -1) {
- filenameWithoutExtension = filename.substring(0, indexExtensionSeparator);
- extension = filename.substring(indexExtensionSeparator + 1);
- }
- let indexFilename;
- ({ filenameWithoutExtension, indexFilename } = extractIndexFilename(filenameWithoutExtension));
- return { filenameWithoutExtension, extension, indexFilename };
- }
- function extractIndexFilename(filenameWithoutExtension) {
- const indexFilenameMatch = filenameWithoutExtension.match(INDEX_FILENAME_REGEXP$1);
- let indexFilename = 0;
- if (indexFilenameMatch && indexFilenameMatch.length > 1) {
- const parsedIndexFilename = Number(indexFilenameMatch[indexFilenameMatch.length - 1]);
- if (!Number.isNaN(parsedIndexFilename)) {
- indexFilename = parsedIndexFilename;
- filenameWithoutExtension = filenameWithoutExtension.replace(INDEX_FILENAME_REGEXP$1, EMPTY_STRING$1);
- }
- }
- return { filenameWithoutExtension, indexFilename };
- }
- function getFilename(filenameWithoutExtension, extension) {
- return filenameWithoutExtension +
- INDEX_FILENAME_PREFIX$1 + options.indexFilename + INDEX_FILENAME_SUFFIX$1 +
- (extension ? EXTENSION_SEPARATOR$1 + extension : EMPTY_STRING$1);
- }
- async function createDirectories() {
- const filenameParts = filename.split(DIRECTORY_SEPARATOR);
- filenameParts.pop();
- let path = EMPTY_STRING$1;
- for (const filenamePart of filenameParts) {
- if (filenamePart) {
- path += filenamePart;
- const response = await sendRequest(path, PROPFIND_METHOD);
- if (response.status == NOT_FOUND_STATUS) {
- const response = await sendRequest(path, MKCOL_METHOD);
- if (response.status >= MIN_ERROR_STATUS) {
- throw new Error(ERROR_PREFIX_MESSAGE + response.status);
- }
- }
- path += DIRECTORY_SEPARATOR;
- }
- }
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global fetch, btoa, Blob, FileReader, AbortController */
- const EMPTY_STRING = "";
- const CONFLICT_ACTION_SKIP$1 = "skip";
- const CONFLICT_ACTION_UNIQUIFY$1 = "uniquify";
- const CONFLICT_ACTION_OVERWRITE = "overwrite";
- const CONFLICT_ACTION_PROMPT = "prompt";
- const AUTHORIZATION_HEADER$1 = "Authorization";
- const BEARER_PREFIX_AUTHORIZATION$1 = "Bearer ";
- const ACCEPT_HEADER$1 = "Accept";
- const GITHUB_API_CONTENT_TYPE = "application/vnd.github+json";
- const GITHUB_API_VERSION_HEADER = "X-GitHub-Api-Version";
- const GITHUB_API_VERSION = "2022-11-28";
- const EXTENSION_SEPARATOR = ".";
- const INDEX_FILENAME_PREFIX = " (";
- const INDEX_FILENAME_SUFFIX = ")";
- const INDEX_FILENAME_REGEXP = /\s\((\d+)\)$/;
- const ABORT_ERROR_NAME = "AbortError";
- const GET_METHOD = "GET";
- const PUT_METHOD = "PUT";
- const GITHUB_URL = "https://github.com";
- const GITHUB_API_URL = "https://api.github.com";
- const BLOB_PATH = "blob";
- const REPOS_PATH = "repos";
- const CONTENTS_PATH = "contents";
- let pendingPush;
- class GitHub {
- constructor(token, userName, repositoryName, branch) {
- this.headers = new Map([
- [AUTHORIZATION_HEADER$1, BEARER_PREFIX_AUTHORIZATION$1 + token],
- [ACCEPT_HEADER$1, GITHUB_API_CONTENT_TYPE],
- [GITHUB_API_VERSION_HEADER, GITHUB_API_VERSION]
- ]);
- this.userName = userName;
- this.repositoryName = repositoryName;
- this.branch = branch;
- }
- async upload(path, content, options) {
- this.controller = new AbortController();
- options.signal = this.controller.signal;
- options.headers = this.headers;
- const base64Content = content instanceof Blob ? await blobToBase64(content) : btoa(unescape(encodeURIComponent(content)));
- return upload(this.userName, this.repositoryName, this.branch, path, base64Content, options);
- }
- abort() {
- if (this.controller) {
- this.controller.abort();
- }
- }
- }
- async function upload(userName, repositoryName, branch, path, content, options) {
- const { filenameConflictAction, prompt, signal, headers } = options;
- while (pendingPush) {
- await pendingPush;
- }
- try {
- pendingPush = await createContent({ path, content });
- } finally {
- pendingPush = null;
- }
- return {
- url: `${GITHUB_URL}/${userName}/${repositoryName}/${BLOB_PATH}/${branch}/${path}`
- };
- async function createContent({ path, content, message = EMPTY_STRING, sha }) {
- try {
- const response = await fetchContentData(PUT_METHOD, JSON.stringify({
- content,
- message,
- branch,
- sha
- }));
- const responseData = await response.json();
- if (response.status == 422) {
- if (filenameConflictAction == CONFLICT_ACTION_OVERWRITE) {
- const response = await fetchContentData(GET_METHOD);
- const responseData = await response.json();
- const sha = responseData.sha;
- return await createContent({ path, content, message, sha });
- } else if (filenameConflictAction == CONFLICT_ACTION_UNIQUIFY$1) {
- const { filenameWithoutExtension, extension, indexFilename } = splitFilename(path);
- options.indexFilename = indexFilename + 1;
- path = getFilename(filenameWithoutExtension, extension);
- return await createContent({ path, content, message });
- } else if (filenameConflictAction == CONFLICT_ACTION_SKIP$1) {
- return responseData;
- } else if (filenameConflictAction == CONFLICT_ACTION_PROMPT) {
- if (prompt) {
- path = await prompt(path);
- if (path) {
- return await createContent({ path, content, message });
- } else {
- return responseData;
- }
- } else {
- options.filenameConflictAction = CONFLICT_ACTION_UNIQUIFY$1;
- return await createContent({ path, content, message });
- }
- }
- }
- if (response.status < 400) {
- return responseData;
- } else {
- throw new Error(responseData.message);
- }
- } catch (error) {
- if (error.name != ABORT_ERROR_NAME) {
- throw error;
- }
- }
- function fetchContentData(method, body) {
- return fetch(`${GITHUB_API_URL}/${REPOS_PATH}/${userName}/${repositoryName}/${CONTENTS_PATH}/${path}`, {
- method,
- headers,
- body,
- signal
- });
- }
- }
- function splitFilename(filename) {
- let filenameWithoutExtension = filename;
- let extension = EMPTY_STRING;
- const indexExtensionSeparator = filename.lastIndexOf(EXTENSION_SEPARATOR);
- if (indexExtensionSeparator > -1) {
- filenameWithoutExtension = filename.substring(0, indexExtensionSeparator);
- extension = filename.substring(indexExtensionSeparator + 1);
- }
- let indexFilename;
- ({ filenameWithoutExtension, indexFilename } = extractIndexFilename(filenameWithoutExtension));
- return { filenameWithoutExtension, extension, indexFilename };
- }
- function extractIndexFilename(filenameWithoutExtension) {
- const indexFilenameMatch = filenameWithoutExtension.match(INDEX_FILENAME_REGEXP);
- let indexFilename = 0;
- if (indexFilenameMatch && indexFilenameMatch.length > 1) {
- const parsedIndexFilename = Number(indexFilenameMatch[indexFilenameMatch.length - 1]);
- if (!Number.isNaN(parsedIndexFilename)) {
- indexFilename = parsedIndexFilename;
- filenameWithoutExtension = filenameWithoutExtension.replace(INDEX_FILENAME_REGEXP, EMPTY_STRING);
- }
- }
- return { filenameWithoutExtension, indexFilename };
- }
- function getFilename(filenameWithoutExtension, extension) {
- return filenameWithoutExtension +
- INDEX_FILENAME_PREFIX + options.indexFilename + INDEX_FILENAME_SUFFIX +
- (extension ? EXTENSION_SEPARATOR + extension : EMPTY_STRING);
- }
- }
- function blobToBase64(blob) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onloadend = () => resolve(reader.result.match(/^data:[^,]+,(.*)$/)[1]);
- reader.onerror = event => reject(event.detail);
- reader.readAsDataURL(blob);
- });
- }
- /* global TextEncoder, TextDecoder */
- const DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;
- const TYPE_REFERENCE = 0;
- const SPECIAL_TYPES = [TYPE_REFERENCE];
- const EMPTY_SLOT_VALUE = Symbol();
- const textEncoder = new TextEncoder();
- const textDecoder = new TextDecoder();
- const types = new Array(256);
- let typeIndex = 0;
- registerType(serializeCircularReference, parseCircularReference, testCircularReference, TYPE_REFERENCE);
- registerType(null, parseObject, testObject);
- registerType(serializeArray, parseArray, testArray);
- registerType(serializeString, parseString, testString);
- registerType(serializeTypedArray, parseFloat64Array, testFloat64Array);
- registerType(serializeTypedArray, parseFloat32Array, testFloat32Array);
- registerType(serializeTypedArray, parseUint32Array, testUint32Array);
- registerType(serializeTypedArray, parseInt32Array, testInt32Array);
- registerType(serializeTypedArray, parseUint16Array, testUint16Array);
- registerType(serializeTypedArray, parseInt16Array, testInt16Array);
- registerType(serializeTypedArray, parseUint8ClampedArray, testUint8ClampedArray);
- registerType(serializeTypedArray, parseUint8Array, testUint8Array);
- registerType(serializeTypedArray, parseInt8Array, testInt8Array);
- registerType(serializeArrayBuffer, parseArrayBuffer, testArrayBuffer);
- registerType(serializeNumber, parseNumber, testNumber);
- registerType(serializeUint32, parseUint32, testUint32);
- registerType(serializeInt32, parseInt32, testInt32);
- registerType(serializeUint16, parseUint16, testUint16);
- registerType(serializeInt16, parseInt16, testInt16);
- registerType(serializeUint8, parseUint8, testUint8);
- registerType(serializeInt8, parseInt8, testInt8);
- registerType(null, parseUndefined, testUndefined);
- registerType(null, parseNull, testNull);
- registerType(null, parseNaN, testNaN);
- registerType(serializeBoolean, parseBoolean, testBoolean);
- registerType(serializeSymbol, parseSymbol, testSymbol);
- registerType(null, parseEmptySlot, testEmptySlot);
- registerType(serializeMap, parseMap, testMap);
- registerType(serializeSet, parseSet, testSet);
- registerType(serializeDate, parseDate, testDate);
- registerType(serializeError, parseError, testError);
- registerType(serializeRegExp, parseRegExp, testRegExp);
- registerType(serializeStringObject, parseStringObject, testStringObject);
- registerType(serializeNumberObject, parseNumberObject, testNumberObject);
- registerType(serializeBooleanObject, parseBooleanObject, testBooleanObject);
- function registerType(serialize, parse, test, type) {
- if (type === undefined) {
- typeIndex++;
- if (types.length - typeIndex >= SPECIAL_TYPES.length) {
- types[types.length - typeIndex] = { serialize, parse, test };
- } else {
- throw new Error("Reached maximum number of custom types");
- }
- } else {
- types[type] = { serialize, parse, test };
- }
- }
- async function parse(array) {
- const parser = getParser();
- await parser.next(array);
- const result = await parser.next();
- return result.value;
- }
- class SerializerData {
- constructor(appendData, chunkSize) {
- this.stream = new WriteStream(appendData, chunkSize);
- this.objects = [];
- }
- append(array) {
- return this.stream.append(array);
- }
- flush() {
- return this.stream.flush();
- }
- addObject(value) {
- this.objects.push(testReferenceable(value) && !testCircularReference(value, this) ? value : undefined);
- }
- }
- class WriteStream {
- constructor(appendData, chunkSize) {
- this.offset = 0;
- this.appendData = appendData;
- this.value = new Uint8Array(chunkSize);
- }
- async append(array) {
- if (this.offset + array.length > this.value.length) {
- const offset = this.value.length - this.offset;
- await this.append(array.subarray(0, offset));
- await this.appendData({ value: this.value });
- this.offset = 0;
- await this.append(array.subarray(offset));
- } else {
- this.value.set(array, this.offset);
- this.offset += array.length;
- }
- }
- async flush() {
- if (this.offset) {
- await this.appendData({ value: this.value.subarray(0, this.offset), done: true });
- }
- }
- }
- function getSerializer(value, { chunkSize = DEFAULT_CHUNK_SIZE } = {}) {
- let serializerData, result, setResult, iterationDone, previousResult, resolvePreviousResult;
- return {
- [Symbol.asyncIterator]() {
- return {
- next() {
- return iterationDone ? { done: iterationDone } : getResult();
- },
- return() {
- return { done: true };
- }
- };
- }
- };
- async function getResult() {
- if (resolvePreviousResult) {
- resolvePreviousResult();
- } else {
- initSerializerData().catch(() => { /* ignored */ });
- }
- initPreviousData();
- const value = await getValue();
- return { value };
- }
- async function initSerializerData() {
- initResult();
- serializerData = new SerializerData(appendData, chunkSize);
- await serializeValue(serializerData, value);
- await serializerData.flush();
- }
- function initResult() {
- result = new Promise(resolve => setResult = resolve);
- }
- function initPreviousData() {
- previousResult = new Promise(resolve => resolvePreviousResult = resolve);
- }
- async function appendData(result) {
- setResult(result);
- await previousResult;
- }
- async function getValue() {
- const { value, done } = await result;
- iterationDone = done;
- if (!done) {
- initResult();
- }
- return value;
- }
- }
- async function serializeValue(data, value) {
- const type = types.findIndex(({ test } = {}) => test && test(value, data));
- data.addObject(value);
- await data.append(new Uint8Array([type]));
- const serialize = types[type].serialize;
- if (serialize) {
- await serialize(data, value);
- }
- if (type != TYPE_REFERENCE && testObject(value)) {
- await serializeSymbols(data, value);
- await serializeOwnProperties(data, value);
- }
- }
- async function serializeSymbols(data, value) {
- const ownPropertySymbols = Object.getOwnPropertySymbols(value);
- const symbols = ownPropertySymbols.map(propertySymbol => [propertySymbol, value[propertySymbol]]);
- await serializeArray(data, symbols);
- }
- async function serializeOwnProperties(data, value) {
- if (!ArrayBuffer.isView(value)) {
- let entries = Object.entries(value);
- if (testArray(value)) {
- entries = entries.filter(([key]) => !testInteger(Number(key)));
- }
- await serializeValue(data, entries.length);
- for (const [key, value] of entries) {
- await serializeString(data, key);
- await serializeValue(data, value);
- }
- } else {
- await serializeValue(data, 0);
- }
- }
- async function serializeCircularReference(data, value) {
- const index = data.objects.indexOf(value);
- await serializeValue(data, index);
- }
- async function serializeArray(data, array) {
- await serializeValue(data, array.length);
- const notEmptyIndexes = Object.keys(array).filter(key => testInteger(Number(key))).map(key => Number(key));
- let indexNotEmptyIndexes = 0, currentNotEmptyIndex = notEmptyIndexes[indexNotEmptyIndexes];
- for (const [indexArray, value] of array.entries()) {
- if (currentNotEmptyIndex == indexArray) {
- currentNotEmptyIndex = notEmptyIndexes[++indexNotEmptyIndexes];
- await serializeValue(data, value);
- } else {
- await serializeValue(data, EMPTY_SLOT_VALUE);
- }
- }
- }
- async function serializeString(data, string) {
- const encodedString = textEncoder.encode(string);
- await serializeValue(data, encodedString.length);
- await data.append(encodedString);
- }
- async function serializeTypedArray(data, array) {
- await serializeValue(data, array.length);
- await data.append(array.constructor.name == "Uint8Array" ? array : new Uint8Array(array.buffer));
- }
- async function serializeArrayBuffer(data, arrayBuffer) {
- await serializeValue(data, arrayBuffer.byteLength);
- await data.append(new Uint8Array(arrayBuffer));
- }
- async function serializeNumber(data, number) {
- const serializedNumber = new Uint8Array(new Float64Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeUint32(data, number) {
- const serializedNumber = new Uint8Array(new Uint32Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeInt32(data, number) {
- const serializedNumber = new Uint8Array(new Int32Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeUint16(data, number) {
- const serializedNumber = new Uint8Array(new Uint16Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeInt16(data, number) {
- const serializedNumber = new Uint8Array(new Int16Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeUint8(data, number) {
- const serializedNumber = new Uint8Array([number]);
- await data.append(serializedNumber);
- }
- async function serializeInt8(data, number) {
- const serializedNumber = new Uint8Array(new Int8Array([number]).buffer);
- await data.append(serializedNumber);
- }
- async function serializeBoolean(data, boolean) {
- const serializedBoolean = new Uint8Array([Number(boolean)]);
- await data.append(serializedBoolean);
- }
- async function serializeMap(data, map) {
- const entries = map.entries();
- await serializeValue(data, map.size);
- for (const [key, value] of entries) {
- await serializeValue(data, key);
- await serializeValue(data, value);
- }
- }
- async function serializeSet(data, set) {
- await serializeValue(data, set.size);
- for (const value of set) {
- await serializeValue(data, value);
- }
- }
- async function serializeDate(data, date) {
- await serializeNumber(data, date.getTime());
- }
- async function serializeError(data, error) {
- await serializeString(data, error.message);
- await serializeString(data, error.stack);
- }
- async function serializeRegExp(data, regExp) {
- await serializeString(data, regExp.source);
- await serializeString(data, regExp.flags);
- }
- async function serializeStringObject(data, string) {
- await serializeString(data, string.valueOf());
- }
- async function serializeNumberObject(data, number) {
- await serializeNumber(data, number.valueOf());
- }
- async function serializeBooleanObject(data, boolean) {
- await serializeBoolean(data, boolean.valueOf());
- }
- async function serializeSymbol(data, symbol) {
- await serializeString(data, symbol.description);
- }
- class Reference {
- constructor(index, data) {
- this.index = index;
- this.data = data;
- }
- getObject() {
- return this.data.objects[this.index];
- }
- }
- class ParserData {
- constructor(consumeData) {
- this.stream = new ReadStream(consumeData);
- this.objects = [];
- this.setters = [];
- }
- consume(size) {
- return this.stream.consume(size);
- }
- getObjectId() {
- const objectIndex = this.objects.length;
- this.objects.push(undefined);
- return objectIndex;
- }
- resolveObject(objectId, value) {
- if (testReferenceable(value) && !testReference(value)) {
- this.objects[objectId] = value;
- }
- }
- setObject(functionArguments, setterFunction) {
- this.setters.push({ functionArguments, setterFunction });
- }
- executeSetters() {
- this.setters.forEach(({ functionArguments, setterFunction }) => {
- const resolvedArguments = functionArguments.map(argument => testReference(argument) ? argument.getObject() : argument);
- setterFunction(...resolvedArguments);
- });
- }
- }
- class ReadStream {
- constructor(consumeData) {
- this.offset = 0;
- this.value = new Uint8Array(0);
- this.consumeData = consumeData;
- }
- async consume(size) {
- if (this.offset + size > this.value.length) {
- const pending = this.value.subarray(this.offset, this.value.length);
- const value = await this.consumeData();
- if (pending.length + value.length != this.value.length) {
- this.value = new Uint8Array(pending.length + value.length);
- }
- this.value.set(pending);
- this.value.set(value, pending.length);
- this.offset = 0;
- return this.consume(size);
- } else {
- const result = this.value.slice(this.offset, this.offset + size);
- this.offset += result.length;
- return result;
- }
- }
- }
- function getParser() {
- let parserData, input, setInput, value, previousData, resolvePreviousData;
- return {
- async next(input) {
- return input ? getResult(input) : { value: await value, done: true };
- },
- return() {
- return { done: true };
- }
- };
- async function getResult(input) {
- if (previousData) {
- await previousData;
- } else {
- initParserData().catch(() => { /* ignored */ });
- }
- initPreviousData();
- setInput(input);
- return { done: false };
- }
- async function initParserData() {
- let setValue;
- value = new Promise(resolve => setValue = resolve);
- parserData = new ParserData(consumeData);
- initChunk();
- const data = await parseValue(parserData);
- parserData.executeSetters();
- setValue(data);
- }
- function initChunk() {
- input = new Promise(resolve => setInput = resolve);
- }
- function initPreviousData() {
- previousData = new Promise(resolve => resolvePreviousData = resolve);
- }
- async function consumeData() {
- const data = await input;
- initChunk();
- if (resolvePreviousData) {
- resolvePreviousData();
- }
- return data;
- }
- }
- async function parseValue(data) {
- const array = await data.consume(1);
- const parserType = array[0];
- const parse = types[parserType].parse;
- const valueId = data.getObjectId();
- const result = await parse(data);
- if (parserType != TYPE_REFERENCE && testObject(result)) {
- await parseSymbols(data, result);
- await parseOwnProperties(data, result);
- }
- data.resolveObject(valueId, result);
- return result;
- }
- async function parseSymbols(data, value) {
- const symbols = await parseArray(data);
- data.setObject([symbols], symbols => symbols.forEach(([symbol, propertyValue]) => value[symbol] = propertyValue));
- }
- async function parseOwnProperties(data, object) {
- const size = await parseValue(data);
- if (size) {
- await parseNextProperty();
- }
- async function parseNextProperty(indexKey = 0) {
- const key = await parseString(data);
- const value = await parseValue(data);
- data.setObject([value], value => object[key] = value);
- if (indexKey < size - 1) {
- await parseNextProperty(indexKey + 1);
- }
- }
- }
- async function parseCircularReference(data) {
- const index = await parseValue(data);
- const result = new Reference(index, data);
- return result;
- }
- function parseObject() {
- return {};
- }
- async function parseArray(data) {
- const length = await parseValue(data);
- const array = new Array(length);
- if (length) {
- await parseNextSlot();
- }
- return array;
- async function parseNextSlot(indexArray = 0) {
- const value = await parseValue(data);
- if (!testEmptySlot(value)) {
- data.setObject([value], value => array[indexArray] = value);
- }
- if (indexArray < length - 1) {
- await parseNextSlot(indexArray + 1);
- }
- }
- }
- function parseEmptySlot() {
- return EMPTY_SLOT_VALUE;
- }
- async function parseString(data) {
- const size = await parseValue(data);
- const array = await data.consume(size);
- return textDecoder.decode(array);
- }
- async function parseFloat64Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 8);
- return new Float64Array(array.buffer);
- }
- async function parseFloat32Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 4);
- return new Float32Array(array.buffer);
- }
- async function parseUint32Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 4);
- return new Uint32Array(array.buffer);
- }
- async function parseInt32Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 4);
- return new Int32Array(array.buffer);
- }
- async function parseUint16Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 2);
- return new Uint16Array(array.buffer);
- }
- async function parseInt16Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length * 2);
- return new Int16Array(array.buffer);
- }
- async function parseUint8ClampedArray(data) {
- const length = await parseValue(data);
- const array = await data.consume(length);
- return new Uint8ClampedArray(array.buffer);
- }
- async function parseUint8Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length);
- return array;
- }
- async function parseInt8Array(data) {
- const length = await parseValue(data);
- const array = await data.consume(length);
- return new Int8Array(array.buffer);
- }
- async function parseArrayBuffer(data) {
- const length = await parseValue(data);
- const array = await data.consume(length);
- return array.buffer;
- }
- async function parseNumber(data) {
- const array = await data.consume(8);
- return new Float64Array(array.buffer)[0];
- }
- async function parseUint32(data) {
- const array = await data.consume(4);
- return new Uint32Array(array.buffer)[0];
- }
- async function parseInt32(data) {
- const array = await data.consume(4);
- return new Int32Array(array.buffer)[0];
- }
- async function parseUint16(data) {
- const array = await data.consume(2);
- return new Uint16Array(array.buffer)[0];
- }
- async function parseInt16(data) {
- const array = await data.consume(2);
- return new Int16Array(array.buffer)[0];
- }
- async function parseUint8(data) {
- const array = await data.consume(1);
- return new Uint8Array(array.buffer)[0];
- }
- async function parseInt8(data) {
- const array = await data.consume(1);
- return new Int8Array(array.buffer)[0];
- }
- function parseUndefined() {
- return undefined;
- }
- function parseNull() {
- return null;
- }
- function parseNaN() {
- return NaN;
- }
- async function parseBoolean(data) {
- const array = await data.consume(1);
- return Boolean(array[0]);
- }
- async function parseMap(data) {
- const size = await parseValue(data);
- const map = new Map();
- if (size) {
- await parseNextEntry();
- }
- return map;
- async function parseNextEntry(indexKey = 0) {
- const key = await parseValue(data);
- const value = await parseValue(data);
- data.setObject([key, value], (key, value) => map.set(key, value));
- if (indexKey < size - 1) {
- await parseNextEntry(indexKey + 1);
- }
- }
- }
- async function parseSet(data) {
- const size = await parseValue(data);
- const set = new Set();
- if (size) {
- await parseNextEntry();
- }
- return set;
- async function parseNextEntry(indexKey = 0) {
- const value = await parseValue(data);
- data.setObject([value], value => set.add(value));
- if (indexKey < size - 1) {
- await parseNextEntry(indexKey + 1);
- }
- }
- }
- async function parseDate(data) {
- const milliseconds = await parseNumber(data);
- return new Date(milliseconds);
- }
- async function parseError(data) {
- const message = await parseString(data);
- const stack = await parseString(data);
- const error = new Error(message);
- error.stack = stack;
- return error;
- }
- async function parseRegExp(data) {
- const source = await parseString(data);
- const flags = await parseString(data);
- return new RegExp(source, flags);
- }
- async function parseStringObject(data) {
- return new String(await parseString(data));
- }
- async function parseNumberObject(data) {
- return new Number(await parseNumber(data));
- }
- async function parseBooleanObject(data) {
- return new Boolean(await parseBoolean(data));
- }
- async function parseSymbol(data) {
- const description = await parseString(data);
- return Symbol(description);
- }
- function testCircularReference(value, data) {
- return testObject(value) && data.objects.includes(value);
- }
- function testReference(value) {
- return value instanceof Reference;
- }
- function testObject(value) {
- return value === Object(value);
- }
- function testArray(value) {
- return typeof value.length == "number";
- }
- function testEmptySlot(value) {
- return value === EMPTY_SLOT_VALUE;
- }
- function testString(value) {
- return typeof value == "string";
- }
- function testFloat64Array(value) {
- return value.constructor.name == "Float64Array";
- }
- function testUint32Array(value) {
- return value.constructor.name == "Uint32Array";
- }
- function testInt32Array(value) {
- return value.constructor.name == "Int32Array";
- }
- function testUint16Array(value) {
- return value.constructor.name == "Uint16Array";
- }
- function testFloat32Array(value) {
- return value.constructor.name == "Float32Array";
- }
- function testInt16Array(value) {
- return value.constructor.name == "Int16Array";
- }
- function testUint8ClampedArray(value) {
- return value.constructor.name == "Uint8ClampedArray";
- }
- function testUint8Array(value) {
- return value.constructor.name == "Uint8Array";
- }
- function testInt8Array(value) {
- return value.constructor.name == "Int8Array";
- }
- function testArrayBuffer(value) {
- return value.constructor.name == "ArrayBuffer";
- }
- function testNumber(value) {
- return typeof value == "number";
- }
- function testUint32(value) {
- return testInteger(value) && value >= 0 && value <= 4294967295;
- }
- function testInt32(value) {
- return testInteger(value) && value >= -2147483648 && value <= 2147483647;
- }
- function testUint16(value) {
- return testInteger(value) && value >= 0 && value <= 65535;
- }
- function testInt16(value) {
- return testInteger(value) && value >= -32768 && value <= 32767;
- }
- function testUint8(value) {
- return testInteger(value) && value >= 0 && value <= 255;
- }
- function testInt8(value) {
- return testInteger(value) && value >= -128 && value <= 127;
- }
- function testInteger(value) {
- return testNumber(value) && Number.isInteger(value);
- }
- function testUndefined(value) {
- return value === undefined;
- }
- function testNull(value) {
- return value === null;
- }
- function testNaN(value) {
- return Number.isNaN(value);
- }
- function testBoolean(value) {
- return typeof value == "boolean";
- }
- function testMap(value) {
- return value instanceof Map;
- }
- function testSet(value) {
- return value instanceof Set;
- }
- function testDate(value) {
- return value instanceof Date;
- }
- function testError(value) {
- return value instanceof Error;
- }
- function testRegExp(value) {
- return value instanceof RegExp;
- }
- function testStringObject(value) {
- return value instanceof String;
- }
- function testNumberObject(value) {
- return value instanceof Number;
- }
- function testBooleanObject(value) {
- return value instanceof Boolean;
- }
- function testSymbol(value) {
- return typeof value == "symbol";
- }
- function testReferenceable(value) {
- return testObject(value) || testSymbol(value);
- }
- /*
- * Copyright 2010-2024 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- * author: gildas.lormeau <at> gmail.com
- * author: dcardin2007 <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global fetch, btoa, Blob, FileReader, AbortController */
- const AUTHORIZATION_HEADER = "Authorization";
- const BEARER_PREFIX_AUTHORIZATION = "Bearer ";
- const ACCEPT_HEADER = "Accept";
- const CONTENT_TYPE = "multipart/form-data";
- class RestFormApi {
- constructor(token, restApiUrl, fileFieldName, urlFieldName) {
- this.headers = new Map([
- [AUTHORIZATION_HEADER, BEARER_PREFIX_AUTHORIZATION + token],
- [ACCEPT_HEADER, CONTENT_TYPE]
- ]);
- this.restApiUrl = restApiUrl;
- this.fileFieldName = fileFieldName;
- this.urlFieldName = urlFieldName;
- }
- async upload(content, url) {
- this.controller = new AbortController();
- try{
- const blob = new Blob([content], { type: 'text/html'});
- const file = new File([blob], "SingleFile.html", { type: 'text/html' });
- let formData = new FormData();
- if(this.fileFieldName){
- formData.append(this.fileFieldName, file);
- }
- if(this.urlFieldName){
- formData.append(this.urlFieldName, url);
- }
- const response = await fetch(this.restApiUrl, {
- method: 'POST',
- body: formData,
- headers: this.headers,
- signal: this.controller.signal
- });
- if ([200,201].includes(response.status)) {
- // do something with the data?
- const data = await response.json();
- } else {
- throw new Error(await response.text());
- }
- }
- catch(e){
- throw new Error(e);
- }
- }
- abort() {
- if (this.controller) {
- this.controller.abort();
- }
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const partialContents = new Map();
- const tabData = new Map();
- const SCOPES = ["https://www.googleapis.com/auth/drive.file"];
- const CONFLICT_ACTION_SKIP = "skip";
- const CONFLICT_ACTION_UNIQUIFY = "uniquify";
- const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
- let GDRIVE_CLIENT_ID = "207618107333-h1220p1oasj3050kr5r416661adm091a.apps.googleusercontent.com";
- let GDRIVE_CLIENT_KEY = "VQJ8Gq8Vxx72QyxPyeLtWvUt";
- const DROPBOX_CLIENT_ID = "s50p6litdvuzrtb";
- const DROPBOX_CLIENT_KEY = "i1vzwllesr14fzd";
- const gDriveOauth2 = browser.runtime.getManifest().oauth2;
- if (gDriveOauth2) {
- GDRIVE_CLIENT_ID = gDriveOauth2.client_id;
- GDRIVE_CLIENT_KEY = gDriveOauth2.client_secret;
- }
- const gDrive = new GDrive(GDRIVE_CLIENT_ID, GDRIVE_CLIENT_KEY, SCOPES);
- const dropbox = new Dropbox(DROPBOX_CLIENT_ID, DROPBOX_CLIENT_KEY);
- async function onMessage$4(message, sender) {
- if (message.method.endsWith(".download")) {
- return downloadTabPage(message, sender.tab);
- }
- if (message.method.endsWith(".disableGDrive")) {
- const authInfo = await getAuthInfo$1();
- removeAuthInfo();
- await gDrive.revokeAuthToken(authInfo && (authInfo.accessToken || authInfo.revokableAccessToken));
- return {};
- }
- if (message.method.endsWith(".disableDropbox")) {
- const authInfo = await getDropboxAuthInfo$1();
- removeDropboxAuthInfo();
- await dropbox.revokeAuthToken(authInfo && (authInfo.accessToken || authInfo.revokableAccessToken));
- return {};
- }
- if (message.method.endsWith(".end")) {
- if (message.hash) {
- try {
- await anchor(message.hash, message.woleetKey);
- } catch (error) {
- onError(sender.tab.id, error.message, error.link);
- }
- }
- onSaveEnd(message.taskId);
- return {};
- }
- if (message.method.endsWith(".getInfo")) {
- return getTasksInfo();
- }
- if (message.method.endsWith(".cancel")) {
- cancelTask(message.taskId);
- return {};
- }
- if (message.method.endsWith(".cancelAll")) {
- cancelAllTasks();
- return {};
- }
- if (message.method.endsWith(".saveUrls")) {
- saveUrls(message.urls);
- return {};
- }
- }
- async function downloadTabPage(message, tab) {
- const tabId = tab.id;
- let contents;
- if (message.blobURL) {
- try {
- if (message.compressContent) {
- message.pageData = await parse(new Uint8Array(await (await fetch(message.blobURL)).arrayBuffer()));
- await downloadCompressedContent(message, tab);
- } else {
- message.content = await (await fetch(message.blobURL)).text();
- await downloadContent([message.content], tab, tab.incognito, message);
- }
- } catch (error) {
- return { error: true };
- }
- } else if (message.compressContent) {
- let blobParts = tabData.get(tabId);
- const type = message.mimeType;
- if (!blobParts) {
- blobParts = [];
- tabData.set(tabId, blobParts);
- }
- if (message.data) {
- blobParts.push(new Uint8Array(message.data));
- } else {
- tabData.delete(tabId);
- const message = await parse(new Uint8Array((await new Blob(blobParts, { type }).arrayBuffer())));
- await downloadCompressedContent(message, tab);
- }
- } else {
- if (message.truncated) {
- contents = partialContents.get(tabId);
- if (!contents) {
- contents = [];
- partialContents.set(tabId, contents);
- }
- contents.push(message.content);
- if (message.finished) {
- partialContents.delete(tabId);
- }
- } else if (message.content) {
- contents = [message.content];
- }
- if (!message.truncated || message.finished) {
- await downloadContent(contents, tab, tab.incognito, message);
- }
- }
- return {};
- }
- async function downloadContent(contents, tab, incognito, message) {
- const tabId = tab.id;
- try {
- let skipped;
- if (message.backgroundSave && !message.saveToGDrive && !message.saveToDropbox && !message.saveWithWebDAV && !message.saveToGitHub && !message.saveToRestFormApi) {
- const testSkip = await testSkipSave(message.filename, message);
- message.filenameConflictAction = testSkip.filenameConflictAction;
- skipped = testSkip.skipped;
- }
- if (skipped) {
- onEnd(tabId);
- } else {
- const prompt = filename => promptFilename(tabId, filename);
- let response;
- if (message.openEditor) {
- onEdit(tabId);
- await open({ tabIndex: tab.index + 1, filename: message.filename, content: contents.join("") });
- } else if (message.saveToClipboard) {
- message.content = contents.join("");
- saveToClipboard(message);
- } else if (message.saveWithWebDAV) {
- response = await saveWithWebDAV(message.taskId, encodeSharpCharacter(message.filename), contents.join(""), message.webDAVURL, message.webDAVUser, message.webDAVPassword, { filenameConflictAction: message.filenameConflictAction, prompt });
- } else if (message.saveToGDrive) {
- await saveToGDrive(message.taskId, encodeSharpCharacter(message.filename), new Blob(contents, { type: message.mimeType }), {
- forceWebAuthFlow: message.forceWebAuthFlow
- }, {
- onProgress: (offset, size) => onUploadProgress(tabId, offset, size),
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- } else if (message.saveToDropbox) {
- await saveToDropbox(message.taskId, encodeSharpCharacter(message.filename), new Blob(contents, { type: message.mimeType }), {
- onProgress: (offset, size) => onUploadProgress(tabId, offset, size),
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- } else if (message.saveToGitHub) {
- response = await saveToGitHub(message.taskId, encodeSharpCharacter(message.filename), contents.join(""), message.githubToken, message.githubUser, message.githubRepository, message.githubBranch, {
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- await response.pushPromise;
- } else if (message.saveWithCompanion) {
- await save({
- filename: message.filename,
- content: message.content,
- filenameConflictAction: message.filenameConflictAction
- });
- } else if (message.saveToRestFormApi) {
- response = await saveToRestFormApi(
- message.taskId,
- contents.join(""),
- tab.url,
- message.saveToRestFormApiToken,
- message.saveToRestFormApiUrl,
- message.saveToRestFormApiFileFieldName,
- message.saveToRestFormApiUrlFieldName
- );
- } else {
- message.url = URL.createObjectURL(new Blob(contents, { type: message.mimeType }));
- response = await downloadPage(message, {
- confirmFilename: message.confirmFilename,
- incognito,
- filenameConflictAction: message.filenameConflictAction,
- filenameReplacementCharacter: message.filenameReplacementCharacter,
- bookmarkId: message.bookmarkId,
- replaceBookmarkURL: message.replaceBookmarkURL,
- includeInfobar: message.includeInfobar
- });
- }
- if (message.bookmarkId && message.replaceBookmarkURL && response && response.url) {
- await update(message.bookmarkId, { url: response.url });
- }
- onEnd(tabId);
- if (message.openSavedPage && !message.openEditor) {
- const createTabProperties = { active: true, url: "/src/ui/pages/viewer.html?blobURI=" + URL.createObjectURL(new Blob(contents, { type: message.mimeType })), windowId: tab.windowId };
- if (tab.index != null) {
- createTabProperties.index = tab.index + 1;
- }
- browser.tabs.create(createTabProperties);
- }
- }
- } catch (error) {
- if (!error.message || error.message != "upload_cancelled") {
- console.error(error); // eslint-disable-line no-console
- onError(tabId, error.message, error.link);
- }
- } finally {
- if (message.url) {
- URL.revokeObjectURL(message.url);
- }
- }
- }
- async function downloadCompressedContent(message, tab) {
- const tabId = tab.id;
- try {
- let skipped;
- if (message.backgroundSave && !message.saveToGDrive && !message.saveToDropbox && !message.saveWithWebDAV && !message.saveToGitHub && !message.sharePage) {
- const testSkip = await testSkipSave(message.filename, message);
- message.filenameConflictAction = testSkip.filenameConflictAction;
- skipped = testSkip.skipped;
- }
- if (skipped) {
- onEnd(tabId);
- } else {
- const pageData = message.pageData;
- const prompt = filename => promptFilename(tabId, filename);
- const blob = await singlefile.processors.compression.process(pageData, {
- insertTextBody: message.insertTextBody,
- url: pageData.url || tab.url,
- createRootDirectory: message.createRootDirectory,
- tabId,
- selfExtractingArchive: message.selfExtractingArchive,
- extractDataFromPage: message.extractDataFromPage,
- preventAppendedData: message.preventAppendedData,
- insertCanonicalLink: message.insertCanonicalLink,
- insertMetaNoIndex: message.insertMetaNoIndex,
- insertMetaCSP: message.insertMetaCSP,
- password: message.password,
- embeddedImage: message.embeddedImage
- });
- let response;
- if (message.openEditor) {
- onEdit(tabId);
- await open({
- tabIndex: tab.index + 1,
- filename: message.filename,
- content: Array.from(new Uint8Array(await blob.arrayBuffer())),
- compressContent: message.compressContent,
- selfExtractingArchive: message.selfExtractingArchive,
- extractDataFromPage: message.extractDataFromPage,
- insertTextBody: message.insertTextBody,
- insertMetaCSP: message.insertMetaCSP,
- embeddedImage: message.embeddedImage
- });
- } else if (message.foregroundSave || !message.backgroundSave || message.sharePage) {
- const response = await downloadPageForeground(message.taskId, message.filename, blob, pageData.mimeType, tabId, {
- foregroundSave: true,
- sharePage: message.sharePage
- });
- if (response.error) {
- throw new Error(response.error);
- }
- } else if (message.saveWithWebDAV) {
- response = await saveWithWebDAV(message.taskId, encodeSharpCharacter(message.filename), blob, message.webDAVURL, message.webDAVUser, message.webDAVPassword, { filenameConflictAction: message.filenameConflictAction, prompt });
- } else if (message.saveToGDrive) {
- await saveToGDrive(message.taskId, encodeSharpCharacter(message.filename), blob, {
- forceWebAuthFlow: message.forceWebAuthFlow
- }, {
- onProgress: (offset, size) => onUploadProgress(tabId, offset, size),
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- } else if (message.saveToDropbox) {
- await saveToDropbox(message.taskId, encodeSharpCharacter(message.filename), blob, {
- onProgress: (offset, size) => onUploadProgress(tabId, offset, size),
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- } else if (message.saveToGitHub) {
- response = await saveToGitHub(message.taskId, encodeSharpCharacter(message.filename), blob, message.githubToken, message.githubUser, message.githubRepository, message.githubBranch, {
- filenameConflictAction: message.filenameConflictAction,
- prompt
- });
- await response.pushPromise;
- } else {
- message.url = URL.createObjectURL(blob);
- response = await downloadPage(message, {
- confirmFilename: message.confirmFilename,
- incognito: tab.incognito,
- filenameConflictAction: message.filenameConflictAction,
- filenameReplacementCharacter: message.filenameReplacementCharacter,
- bookmarkId: message.bookmarkId,
- replaceBookmarkURL: message.replaceBookmarkURL,
- includeInfobar: message.includeInfobar
- });
- }
- if (message.bookmarkId && message.replaceBookmarkURL && response && response.url) {
- await update(message.bookmarkId, { url: response.url });
- }
- onEnd(tabId);
- if (message.openSavedPage && !message.openEditor) {
- const createTabProperties = { active: true, url: "/src/ui/pages/viewer.html?compressed&blobURI=" + URL.createObjectURL(blob), windowId: tab.windowId };
- if (tab.index != null) {
- createTabProperties.index = tab.index + 1;
- }
- browser.tabs.create(createTabProperties);
- }
- }
- } catch (error) {
- if (!error.message || error.message != "upload_cancelled") {
- console.error(error); // eslint-disable-line no-console
- onError(tabId, error.message, error.link);
- }
- } finally {
- if (message.url) {
- URL.revokeObjectURL(message.url);
- }
- }
- }
- function encodeSharpCharacter(path) {
- return path.replace(/#/g, "%23");
- }
- function getRegExp(string) {
- return string.replace(REGEXP_ESCAPE, "\\$1");
- }
- async function getAuthInfo(authOptions, force) {
- let authInfo = await getAuthInfo$1();
- const options = {
- interactive: true,
- forceWebAuthFlow: authOptions.forceWebAuthFlow,
- launchWebAuthFlow: options => launchWebAuthFlow(options),
- extractAuthCode: authURL => extractAuthCode(authURL)
- };
- gDrive.setAuthInfo(authInfo, options);
- if (!authInfo || !authInfo.accessToken || force) {
- authInfo = await gDrive.auth(options);
- if (authInfo) {
- await setAuthInfo(authInfo);
- } else {
- await removeAuthInfo();
- }
- }
- return authInfo;
- }
- async function getDropboxAuthInfo(force) {
- let authInfo = await getDropboxAuthInfo$1();
- const options = {
- launchWebAuthFlow: options => launchWebAuthFlow(options),
- extractAuthCode: authURL => extractAuthCode(authURL)
- };
- dropbox.setAuthInfo(authInfo);
- if (!authInfo || !authInfo.accessToken || force) {
- authInfo = await dropbox.auth(options);
- if (authInfo) {
- await setDropboxAuthInfo(authInfo);
- } else {
- await removeDropboxAuthInfo();
- }
- }
- return authInfo;
- }
- async function saveToGitHub(taskId, filename, content, githubToken, githubUser, githubRepository, githubBranch, { filenameConflictAction, prompt }) {
- try {
- const taskInfo = getTaskInfo(taskId);
- if (!taskInfo || !taskInfo.cancelled) {
- const client = new GitHub(githubToken, githubUser, githubRepository, githubBranch);
- setCancelCallback(taskId, () => client.abort());
- return await client.upload(filename, content, { filenameConflictAction, prompt });
- }
- } catch (error) {
- throw new Error(error.message + " (GitHub)");
- }
- }
- async function saveWithWebDAV(taskId, filename, content, url, username, password, { filenameConflictAction, prompt }) {
- try {
- const taskInfo = getTaskInfo(taskId);
- if (!taskInfo || !taskInfo.cancelled) {
- const client = new WebDAV(url, username, password);
- setCancelCallback(taskId, () => client.abort());
- return await client.upload(filename, content, { filenameConflictAction, prompt });
- }
- } catch (error) {
- throw new Error(error.message + " (WebDAV)");
- }
- }
- async function saveToGDrive(taskId, filename, blob, authOptions, uploadOptions) {
- try {
- await getAuthInfo(authOptions);
- const taskInfo = getTaskInfo(taskId);
- if (!taskInfo || !taskInfo.cancelled) {
- return await gDrive.upload(filename, blob, uploadOptions, callback => setCancelCallback(taskId, callback));
- }
- }
- catch (error) {
- if (error.message == "invalid_token") {
- let authInfo;
- try {
- authInfo = await gDrive.refreshAuthToken();
- } catch (error) {
- if (error.message == "unknown_token") {
- authInfo = await getAuthInfo(authOptions, true);
- } else {
- throw new Error(error.message + " (Google Drive)");
- }
- }
- if (authInfo) {
- await setAuthInfo(authInfo);
- } else {
- await removeAuthInfo();
- }
- return await saveToGDrive(taskId, filename, blob, authOptions, uploadOptions);
- } else {
- throw new Error(error.message + " (Google Drive)");
- }
- }
- }
- async function saveToDropbox(taskId, filename, blob, uploadOptions) {
- try {
- await getDropboxAuthInfo();
- const taskInfo = getTaskInfo(taskId);
- if (!taskInfo || !taskInfo.cancelled) {
- return await dropbox.upload(filename, blob, uploadOptions, callback => setCancelCallback(taskId, callback));
- }
- }
- catch (error) {
- if (error.message == "invalid_token") {
- let authInfo;
- try {
- authInfo = await dropbox.refreshAuthToken();
- } catch (error) {
- if (error.message == "unknown_token") {
- authInfo = await getDropboxAuthInfo(true);
- } else {
- throw new Error(error.message + " (Dropbox)");
- }
- }
- if (authInfo) {
- await setDropboxAuthInfo(authInfo);
- } else {
- await removeDropboxAuthInfo();
- }
- return await saveToDropbox(taskId, filename, blob, uploadOptions);
- } else {
- throw new Error(error.message + " (Dropbox)");
- }
- }
- }
- async function testSkipSave(filename, options) {
- let skipped, filenameConflictAction = options.filenameConflictAction;
- if (filenameConflictAction == CONFLICT_ACTION_SKIP) {
- const downloadItems = await browser.downloads.search({
- filenameRegex: "(\\\\|/)" + getRegExp(filename) + "$",
- exists: true
- });
- if (downloadItems.length) {
- skipped = true;
- } else {
- filenameConflictAction = CONFLICT_ACTION_UNIQUIFY;
- }
- }
- return { skipped, filenameConflictAction };
- }
- function promptFilename(tabId, filename) {
- return browser.tabs.sendMessage(tabId, { method: "content.prompt", message: "Filename conflict, please enter a new filename", value: filename });
- }
- async function downloadPage(pageData, options) {
- const downloadInfo = {
- url: pageData.url,
- saveAs: options.confirmFilename,
- filename: pageData.filename,
- conflictAction: options.filenameConflictAction
- };
- if (options.incognito) {
- downloadInfo.incognito = true;
- }
- const downloadData = await download(downloadInfo, options.filenameReplacementCharacter);
- if (downloadData.filename) {
- let url = downloadData.filename;
- if (!url.startsWith("file:")) {
- if (url.startsWith("/")) {
- url = url.substring(1);
- }
- url = "file:///" + encodeSharpCharacter(url);
- }
- return { url };
- }
- }
- function saveToClipboard(pageData) {
- const command = "copy";
- document.addEventListener(command, listener);
- document.execCommand(command);
- document.removeEventListener(command, listener);
- function listener(event) {
- event.clipboardData.setData(pageData.mimeType, pageData.content);
- event.clipboardData.setData("text/plain", pageData.content);
- event.preventDefault();
- }
- }
- async function saveToRestFormApi(taskId, content, url, token, restApiUrl, fileFieldName, urlFieldName) {
- try {
- const taskInfo = getTaskInfo(taskId);
- if (!taskInfo || !taskInfo.cancelled) {
- const client = new RestFormApi(token, restApiUrl, fileFieldName, urlFieldName);
- setCancelCallback(taskId, () => client.abort());
- return await client.upload(content, url);
- }
- } catch (error) {
- throw new Error(error.message + " (RestFormApi)");
- }
- }
- async function downloadPageForeground(taskId, filename, content, mimeType, tabId, { foregroundSave, sharePage }) {
- const serializer = getSerializer({
- filename,
- taskId,
- foregroundSave,
- sharePage,
- content: await content.arrayBuffer(),
- mimeType
- });
- for await (const data of serializer) {
- await browser.tabs.sendMessage(tabId, {
- method: "content.download",
- data: Array.from(data)
- });
- }
- return browser.tabs.sendMessage(tabId, { method: "content.download" });
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const pendingMessages = {};
- const replacedTabIds = {};
- async function onMessage$3(message, sender) {
- if (message.method.endsWith(".save")) {
- if (message.autoSaveDiscard || message.autoSaveRemove) {
- if (sender.tab) {
- message.tab = sender.tab;
- pendingMessages[sender.tab.id] = message;
- } else if (pendingMessages[message.tabId] &&
- ((pendingMessages[message.tabId].removed && message.autoSaveRemove) ||
- (pendingMessages[message.tabId].discarded && message.autoSaveDiscard))
- ) {
- delete pendingMessages[message.tabId];
- await saveContent(message, { id: message.tabId, index: message.tabIndex, url: sender.url });
- }
- if (message.autoSaveUnload) {
- delete pendingMessages[message.tabId];
- await saveContent(message, sender.tab);
- }
- } else {
- delete pendingMessages[message.tabId];
- await saveContent(message, sender.tab);
- }
- return {};
- }
- }
- function onTabUpdated$1(tabId) {
- delete pendingMessages[tabId];
- }
- async function onTabRemoved$1(tabId) {
- const message = pendingMessages[tabId];
- if (message) {
- if (message.autoSaveRemove) {
- delete pendingMessages[tabId];
- await saveContent(message, message.tab);
- }
- } else {
- pendingMessages[tabId] = { removed: true };
- }
- }
- async function onTabDiscarded(tabId) {
- const message = pendingMessages[tabId];
- if (message) {
- delete pendingMessages[tabId];
- await saveContent(message, message.tab);
- } else {
- pendingMessages[tabId] = { discarded: true };
- }
- }
- async function onTabReplaced$1(addedTabId, removedTabId) {
- if (pendingMessages[removedTabId] && !pendingMessages[addedTabId]) {
- pendingMessages[addedTabId] = pendingMessages[removedTabId];
- delete pendingMessages[removedTabId];
- replacedTabIds[removedTabId] = addedTabId;
- }
- }
- async function onMessageExternal(message, currentTab) {
- if (message.method == "enableAutoSave") {
- const allTabsData = await getPersistent(currentTab.id);
- allTabsData[currentTab.id].autoSave = message.enabled;
- await setPersistent(allTabsData);
- refreshTab(currentTab);
- }
- if (message.method == "isAutoSaveEnabled") {
- return autoSaveIsEnabled(currentTab);
- }
- }
- async function onInit$1(tab) {
- const [options, autoSaveEnabled] = await Promise.all([getOptions(tab.url, true), autoSaveIsEnabled(tab)]);
- if (options && ((options.autoSaveLoad || options.autoSaveLoadOrUnload) && autoSaveEnabled)) {
- saveTabs([tab], { autoSave: true });
- }
- }
- async function saveContent(message, tab) {
- const tabId = tab.id;
- const options = await getOptions(tab.url, true);
- if (options) {
- onStart(tabId, 1, true);
- options.content = message.content;
- options.url = message.url;
- options.frames = message.frames;
- options.canvases = message.canvases;
- options.fonts = message.fonts;
- options.stylesheets = message.stylesheets;
- options.images = message.images;
- options.posters = message.posters;
- options.videos = message.videos;
- options.usedFonts = message.usedFonts;
- options.shadowRoots = message.shadowRoots;
- options.referrer = message.referrer;
- options.updatedResources = message.updatedResources;
- options.adoptedStyleSheets = message.adoptedStyleSheets;
- options.visitDate = new Date(message.visitDate);
- options.backgroundTab = true;
- options.autoSave = true;
- options.incognito = tab.incognito;
- options.tabId = tabId;
- options.tabIndex = tab.index;
- options.keepFilename = options.saveToGDrive || options.saveToGitHub || options.saveWithWebDAV || options.saveToDropbox || options.saveToRestFormApi;
- let pageData;
- try {
- if (options.autoSaveExternalSave) {
- await externalSave(options);
- } else {
- if (options.passReferrerOnError) {
- enableReferrerOnError();
- }
- options.tabId = tabId;
- pageData = await getPageData(options, null, null, { fetch: fetch$1 });
- let skipped;
- if (!options.saveToGDrive && !options.saveWithWebDAV && !options.saveToGitHub && !options.saveToDropbox && !options.saveWithCompanion && !options.saveToRestFormApi) {
- const testSkip = await testSkipSave(pageData.filename, options);
- skipped = testSkip.skipped;
- options.filenameConflictAction = testSkip.filenameConflictAction;
- }
- if (!skipped) {
- let { content, mimeType: type } = pageData;
- if (options.compressContent) {
- content = new Blob([new Uint8Array(content)], { type });
- }
- if (options.saveToGDrive) {
- if (!(content instanceof Blob)) {
- content = new Blob([content], { type });
- }
- await saveToGDrive(message.taskId, encodeSharpCharacter(pageData.filename), content, options, {
- forceWebAuthFlow: options.forceWebAuthFlow
- }, {
- filenameConflictAction: options.filenameConflictAction
- });
- } if (options.saveToDropbox) {
- if (!(content instanceof Blob)) {
- content = new Blob([content], { type });
- }
- await saveToDropbox(message.taskId, encodeSharpCharacter(pageData.filename), content, {
- filenameConflictAction: options.filenameConflictAction
- });
- } else if (options.saveWithWebDAV) {
- await saveWithWebDAV(message.taskId, encodeSharpCharacter(pageData.filename), content, options.webDAVURL, options.webDAVUser, options.webDAVPassword, {
- filenameConflictAction: options.filenameConflictAction
- });
- } else if (options.saveToGitHub) {
- await (await saveToGitHub(message.taskId, encodeSharpCharacter(pageData.filename), content, options.githubToken, options.githubUser, options.githubRepository, options.githubBranch, {
- filenameConflictAction: options.filenameConflictAction
- })).pushPromise;
- } else if (options.saveWithCompanion && !options.compressContent) {
- await save({
- filename: pageData.filename,
- content: pageData.content,
- filenameConflictAction: options.filenameConflictAction
- });
- } else if (options.saveToRestFormApi) {
- await saveToRestFormApi(
- message.taskId,
- content,
- pageData.url,
- options.restFormApiToken,
- options.restFormApiUrl,
- options.restFormApiFileFieldName,
- options.restFormApiUrlFieldName
- );
- } else {
- if (!(content instanceof Blob)) {
- content = new Blob([content], { type });
- }
- pageData.url = URL.createObjectURL(content);
- await downloadPage(pageData, options);
- }
- if (options.openSavedPage) {
- const createTabProperties = { active: true, url: "/src/ui/pages/viewer.html?compressed=true&blobURI=" + URL.createObjectURL(content), windowId: tab.windowId };
- const index = tab.index;
- try {
- await browser.tabs.get(tabId);
- createTabProperties.index = index + 1;
- } catch (error) {
- createTabProperties.index = index;
- }
- browser.tabs.create(createTabProperties);
- }
- if (pageData.hash) {
- await anchor(pageData.hash, options.woleetKey);
- }
- }
- }
- } finally {
- if (message.taskId) {
- onSaveEnd(message.taskId);
- } else if (options.autoClose) {
- browser.tabs.remove(replacedTabIds[tabId] || tabId);
- delete replacedTabIds[tabId];
- }
- if (pageData && pageData.url) {
- URL.revokeObjectURL(pageData.url);
- }
- onEnd(tabId, true);
- }
- }
- }
- async function fetch$1(url, options = {}) {
- const response = await fetchResource$1(url, options);
- return {
- status: response.status,
- headers: {
- get: name => response.headers.get(name)
- },
- arrayBuffer: () => response.arrayBuffer
- };
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- async function onMessage$2(message) {
- if (message.method.endsWith(".resourceCommitted")) {
- if (message.tabId && message.url && (message.type == "stylesheet" || message.type == "script")) {
- await browser.tabs.sendMessage(message.tabId, message);
- }
- }
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const DELAY_MAYBE_INIT = 1500;
- browser.tabs.onCreated.addListener(tab => onTabCreated(tab));
- browser.tabs.onActivated.addListener(activeInfo => onTabActivated(activeInfo));
- browser.tabs.onRemoved.addListener(tabId => onTabRemoved(tabId));
- browser.tabs.onUpdated.addListener((tabId, changeInfo) => onTabUpdated(tabId, changeInfo));
- browser.tabs.onReplaced.addListener((addedTabId, removedTabId) => onTabReplaced(addedTabId, removedTabId));
- async function onMessage$1(message, sender) {
- if (message.method.endsWith(".init")) {
- await onInit(sender.tab, message);
- onInit$3(sender.tab);
- onInit$2(sender.tab);
- onInit$1(sender.tab);
- }
- if (message.method.endsWith(".getOptions")) {
- return getOptions(message.url);
- }
- if (message.method.endsWith(".activate")) {
- await browser.tabs.update(message.tabId, { active: true });
- }
- if (message.method.endsWith(".getScreenshot")) {
- return captureTab(sender.tab.id, message);
- }
- }
- async function onInit(tab, options) {
- await remove(tab.id);
- const allTabsData = await getPersistent(tab.id);
- allTabsData[tab.id].savedPageDetected = options.savedPageDetected;
- await setPersistent(allTabsData);
- }
- async function onTabUpdated(tabId, changeInfo) {
- if (changeInfo.status == "complete") {
- setTimeout(async () => {
- try {
- await browser.tabs.sendMessage(tabId, { method: "content.maybeInit" });
- }
- catch (error) {
- // ignored
- }
- }, DELAY_MAYBE_INIT);
- onTabUpdated$1(tabId);
- const tab = await browser.tabs.get(tabId);
- if (isEditor(tab)) {
- const allTabsData = await getPersistent(tab.id);
- allTabsData[tab.id].editorDetected = true;
- await setPersistent(allTabsData);
- onTabActivated$1(tab);
- }
- }
- if (changeInfo.discarded) {
- onTabDiscarded(tabId);
- }
- }
- function onTabReplaced(addedTabId, removedTabId) {
- onTabReplaced$3(addedTabId, removedTabId);
- onTabReplaced$1(addedTabId, removedTabId);
- onTabReplaced$2(addedTabId, removedTabId);
- }
- function onTabCreated(tab) {
- onTabCreated$1(tab);
- }
- async function onTabActivated(activeInfo) {
- const tab = await browser.tabs.get(activeInfo.tabId);
- onTabActivated$1(tab);
- }
- function onTabRemoved(tabId) {
- remove(tabId);
- onTabRemoved$2(tabId);
- cancelTab(tabId);
- onTabRemoved$1(tabId);
- }
- async function captureTab(tabId, options) {
- const { width, height } = options;
- const canvas = new OffscreenCanvas(width, height);
- const context = canvas.getContext("2d");
- const image = new Image();
- let y = 0, scrollYStep, activeTabId;
- if (browser.tabs.captureTab) {
- scrollYStep = 4 * 1024;
- } else {
- scrollYStep = options.innerHeight;
- activeTabId = (await browser.tabs.query({ active: true, currentWindow: true }))[0].id;
- await browser.tabs.sendMessage(tabId, { method: "content.beginScrollTo" });
- }
- while (y < height) {
- let imageSrc;
- if (browser.tabs.captureTab) {
- imageSrc = await browser.tabs.captureTab(tabId, {
- format: "png",
- rect: { x: 0, y, width, height: Math.min(height - y, scrollYStep) }
- });
- } else {
- await browser.tabs.sendMessage(tabId, { method: "content.scrollTo", y });
- await browser.tabs.update(tabId, { active: true });
- imageSrc = await browser.tabs.captureVisibleTab(null, {
- format: "png"
- });
- }
- await new Promise((resolve, reject) => {
- image.onload = resolve;
- image.onerror = event => reject(new Error(event.detail));
- image.src = imageSrc;
- });
- context.drawImage(image, 0, y, width, Math.min(height - y, scrollYStep));
- y += scrollYStep;
- }
- if (!browser.tabs.captureTab) {
- await browser.tabs.update(activeTabId, { active: true });
- await browser.tabs.sendMessage(tabId, { method: "content.endScrollTo" });
- }
- const blob = await canvas.convertToBlob({ type: "image/png" });
- return URL.createObjectURL(blob);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser */
- browser.runtime.onMessage.addListener((message, sender) => {
- if (message.method == "singlefile.frameTree.initResponse" || message.method == "singlefile.frameTree.ackInitRequest") {
- browser.tabs.sendMessage(sender.tab.id, message, { frameId: 0 });
- return Promise.resolve({});
- }
- });
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- /* global browser, setTimeout, clearTimeout */
- const timeouts = new Map();
- browser.runtime.onMessage.addListener((message, sender) => {
- if (message.method == "singlefile.lazyTimeout.setTimeout") {
- let tabTimeouts = timeouts.get(sender.tab.id);
- let frameTimeouts;
- if (tabTimeouts) {
- frameTimeouts = tabTimeouts.get(sender.frameId);
- if (frameTimeouts) {
- const previousTimeoutId = frameTimeouts.get(message.type);
- if (previousTimeoutId) {
- clearTimeout(previousTimeoutId);
- }
- } else {
- frameTimeouts = new Map();
- }
- }
- const timeoutId = setTimeout(async () => {
- try {
- const tabTimeouts = timeouts.get(sender.tab.id);
- const frameTimeouts = tabTimeouts.get(sender.frameId);
- if (tabTimeouts && frameTimeouts) {
- deleteTimeout(frameTimeouts, message.type);
- }
- await browser.tabs.sendMessage(sender.tab.id, { method: "singlefile.lazyTimeout.onTimeout", type: message.type });
- } catch (error) {
- // ignored
- }
- }, message.delay);
- if (!tabTimeouts) {
- tabTimeouts = new Map();
- frameTimeouts = new Map();
- tabTimeouts.set(sender.frameId, frameTimeouts);
- timeouts.set(sender.tab.id, tabTimeouts);
- }
- frameTimeouts.set(message.type, timeoutId);
- return Promise.resolve({});
- }
- if (message.method == "singlefile.lazyTimeout.clearTimeout") {
- let tabTimeouts = timeouts.get(sender.tab.id);
- if (tabTimeouts) {
- const frameTimeouts = tabTimeouts.get(sender.frameId);
- if (frameTimeouts) {
- const timeoutId = frameTimeouts.get(message.type);
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- deleteTimeout(frameTimeouts, message.type);
- }
- }
- return Promise.resolve({});
- }
- });
- browser.tabs.onRemoved.addListener(tabId => timeouts.delete(tabId));
- function deleteTimeout(framesTimeouts, type) {
- framesTimeouts.delete(type);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- const ACTION_SAVE_PAGE = "save-page";
- const ACTION_EDIT_AND_SAVE_PAGE = "edit-and-save-page";
- const ACTION_SAVE_SELECTED_LINKS = "save-selected-links";
- const ACTION_SAVE_SELECTED = "save-selected-content";
- const ACTION_SAVE_SELECTED_TABS = "save-selected-tabs";
- const ACTION_SAVE_UNPINNED_TABS = "save-unpinned-tabs";
- const ACTION_SAVE_ALL_TABS = "save-all-tabs";
- async function onMessage(message, sender) {
- if (message == ACTION_SAVE_PAGE) {
- const tabs = await browser.tabs.query({ currentWindow: true, active: true });
- tabs.length = 1;
- await saveTabs(tabs);
- } else if (message == ACTION_EDIT_AND_SAVE_PAGE) {
- const tabs = await browser.tabs.query({ currentWindow: true, active: true });
- tabs.length = 1;
- await saveTabs(tabs, { openEditor: true });
- } else if (message == ACTION_SAVE_SELECTED_LINKS) {
- const tabs = await browser.tabs.query({ currentWindow: true, active: true });
- await saveSelectedLinks(tabs[0]);
- } else if (message == ACTION_SAVE_SELECTED) {
- const tabs = await browser.tabs.query({ currentWindow: true, active: true });
- await saveTabs(tabs, { selected: true });
- } else if (message == ACTION_SAVE_SELECTED_TABS) {
- const tabs = await queryTabs({ currentWindow: true, highlighted: true });
- await saveTabs(tabs);
- } else if (message == ACTION_SAVE_UNPINNED_TABS) {
- const tabs = await queryTabs({ currentWindow: true, pinned: false });
- await saveTabs(tabs);
- } else if (message == ACTION_SAVE_ALL_TABS) {
- const tabs = await queryTabs({ currentWindow: true });
- await saveTabs(tabs);
- } else if (message.method) {
- const tabs = await browser.tabs.query({ currentWindow: true, active: true });
- const currentTab = tabs[0];
- if (currentTab) {
- return onMessageExternal(message, currentTab);
- } else {
- return false;
- }
- }
- }
- async function queryTabs(options) {
- const tabs = await browser.tabs.query(options);
- return tabs.sort((tab1, tab2) => tab1.index - tab2.index);
- }
- /*
- * Copyright 2010-2020 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- *
- * This file is part of SingleFile.
- *
- * The code in this file is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * (GNU AGPL) as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * The code in this file 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 Affero
- * General Public License for more details.
- *
- * As additional permission under GNU AGPL version 3 section 7, you may
- * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
- * AGPL normally required by section 4, provided you include this license
- * notice and a URL through which recipients can access the Corresponding
- * Source.
- */
- browser.runtime.onMessage.addListener((message, sender) => {
- if (message.method.startsWith("tabs.")) {
- return onMessage$1(message, sender);
- }
- if (message.method.startsWith("downloads.")) {
- return onMessage$4(message, sender);
- }
- if (message.method.startsWith("autosave.")) {
- return onMessage$3(message, sender);
- }
- if (message.method.startsWith("ui.")) {
- return onMessage$7(message, sender);
- }
- if (message.method.startsWith("config.")) {
- return onMessage$d(message);
- }
- if (message.method.startsWith("tabsData.")) {
- return onMessage$e(message);
- }
- if (message.method.startsWith("devtools.")) {
- return onMessage$2(message);
- }
- if (message.method.startsWith("editor.")) {
- return onMessage$b(message, sender);
- }
- if (message.method.startsWith("bookmarks.")) {
- return onMessage$5(message);
- }
- if (message.method.startsWith("companion.")) {
- return onMessage$6(message);
- }
- if (message.method.startsWith("requests.")) {
- return onMessage$a(message);
- }
- if (message.method.startsWith("bootstrap.")) {
- return onMessage$c(message, sender);
- }
- });
- if (browser.runtime.onMessageExternal) {
- browser.runtime.onMessageExternal.addListener(onMessage);
- }
- })();
|