nsStandardURL.cpp 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* vim:set ts=4 sw=4 sts=4 et cindent: */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #include "IPCMessageUtils.h"
  7. #include "nsStandardURL.h"
  8. #include "nsCRT.h"
  9. #include "nsEscape.h"
  10. #include "nsIFile.h"
  11. #include "nsIObjectInputStream.h"
  12. #include "nsIObjectOutputStream.h"
  13. #include "nsIPrefService.h"
  14. #include "nsIPrefBranch.h"
  15. #include "nsIIDNService.h"
  16. #include "mozilla/Logging.h"
  17. #include "nsAutoPtr.h"
  18. #include "nsIURLParser.h"
  19. #include "nsNetCID.h"
  20. #include "mozilla/MemoryReporting.h"
  21. #include "mozilla/ipc/URIUtils.h"
  22. #include <algorithm>
  23. #include "mozilla/dom/EncodingUtils.h"
  24. #include "nsContentUtils.h"
  25. #include "prprf.h"
  26. #include "nsReadableUtils.h"
  27. #include "nsPrintfCString.h"
  28. using mozilla::dom::EncodingUtils;
  29. using namespace mozilla::ipc;
  30. namespace mozilla {
  31. namespace net {
  32. static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
  33. static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
  34. nsIIDNService *nsStandardURL::gIDN = nullptr;
  35. bool nsStandardURL::gInitialized = false;
  36. bool nsStandardURL::gEscapeUTF8 = true;
  37. bool nsStandardURL::gAlwaysEncodeInUTF8 = true;
  38. char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
  39. //
  40. // setenv MOZ_LOG nsStandardURL:5
  41. //
  42. static LazyLogModule gStandardURLLog("nsStandardURL");
  43. // The Chromium code defines its own LOG macro which we don't want
  44. #undef LOG
  45. #define LOG(args) MOZ_LOG(gStandardURLLog, LogLevel::Debug, args)
  46. #undef LOG_ENABLED
  47. #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug)
  48. //----------------------------------------------------------------------------
  49. #define ENSURE_MUTABLE() \
  50. PR_BEGIN_MACRO \
  51. if (!mMutable) { \
  52. NS_WARNING("attempt to modify an immutable nsStandardURL"); \
  53. return NS_ERROR_ABORT; \
  54. } \
  55. PR_END_MACRO
  56. //----------------------------------------------------------------------------
  57. static nsresult
  58. EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
  59. {
  60. nsresult rv;
  61. int32_t len = str.Length();
  62. int32_t maxlen;
  63. rv = encoder->GetMaxLength(str.get(), len, &maxlen);
  64. if (NS_FAILED(rv))
  65. return rv;
  66. char buf[256], *p = buf;
  67. if (uint32_t(maxlen) > sizeof(buf) - 1) {
  68. p = (char *) malloc(maxlen + 1);
  69. if (!p)
  70. return NS_ERROR_OUT_OF_MEMORY;
  71. }
  72. rv = encoder->Convert(str.get(), &len, p, &maxlen);
  73. if (NS_FAILED(rv))
  74. goto end;
  75. if (rv == NS_ERROR_UENC_NOMAPPING) {
  76. NS_WARNING("unicode conversion failed");
  77. rv = NS_ERROR_UNEXPECTED;
  78. goto end;
  79. }
  80. p[maxlen] = 0;
  81. result.Assign(p);
  82. len = sizeof(buf) - 1;
  83. rv = encoder->Finish(buf, &len);
  84. if (NS_FAILED(rv))
  85. goto end;
  86. buf[len] = 0;
  87. result.Append(buf);
  88. end:
  89. encoder->Reset();
  90. if (p != buf)
  91. free(p);
  92. return rv;
  93. }
  94. //----------------------------------------------------------------------------
  95. // nsStandardURL::nsPrefObserver
  96. //----------------------------------------------------------------------------
  97. #define NS_NET_PREF_ESCAPEUTF8 "network.standard-url.escape-utf8"
  98. #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
  99. NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver)
  100. NS_IMETHODIMP nsStandardURL::
  101. nsPrefObserver::Observe(nsISupports *subject,
  102. const char *topic,
  103. const char16_t *data)
  104. {
  105. if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  106. nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
  107. if (prefBranch) {
  108. PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
  109. }
  110. }
  111. return NS_OK;
  112. }
  113. //----------------------------------------------------------------------------
  114. // nsStandardURL::nsSegmentEncoder
  115. //----------------------------------------------------------------------------
  116. nsStandardURL::
  117. nsSegmentEncoder::nsSegmentEncoder(const char *charset)
  118. : mCharset(charset)
  119. {
  120. }
  121. int32_t nsStandardURL::
  122. nsSegmentEncoder::EncodeSegmentCount(const char *str,
  123. const URLSegment &seg,
  124. int16_t mask,
  125. nsAFlatCString &result,
  126. bool &appended,
  127. uint32_t extraLen)
  128. {
  129. // extraLen is characters outside the segment that will be
  130. // added when the segment is not empty (like the @ following
  131. // a username).
  132. appended = false;
  133. if (!str)
  134. return 0;
  135. int32_t len = 0;
  136. if (seg.mLen > 0) {
  137. uint32_t pos = seg.mPos;
  138. len = seg.mLen;
  139. // first honor the origin charset if appropriate. as an optimization,
  140. // only do this if the segment is non-ASCII. Further, if mCharset is
  141. // null or the empty string then the origin charset is UTF-8 and there
  142. // is nothing to do.
  143. nsAutoCString encBuf;
  144. if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) {
  145. // we have to encode this segment
  146. if (mEncoder || InitUnicodeEncoder()) {
  147. NS_ConvertUTF8toUTF16 ucsBuf(Substring(str + pos, str + pos + len));
  148. if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) {
  149. str = encBuf.get();
  150. pos = 0;
  151. len = encBuf.Length();
  152. }
  153. // else some failure occurred... assume UTF-8 is ok.
  154. }
  155. }
  156. // escape per RFC2396 unless UTF-8 and allowed by preferences
  157. int16_t escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII;
  158. uint32_t initLen = result.Length();
  159. // now perform any required escaping
  160. if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) {
  161. len = result.Length() - initLen;
  162. appended = true;
  163. }
  164. else if (str == encBuf.get()) {
  165. result += encBuf; // append only!!
  166. len = encBuf.Length();
  167. appended = true;
  168. }
  169. len += extraLen;
  170. }
  171. return len;
  172. }
  173. const nsACString &nsStandardURL::
  174. nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
  175. int16_t mask,
  176. nsAFlatCString &result)
  177. {
  178. const char *text;
  179. bool encoded;
  180. EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
  181. if (encoded)
  182. return result;
  183. return str;
  184. }
  185. bool nsStandardURL::
  186. nsSegmentEncoder::InitUnicodeEncoder()
  187. {
  188. NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!");
  189. // "replacement" won't survive another label resolution
  190. nsDependentCString label(mCharset);
  191. if (label.EqualsLiteral("replacement")) {
  192. mEncoder = EncodingUtils::EncoderForEncoding(label);
  193. return true;
  194. }
  195. nsAutoCString encoding;
  196. if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
  197. return false;
  198. }
  199. mEncoder = EncodingUtils::EncoderForEncoding(encoding);
  200. return true;
  201. }
  202. #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
  203. nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get())
  204. #define GET_SEGMENT_ENCODER(name) \
  205. GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8)
  206. #define GET_QUERY_ENCODER(name) \
  207. GET_SEGMENT_ENCODER_INTERNAL(name, false)
  208. //----------------------------------------------------------------------------
  209. // nsStandardURL <public>
  210. //----------------------------------------------------------------------------
  211. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  212. static PRCList gAllURLs;
  213. #endif
  214. nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
  215. : mDefaultPort(-1)
  216. , mPort(-1)
  217. , mHostA(nullptr)
  218. , mHostEncoding(eEncoding_ASCII)
  219. , mSpecEncoding(eEncoding_Unknown)
  220. , mURLType(URLTYPE_STANDARD)
  221. , mMutable(true)
  222. , mSupportsFileURL(aSupportsFileURL)
  223. {
  224. LOG(("Creating nsStandardURL @%p\n", this));
  225. if (!gInitialized) {
  226. gInitialized = true;
  227. InitGlobalObjects();
  228. }
  229. // default parser in case nsIStandardURL::Init is never called
  230. mParser = net_GetStdURLParser();
  231. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  232. memset(&mDebugCList, 0, sizeof(mDebugCList));
  233. if (NS_IsMainThread()) {
  234. if (aTrackURL) {
  235. PR_APPEND_LINK(&mDebugCList, &gAllURLs);
  236. } else {
  237. PR_INIT_CLIST(&mDebugCList);
  238. }
  239. }
  240. #endif
  241. }
  242. nsStandardURL::~nsStandardURL()
  243. {
  244. LOG(("Destroying nsStandardURL @%p\n", this));
  245. if (mHostA) {
  246. free(mHostA);
  247. }
  248. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  249. if (NS_IsMainThread()) {
  250. if (!PR_CLIST_IS_EMPTY(&mDebugCList)) {
  251. PR_REMOVE_LINK(&mDebugCList);
  252. }
  253. }
  254. #endif
  255. }
  256. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  257. struct DumpLeakedURLs {
  258. DumpLeakedURLs() {}
  259. ~DumpLeakedURLs();
  260. };
  261. DumpLeakedURLs::~DumpLeakedURLs()
  262. {
  263. MOZ_ASSERT(NS_IsMainThread());
  264. if (!PR_CLIST_IS_EMPTY(&gAllURLs)) {
  265. printf("Leaked URLs:\n");
  266. for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) {
  267. nsStandardURL *url = reinterpret_cast<nsStandardURL*>(reinterpret_cast<char*>(l) - offsetof(nsStandardURL, mDebugCList));
  268. url->PrintSpec();
  269. }
  270. }
  271. }
  272. #endif
  273. bool nsStandardURL::IsValid() {
  274. auto checkSegment = [&](const nsStandardURL::URLSegment& aSeg) {
  275. // Bad value
  276. if (NS_WARN_IF(aSeg.mLen < -1)) {
  277. return false;
  278. }
  279. if (aSeg.mLen == -1) {
  280. return true;
  281. }
  282. // Position outside of string
  283. if (NS_WARN_IF(aSeg.mPos + aSeg.mLen > mSpec.Length())) {
  284. return false;
  285. }
  286. // Overflow
  287. if (NS_WARN_IF(aSeg.mPos + aSeg.mLen < aSeg.mPos)) {
  288. return false;
  289. }
  290. return true;
  291. };
  292. bool allSegmentsValid = checkSegment(mScheme) && checkSegment(mAuthority) &&
  293. checkSegment(mUsername) && checkSegment(mPassword) &&
  294. checkSegment(mHost) && checkSegment(mPath) &&
  295. checkSegment(mFilepath) && checkSegment(mDirectory) &&
  296. checkSegment(mBasename) && checkSegment(mExtension) &&
  297. checkSegment(mQuery) && checkSegment(mRef);
  298. if (!allSegmentsValid) {
  299. return false;
  300. }
  301. if (mScheme.mPos != 0) {
  302. return false;
  303. }
  304. return true;
  305. }
  306. void
  307. nsStandardURL::InitGlobalObjects()
  308. {
  309. nsCOMPtr<nsIPrefBranch> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
  310. if (prefBranch) {
  311. nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
  312. prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false);
  313. prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false);
  314. PrefsChanged(prefBranch, nullptr);
  315. }
  316. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  317. PR_INIT_CLIST(&gAllURLs);
  318. #endif
  319. }
  320. void
  321. nsStandardURL::ShutdownGlobalObjects()
  322. {
  323. NS_IF_RELEASE(gIDN);
  324. #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
  325. if (gInitialized) {
  326. // This instanciates a dummy class, and will trigger the class
  327. // destructor when libxul is unloaded. This is equivalent to atexit(),
  328. // but gracefully handles dlclose().
  329. static DumpLeakedURLs d;
  330. }
  331. #endif
  332. }
  333. //----------------------------------------------------------------------------
  334. // nsStandardURL <private>
  335. //----------------------------------------------------------------------------
  336. void
  337. nsStandardURL::Clear()
  338. {
  339. mSpec.Truncate();
  340. mPort = -1;
  341. mScheme.Reset();
  342. mAuthority.Reset();
  343. mUsername.Reset();
  344. mPassword.Reset();
  345. mHost.Reset();
  346. mHostEncoding = eEncoding_ASCII;
  347. mPath.Reset();
  348. mFilepath.Reset();
  349. mDirectory.Reset();
  350. mBasename.Reset();
  351. mExtension.Reset();
  352. mQuery.Reset();
  353. mRef.Reset();
  354. InvalidateCache();
  355. }
  356. void
  357. nsStandardURL::InvalidateCache(bool invalidateCachedFile)
  358. {
  359. if (invalidateCachedFile)
  360. mFile = nullptr;
  361. if (mHostA) {
  362. free(mHostA);
  363. mHostA = nullptr;
  364. }
  365. mSpecEncoding = eEncoding_Unknown;
  366. }
  367. // |base| should be 8, 10 or 16. Not check the precondition for performance.
  368. /* static */ inline bool
  369. nsStandardURL::IsValidOfBase(unsigned char c, const uint32_t base) {
  370. MOZ_ASSERT(base == 8 || base == 10 || base == 16, "invalid base");
  371. if ('0' <= c && c <= '7') {
  372. return true;
  373. } else if (c == '8' || c== '9') {
  374. return base != 8;
  375. } else if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) {
  376. return base == 16;
  377. }
  378. return false;
  379. }
  380. /* static */ inline nsresult
  381. nsStandardURL::ParseIPv4Number(nsCString &input, uint32_t &number)
  382. {
  383. if (input.Length() == 0) {
  384. return NS_ERROR_FAILURE;
  385. }
  386. uint32_t base;
  387. uint32_t prefixLength = 0;
  388. if (input[0] == '0') {
  389. if (input.Length() == 1) {
  390. base = 10;
  391. } else if (input[1] == 'x' || input[1] == 'X') {
  392. base = 16;
  393. prefixLength = 2;
  394. } else {
  395. base = 8;
  396. prefixLength = 1;
  397. }
  398. } else {
  399. base = 10;
  400. }
  401. if (prefixLength == input.Length()) {
  402. return NS_ERROR_FAILURE;
  403. }
  404. // ignore leading zeros to calculate the valid length of number
  405. while (prefixLength < input.Length() && input[prefixLength] == '0') {
  406. prefixLength++;
  407. }
  408. // all zero case
  409. if (prefixLength == input.Length()) {
  410. number = 0;
  411. return NS_OK;
  412. }
  413. // overflow case
  414. if (input.Length() - prefixLength > 16) {
  415. return NS_ERROR_FAILURE;
  416. }
  417. for (uint32_t i = prefixLength; i < input.Length(); ++i) {
  418. if (!IsValidOfBase(input[i], base)) {
  419. return NS_ERROR_FAILURE;
  420. }
  421. }
  422. const char* fmt = "";
  423. switch (base) {
  424. case 8:
  425. fmt = "%llo";
  426. break;
  427. case 10:
  428. fmt = "%lli";
  429. break;
  430. case 16:
  431. fmt = "%llx";
  432. break;
  433. default:
  434. return NS_ERROR_FAILURE;
  435. }
  436. uint64_t number64;
  437. if (PR_sscanf(input.get(), fmt, &number64) == 1 &&
  438. number64 <= 0xffffffffu) {
  439. number = number64;
  440. return NS_OK;
  441. }
  442. return NS_ERROR_FAILURE;
  443. }
  444. // IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser
  445. /* static */ nsresult
  446. nsStandardURL::NormalizeIPv4(const nsCSubstring &host, nsCString &result)
  447. {
  448. if (host.Length() == 0 ||
  449. host[0] < '0' || '9' < host[0] || // bail-out fast
  450. FindInReadable(NS_LITERAL_CSTRING(".."), host)) {
  451. return NS_ERROR_FAILURE;
  452. }
  453. nsTArray<nsCString> parts;
  454. if (!ParseString(host, '.', parts) ||
  455. parts.Length() == 0 ||
  456. parts.Length() > 4) {
  457. return NS_ERROR_FAILURE;
  458. }
  459. uint32_t n = 0;
  460. nsTArray<int32_t> numbers;
  461. for (uint32_t i = 0; i < parts.Length(); ++i) {
  462. if (NS_FAILED(ParseIPv4Number(parts[i], n))) {
  463. return NS_ERROR_FAILURE;
  464. }
  465. numbers.AppendElement(n);
  466. }
  467. uint32_t ipv4 = numbers.LastElement();
  468. static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu,
  469. 0xffffu, 0xffu};
  470. if (ipv4 > upperBounds[numbers.Length() - 1]) {
  471. return NS_ERROR_FAILURE;
  472. }
  473. for (uint32_t i = 0; i < numbers.Length() - 1; ++i) {
  474. if (numbers[i] > 255) {
  475. return NS_ERROR_FAILURE;
  476. }
  477. ipv4 += numbers[i] << (8 * (3 - i));
  478. }
  479. uint8_t ipSegments[4];
  480. NetworkEndian::writeUint32(ipSegments, ipv4);
  481. result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1],
  482. ipSegments[2], ipSegments[3]);
  483. return NS_OK;
  484. }
  485. /**
  486. * Returns |true| if |aString| contains only ASCII characters according
  487. * to our CRT.
  488. *
  489. * @param aString an 8-bit wide string to scan
  490. */
  491. inline bool IsAsciiString(mozilla::Span<const char> aString) {
  492. for (char c : aString) {
  493. if (!nsCRT::IsAscii(c)) {
  494. return false;
  495. }
  496. }
  497. return true;
  498. }
  499. nsresult
  500. nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result)
  501. {
  502. nsresult rv = NS_ERROR_UNEXPECTED;
  503. // Clear result even if we bail.
  504. result.Truncate();
  505. if (!gIDN) {
  506. nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
  507. if (serv) {
  508. NS_ADDREF(gIDN = serv.get());
  509. }
  510. }
  511. if (!gIDN) {
  512. return NS_ERROR_UNEXPECTED;
  513. }
  514. NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding");
  515. bool isASCII;
  516. nsAutoCString normalized;
  517. // If the input is ASCII, and not ACE encoded, then there's no processing
  518. // needed. This is needed because we want to allow ascii labels longer than
  519. // 64 characters for some schemes.
  520. bool isACE = false;
  521. if (IsAsciiString(host) && NS_SUCCEEDED(gIDN->IsACE(host, &isACE)) && !isACE) {
  522. result = host;
  523. return NS_OK;
  524. }
  525. // If the input is an ACE encoded string it MUST be ASCII or it's malformed
  526. // according to the spec.
  527. if (!IsAsciiString(host) && isACE) {
  528. return NS_ERROR_MALFORMED_URI;
  529. }
  530. // Even if it's already ACE, we must still call ConvertUTF8toACE in order
  531. // for the input normalization to take place.
  532. rv = gIDN->ConvertUTF8toACE(host, normalized);
  533. if (NS_FAILED(rv)) {
  534. return rv;
  535. }
  536. // If the ASCII representation doesn't contain the xn-- token then we don't
  537. // need to call ConvertToDisplayIDN as that would not change anything.
  538. if (!StringBeginsWith(normalized, NS_LITERAL_CSTRING("xn--")) &&
  539. normalized.Find(NS_LITERAL_CSTRING(".xn--")) == kNotFound) {
  540. return NS_OK;
  541. }
  542. // Finally, convert to IDN
  543. rv = gIDN->ConvertToDisplayIDN(normalized, &isASCII, result);
  544. if (NS_SUCCEEDED(rv) && !isASCII) {
  545. mHostEncoding = eEncoding_UTF8;
  546. }
  547. return rv;
  548. }
  549. bool
  550. nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length)
  551. {
  552. if (!host || !*host) {
  553. // Should not be NULL or empty string
  554. return false;
  555. }
  556. if (length != strlen(host)) {
  557. // Embedded null
  558. return false;
  559. }
  560. bool openBracket = host[0] == '[';
  561. bool closeBracket = host[length - 1] == ']';
  562. if (openBracket && closeBracket) {
  563. return net_IsValidIPv6Addr(host + 1, length - 2);
  564. }
  565. if (openBracket || closeBracket) {
  566. // Fail if only one of the brackets is present
  567. return false;
  568. }
  569. const char *end = host + length;
  570. if (end != net_FindCharInSet(host, end, CONTROL_CHARACTERS " #/:?@[\\]*<>|\"^")) {
  571. // We still allow % because it is in the ID of addons.
  572. // Any percent encoded ASCII characters that are not allowed in the
  573. // hostname are not percent decoded, and will be parsed just fine.
  574. return false;
  575. }
  576. return true;
  577. }
  578. void
  579. nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
  580. {
  581. net_CoalesceDirs(coalesceFlag, path);
  582. int32_t newLen = strlen(path);
  583. if (newLen < mPath.mLen) {
  584. int32_t diff = newLen - mPath.mLen;
  585. mPath.mLen = newLen;
  586. mDirectory.mLen += diff;
  587. mFilepath.mLen += diff;
  588. ShiftFromBasename(diff);
  589. }
  590. }
  591. uint32_t
  592. nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str,
  593. const URLSegment &segInput, URLSegment &segOutput,
  594. const nsCString *escapedStr,
  595. bool useEscaped, int32_t *diff)
  596. {
  597. MOZ_ASSERT(segInput.mLen == segOutput.mLen);
  598. if (diff) *diff = 0;
  599. if (segInput.mLen > 0) {
  600. if (useEscaped) {
  601. MOZ_ASSERT(diff);
  602. segOutput.mLen = escapedStr->Length();
  603. *diff = segOutput.mLen - segInput.mLen;
  604. memcpy(buf + i, escapedStr->get(), segOutput.mLen);
  605. } else {
  606. memcpy(buf + i, str + segInput.mPos, segInput.mLen);
  607. }
  608. segOutput.mPos = i;
  609. i += segOutput.mLen;
  610. } else {
  611. segOutput.mPos = i;
  612. }
  613. return i;
  614. }
  615. uint32_t
  616. nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len)
  617. {
  618. memcpy(buf + i, str, len);
  619. return i + len;
  620. }
  621. // basic algorithm:
  622. // 1- escape url segments (for improved GetSpec efficiency)
  623. // 2- allocate spec buffer
  624. // 3- write url segments
  625. // 4- update url segment positions and lengths
  626. nsresult
  627. nsStandardURL::BuildNormalizedSpec(const char *spec)
  628. {
  629. // Assumptions: all member URLSegments must be relative the |spec| argument
  630. // passed to this function.
  631. // buffers for holding escaped url segments (these will remain empty unless
  632. // escaping is required).
  633. nsAutoCString encUsername, encPassword, encHost, encDirectory,
  634. encBasename, encExtension, encQuery, encRef;
  635. bool useEncUsername, useEncPassword, useEncHost = false,
  636. useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef;
  637. nsAutoCString portbuf;
  638. //
  639. // escape each URL segment, if necessary, and calculate approximate normalized
  640. // spec length.
  641. //
  642. // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
  643. uint32_t approxLen = 0;
  644. // the scheme is already ASCII
  645. if (mScheme.mLen > 0)
  646. approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
  647. // encode URL segments; convert UTF-8 to origin charset and possibly escape.
  648. // results written to encXXX variables only if |spec| is not already in the
  649. // appropriate encoding.
  650. {
  651. GET_SEGMENT_ENCODER(encoder);
  652. GET_QUERY_ENCODER(queryEncoder);
  653. // Items using an extraLen of 1 don't add anything unless mLen > 0
  654. // Username@
  655. approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1);
  656. // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
  657. if (mPassword.mLen >= 0)
  658. approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword);
  659. // mHost is handled differently below due to encoding differences
  660. MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
  661. if (mPort != -1 && mPort != mDefaultPort)
  662. {
  663. // :port
  664. portbuf.AppendInt(mPort);
  665. approxLen += portbuf.Length() + 1;
  666. }
  667. approxLen += 1; // reserve space for possible leading '/' - may not be needed
  668. // Should just use mPath? These are pessimistic, and thus waste space
  669. approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1);
  670. approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename);
  671. approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
  672. // These next ones *always* add their leading character even if length is 0
  673. // Handles items like "http://#"
  674. // ?query
  675. if (mQuery.mLen >= 0)
  676. approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery);
  677. // #ref
  678. if (mRef.mLen >= 0) {
  679. if (nsContentUtils::EncodeDecodeURLHash()) {
  680. approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref,
  681. encRef, useEncRef);
  682. } else {
  683. approxLen += 1 + mRef.mLen;
  684. useEncRef = false;
  685. }
  686. }
  687. }
  688. // do not escape the hostname, if IPv6 address literal, mHost will
  689. // already point to a [ ] delimited IPv6 address literal.
  690. // However, perform Unicode normalization on it, as IDN does.
  691. mHostEncoding = eEncoding_ASCII;
  692. // Note that we don't disallow URLs without a host - file:, etc
  693. if (mHost.mLen > 0) {
  694. nsAutoCString tempHost;
  695. NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost);
  696. if (tempHost.Contains('\0'))
  697. return NS_ERROR_MALFORMED_URI; // null embedded in hostname
  698. if (tempHost.Contains(' '))
  699. return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname
  700. nsresult rv = NormalizeIDN(tempHost, encHost);
  701. if (NS_FAILED(rv)) {
  702. return rv;
  703. }
  704. if (!SegmentIs(spec, mScheme, "resource") &&
  705. !SegmentIs(spec, mScheme, "chrome")) {
  706. nsAutoCString ipString;
  707. if (NS_SUCCEEDED(NormalizeIPv4(encHost, ipString))) {
  708. encHost = ipString;
  709. }
  710. }
  711. // NormalizeIDN always copies, if the call was successful.
  712. useEncHost = true;
  713. approxLen += encHost.Length();
  714. if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) {
  715. return NS_ERROR_MALFORMED_URI;
  716. }
  717. }
  718. // We must take a copy of every single segment because they are pointing to
  719. // the |spec| while we are changing their value, in case we must use
  720. // encoded strings.
  721. URLSegment username(mUsername);
  722. URLSegment password(mPassword);
  723. URLSegment host(mHost);
  724. URLSegment path(mPath);
  725. URLSegment filepath(mFilepath);
  726. URLSegment directory(mDirectory);
  727. URLSegment basename(mBasename);
  728. URLSegment extension(mExtension);
  729. URLSegment query(mQuery);
  730. URLSegment ref(mRef);
  731. //
  732. // generate the normalized URL string
  733. //
  734. // approxLen should be correct or 1 high
  735. if (!mSpec.SetLength(approxLen+1, fallible)) // buf needs a trailing '\0' below
  736. return NS_ERROR_OUT_OF_MEMORY;
  737. char *buf;
  738. mSpec.BeginWriting(buf);
  739. uint32_t i = 0;
  740. int32_t diff = 0;
  741. if (mScheme.mLen > 0) {
  742. i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
  743. net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
  744. i = AppendToBuf(buf, i, "://", 3);
  745. }
  746. // record authority starting position
  747. mAuthority.mPos = i;
  748. // append authority
  749. if (mUsername.mLen > 0) {
  750. i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
  751. &encUsername, useEncUsername, &diff);
  752. ShiftFromPassword(diff);
  753. if (password.mLen > 0) {
  754. buf[i++] = ':';
  755. i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
  756. &encPassword, useEncPassword, &diff);
  757. ShiftFromHost(diff);
  758. } else {
  759. mPassword.mLen = -1;
  760. }
  761. buf[i++] = '@';
  762. }
  763. if (host.mLen > 0) {
  764. i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
  765. &diff);
  766. ShiftFromPath(diff);
  767. net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
  768. MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
  769. if (mPort != -1 && mPort != mDefaultPort) {
  770. buf[i++] = ':';
  771. // Already formatted while building approxLen
  772. i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
  773. }
  774. }
  775. // record authority length
  776. mAuthority.mLen = i - mAuthority.mPos;
  777. // path must always start with a "/"
  778. if (mPath.mLen <= 0) {
  779. LOG(("setting path=/"));
  780. mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
  781. mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
  782. // basename must exist, even if empty (bug 113508)
  783. mBasename.mPos = i+1;
  784. mBasename.mLen = 0;
  785. buf[i++] = '/';
  786. }
  787. else {
  788. uint32_t leadingSlash = 0;
  789. if (spec[path.mPos] != '/') {
  790. LOG(("adding leading slash to path\n"));
  791. leadingSlash = 1;
  792. buf[i++] = '/';
  793. // basename must exist, even if empty (bugs 113508, 429347)
  794. if (mBasename.mLen == -1) {
  795. mBasename.mPos = basename.mPos = i;
  796. mBasename.mLen = basename.mLen = 0;
  797. }
  798. }
  799. // record corrected (file)path starting position
  800. mPath.mPos = mFilepath.mPos = i - leadingSlash;
  801. i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory,
  802. &encDirectory, useEncDirectory, &diff);
  803. ShiftFromBasename(diff);
  804. // the directory must end with a '/'
  805. if (buf[i-1] != '/') {
  806. buf[i++] = '/';
  807. mDirectory.mLen++;
  808. }
  809. i = AppendSegmentToBuf(buf, i, spec, basename, mBasename,
  810. &encBasename, useEncBasename, &diff);
  811. ShiftFromExtension(diff);
  812. // make corrections to directory segment if leadingSlash
  813. if (leadingSlash) {
  814. mDirectory.mPos = mPath.mPos;
  815. if (mDirectory.mLen >= 0)
  816. mDirectory.mLen += leadingSlash;
  817. else
  818. mDirectory.mLen = 1;
  819. }
  820. if (mExtension.mLen >= 0) {
  821. buf[i++] = '.';
  822. i = AppendSegmentToBuf(buf, i, spec, extension, mExtension,
  823. &encExtension, useEncExtension, &diff);
  824. ShiftFromQuery(diff);
  825. }
  826. // calculate corrected filepath length
  827. mFilepath.mLen = i - mFilepath.mPos;
  828. if (mQuery.mLen >= 0) {
  829. buf[i++] = '?';
  830. i = AppendSegmentToBuf(buf, i, spec, query, mQuery,
  831. &encQuery, useEncQuery,
  832. &diff);
  833. ShiftFromRef(diff);
  834. }
  835. if (mRef.mLen >= 0) {
  836. buf[i++] = '#';
  837. i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
  838. &diff);
  839. }
  840. // calculate corrected path length
  841. mPath.mLen = i - mPath.mPos;
  842. }
  843. buf[i] = '\0';
  844. if (mDirectory.mLen > 1) {
  845. netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
  846. if (SegmentIs(buf,mScheme,"ftp")) {
  847. coalesceFlag = (netCoalesceFlags) (coalesceFlag
  848. | NET_COALESCE_ALLOW_RELATIVE_ROOT
  849. | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
  850. }
  851. CoalescePath(coalesceFlag, buf + mDirectory.mPos);
  852. }
  853. mSpec.SetLength(strlen(buf));
  854. NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
  855. return NS_OK;
  856. }
  857. bool
  858. nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
  859. {
  860. // one or both may be null
  861. if (!val || mSpec.IsEmpty())
  862. return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
  863. if (seg.mLen < 0)
  864. return false;
  865. // if the first |seg.mLen| chars of |val| match, then |val| must
  866. // also be null terminated at |seg.mLen|.
  867. if (ignoreCase)
  868. return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
  869. && (val[seg.mLen] == '\0');
  870. else
  871. return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
  872. && (val[seg.mLen] == '\0');
  873. }
  874. bool
  875. nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase)
  876. {
  877. // one or both may be null
  878. if (!val || !spec)
  879. return (!val && (!spec || seg.mLen < 0));
  880. if (seg.mLen < 0)
  881. return false;
  882. // if the first |seg.mLen| chars of |val| match, then |val| must
  883. // also be null terminated at |seg.mLen|.
  884. if (ignoreCase)
  885. return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
  886. && (val[seg.mLen] == '\0');
  887. else
  888. return !strncmp(spec + seg.mPos, val, seg.mLen)
  889. && (val[seg.mLen] == '\0');
  890. }
  891. bool
  892. nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase)
  893. {
  894. if (seg1.mLen != seg2.mLen)
  895. return false;
  896. if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
  897. return true; // both are empty
  898. if (!val)
  899. return false;
  900. if (ignoreCase)
  901. return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
  902. else
  903. return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
  904. }
  905. int32_t
  906. nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen)
  907. {
  908. if (val && valLen) {
  909. if (len == 0)
  910. mSpec.Insert(val, pos, valLen);
  911. else
  912. mSpec.Replace(pos, len, nsDependentCString(val, valLen));
  913. return valLen - len;
  914. }
  915. // else remove the specified segment
  916. mSpec.Cut(pos, len);
  917. return -int32_t(len);
  918. }
  919. int32_t
  920. nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val)
  921. {
  922. if (len == 0)
  923. mSpec.Insert(val, pos);
  924. else
  925. mSpec.Replace(pos, len, val);
  926. return val.Length() - len;
  927. }
  928. nsresult
  929. nsStandardURL::ParseURL(const char *spec, int32_t specLen)
  930. {
  931. nsresult rv;
  932. if (specLen > net_GetURLMaxLength()) {
  933. return NS_ERROR_MALFORMED_URI;
  934. }
  935. //
  936. // parse given URL string
  937. //
  938. rv = mParser->ParseURL(spec, specLen,
  939. &mScheme.mPos, &mScheme.mLen,
  940. &mAuthority.mPos, &mAuthority.mLen,
  941. &mPath.mPos, &mPath.mLen);
  942. if (NS_FAILED(rv)) return rv;
  943. #ifdef DEBUG
  944. if (mScheme.mLen <= 0) {
  945. printf("spec=%s\n", spec);
  946. NS_WARNING("malformed url: no scheme");
  947. }
  948. #endif
  949. if (mAuthority.mLen > 0) {
  950. rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
  951. &mUsername.mPos, &mUsername.mLen,
  952. &mPassword.mPos, &mPassword.mLen,
  953. &mHost.mPos, &mHost.mLen,
  954. &mPort);
  955. if (NS_FAILED(rv)) return rv;
  956. // Don't allow mPort to be set to this URI's default port
  957. if (mPort == mDefaultPort)
  958. mPort = -1;
  959. mUsername.mPos += mAuthority.mPos;
  960. mPassword.mPos += mAuthority.mPos;
  961. mHost.mPos += mAuthority.mPos;
  962. }
  963. if (mPath.mLen > 0)
  964. rv = ParsePath(spec, mPath.mPos, mPath.mLen);
  965. return rv;
  966. }
  967. nsresult
  968. nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
  969. {
  970. LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
  971. if (pathLen > net_GetURLMaxLength()) {
  972. return NS_ERROR_MALFORMED_URI;
  973. }
  974. nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
  975. &mFilepath.mPos, &mFilepath.mLen,
  976. &mQuery.mPos, &mQuery.mLen,
  977. &mRef.mPos, &mRef.mLen);
  978. if (NS_FAILED(rv)) return rv;
  979. mFilepath.mPos += pathPos;
  980. mQuery.mPos += pathPos;
  981. mRef.mPos += pathPos;
  982. if (mFilepath.mLen > 0) {
  983. rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
  984. &mDirectory.mPos, &mDirectory.mLen,
  985. &mBasename.mPos, &mBasename.mLen,
  986. &mExtension.mPos, &mExtension.mLen);
  987. if (NS_FAILED(rv)) return rv;
  988. mDirectory.mPos += mFilepath.mPos;
  989. mBasename.mPos += mFilepath.mPos;
  990. mExtension.mPos += mFilepath.mPos;
  991. }
  992. return NS_OK;
  993. }
  994. char *
  995. nsStandardURL::AppendToSubstring(uint32_t pos,
  996. int32_t len,
  997. const char *tail)
  998. {
  999. // Verify pos and length are within boundaries
  1000. if (pos > mSpec.Length())
  1001. return nullptr;
  1002. if (len < 0)
  1003. return nullptr;
  1004. if ((uint32_t)len > (mSpec.Length() - pos))
  1005. return nullptr;
  1006. if (!tail)
  1007. return nullptr;
  1008. uint32_t tailLen = strlen(tail);
  1009. // Check for int overflow for proposed length of combined string
  1010. if (UINT32_MAX - ((uint32_t)len + 1) < tailLen)
  1011. return nullptr;
  1012. char *result = (char *) moz_xmalloc(len + tailLen + 1);
  1013. if (result) {
  1014. memcpy(result, mSpec.get() + pos, len);
  1015. memcpy(result + len, tail, tailLen);
  1016. result[len + tailLen] = '\0';
  1017. }
  1018. return result;
  1019. }
  1020. nsresult
  1021. nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
  1022. {
  1023. nsresult rv;
  1024. rv = stream->Read32(&seg.mPos);
  1025. if (NS_FAILED(rv)) return rv;
  1026. rv = stream->Read32((uint32_t *) &seg.mLen);
  1027. if (NS_FAILED(rv)) return rv;
  1028. return NS_OK;
  1029. }
  1030. nsresult
  1031. nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
  1032. {
  1033. nsresult rv;
  1034. rv = stream->Write32(seg.mPos);
  1035. if (NS_FAILED(rv)) return rv;
  1036. rv = stream->Write32(uint32_t(seg.mLen));
  1037. if (NS_FAILED(rv)) return rv;
  1038. return NS_OK;
  1039. }
  1040. /* static */ void
  1041. nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
  1042. {
  1043. bool val;
  1044. LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref));
  1045. #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p))
  1046. #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
  1047. if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) {
  1048. if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val))
  1049. gEscapeUTF8 = val;
  1050. LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled"));
  1051. }
  1052. if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) {
  1053. if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val))
  1054. gAlwaysEncodeInUTF8 = val;
  1055. LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled"));
  1056. }
  1057. #undef PREF_CHANGED
  1058. #undef GOT_PREF
  1059. }
  1060. #define SHIFT_FROM(name, what) \
  1061. void \
  1062. nsStandardURL::name(int32_t diff) \
  1063. { \
  1064. if (!diff) return; \
  1065. if (what.mLen >= 0) { \
  1066. CheckedInt<int32_t> pos = what.mPos; \
  1067. pos += diff; \
  1068. MOZ_ASSERT(pos.isValid()); \
  1069. what.mPos = pos.value(); \
  1070. }
  1071. #define SHIFT_FROM_NEXT(name, what, next) \
  1072. SHIFT_FROM(name, what) \
  1073. next(diff); \
  1074. }
  1075. #define SHIFT_FROM_LAST(name, what) \
  1076. SHIFT_FROM(name, what) \
  1077. }
  1078. SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername)
  1079. SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword)
  1080. SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost)
  1081. SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath)
  1082. SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath)
  1083. SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory)
  1084. SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename)
  1085. SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension)
  1086. SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery)
  1087. SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef)
  1088. SHIFT_FROM_LAST(ShiftFromRef, mRef)
  1089. //----------------------------------------------------------------------------
  1090. // nsStandardURL::nsISupports
  1091. //----------------------------------------------------------------------------
  1092. NS_IMPL_ADDREF(nsStandardURL)
  1093. NS_IMPL_RELEASE(nsStandardURL)
  1094. NS_INTERFACE_MAP_BEGIN(nsStandardURL)
  1095. NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
  1096. NS_INTERFACE_MAP_ENTRY(nsIURI)
  1097. NS_INTERFACE_MAP_ENTRY(nsIURL)
  1098. NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
  1099. NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
  1100. NS_INTERFACE_MAP_ENTRY(nsISerializable)
  1101. NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
  1102. NS_INTERFACE_MAP_ENTRY(nsIMutable)
  1103. NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
  1104. NS_INTERFACE_MAP_ENTRY(nsISensitiveInfoHiddenURI)
  1105. // see nsStandardURL::Equals
  1106. if (aIID.Equals(kThisImplCID))
  1107. foundInterface = static_cast<nsIURI *>(this);
  1108. else
  1109. NS_INTERFACE_MAP_ENTRY(nsISizeOf)
  1110. NS_INTERFACE_MAP_END
  1111. //----------------------------------------------------------------------------
  1112. // nsStandardURL::nsIURI
  1113. //----------------------------------------------------------------------------
  1114. // result may contain unescaped UTF-8 characters
  1115. NS_IMETHODIMP
  1116. nsStandardURL::GetSpec(nsACString &result)
  1117. {
  1118. MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
  1119. "The spec should never be this long, we missed a check.");
  1120. result = mSpec;
  1121. return NS_OK;
  1122. }
  1123. // result may contain unescaped UTF-8 characters
  1124. NS_IMETHODIMP
  1125. nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
  1126. {
  1127. result = mSpec;
  1128. if (mPassword.mLen >= 0) {
  1129. result.Replace(mPassword.mPos, mPassword.mLen, "****");
  1130. }
  1131. return NS_OK;
  1132. }
  1133. // result may contain unescaped UTF-8 characters
  1134. NS_IMETHODIMP
  1135. nsStandardURL::GetSpecIgnoringRef(nsACString &result)
  1136. {
  1137. // URI without ref is 0 to one char before ref
  1138. if (mRef.mLen >= 0) {
  1139. URLSegment noRef(0, mRef.mPos - 1);
  1140. result = Segment(noRef);
  1141. } else {
  1142. result = mSpec;
  1143. }
  1144. return NS_OK;
  1145. }
  1146. // result may contain unescaped UTF-8 characters
  1147. NS_IMETHODIMP
  1148. nsStandardURL::GetPrePath(nsACString &result)
  1149. {
  1150. result = Prepath();
  1151. return NS_OK;
  1152. }
  1153. // result is strictly US-ASCII
  1154. NS_IMETHODIMP
  1155. nsStandardURL::GetScheme(nsACString &result)
  1156. {
  1157. result = Scheme();
  1158. return NS_OK;
  1159. }
  1160. // result may contain unescaped UTF-8 characters
  1161. NS_IMETHODIMP
  1162. nsStandardURL::GetUserPass(nsACString &result)
  1163. {
  1164. result = Userpass();
  1165. return NS_OK;
  1166. }
  1167. // result may contain unescaped UTF-8 characters
  1168. NS_IMETHODIMP
  1169. nsStandardURL::GetUsername(nsACString &result)
  1170. {
  1171. result = Username();
  1172. return NS_OK;
  1173. }
  1174. // result may contain unescaped UTF-8 characters
  1175. NS_IMETHODIMP
  1176. nsStandardURL::GetPassword(nsACString &result)
  1177. {
  1178. result = Password();
  1179. return NS_OK;
  1180. }
  1181. NS_IMETHODIMP
  1182. nsStandardURL::GetHostPort(nsACString &result)
  1183. {
  1184. result = Hostport();
  1185. return NS_OK;
  1186. }
  1187. NS_IMETHODIMP
  1188. nsStandardURL::GetHost(nsACString &result)
  1189. {
  1190. result = Host();
  1191. return NS_OK;
  1192. }
  1193. NS_IMETHODIMP
  1194. nsStandardURL::GetPort(int32_t *result)
  1195. {
  1196. // should never be more than 16 bit
  1197. MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
  1198. *result = mPort;
  1199. return NS_OK;
  1200. }
  1201. // result may contain unescaped UTF-8 characters
  1202. NS_IMETHODIMP
  1203. nsStandardURL::GetPath(nsACString &result)
  1204. {
  1205. result = Path();
  1206. return NS_OK;
  1207. }
  1208. // result is ASCII
  1209. NS_IMETHODIMP
  1210. nsStandardURL::GetAsciiSpec(nsACString &result)
  1211. {
  1212. if (mSpecEncoding == eEncoding_Unknown) {
  1213. if (IsASCII(mSpec))
  1214. mSpecEncoding = eEncoding_ASCII;
  1215. else
  1216. mSpecEncoding = eEncoding_UTF8;
  1217. }
  1218. if (mSpecEncoding == eEncoding_ASCII) {
  1219. result = mSpec;
  1220. return NS_OK;
  1221. }
  1222. // try to guess the capacity required for result...
  1223. result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10));
  1224. result = Substring(mSpec, 0, mScheme.mLen + 3);
  1225. // This is left fallible as this entire function is expected to be
  1226. // infallible.
  1227. NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1228. // get the hostport
  1229. nsAutoCString hostport;
  1230. MOZ_ALWAYS_SUCCEEDS(GetAsciiHostPort(hostport));
  1231. result += hostport;
  1232. // This is left fallible as this entire function is expected to be
  1233. // infallible.
  1234. NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1235. return NS_OK;
  1236. }
  1237. // result is ASCII
  1238. NS_IMETHODIMP
  1239. nsStandardURL::GetAsciiHostPort(nsACString &result)
  1240. {
  1241. if (mHostEncoding == eEncoding_ASCII) {
  1242. result = Hostport();
  1243. return NS_OK;
  1244. }
  1245. MOZ_ALWAYS_SUCCEEDS(GetAsciiHost(result));
  1246. // As our mHostEncoding is not eEncoding_ASCII, we know that
  1247. // the our host is not ipv6, and we can avoid looking at it.
  1248. MOZ_ASSERT(result.FindChar(':') == -1, "The host must not be ipv6");
  1249. // hostport = "hostA" + ":port"
  1250. uint32_t pos = mHost.mPos + mHost.mLen;
  1251. if (pos < mPath.mPos)
  1252. result += Substring(mSpec, pos, mPath.mPos - pos);
  1253. return NS_OK;
  1254. }
  1255. // result is ASCII
  1256. NS_IMETHODIMP
  1257. nsStandardURL::GetAsciiHost(nsACString &result)
  1258. {
  1259. if (mHostEncoding == eEncoding_ASCII) {
  1260. result = Host();
  1261. return NS_OK;
  1262. }
  1263. // perhaps we have it cached...
  1264. if (mHostA) {
  1265. result = mHostA;
  1266. return NS_OK;
  1267. }
  1268. if (gIDN) {
  1269. nsresult rv;
  1270. rv = gIDN->ConvertUTF8toACE(Host(), result);
  1271. if (NS_SUCCEEDED(rv)) {
  1272. mHostA = ToNewCString(result);
  1273. return NS_OK;
  1274. }
  1275. NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
  1276. }
  1277. // something went wrong... guess all we can do is URL escape :-/
  1278. NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1279. return NS_OK;
  1280. }
  1281. NS_IMETHODIMP
  1282. nsStandardURL::GetOriginCharset(nsACString &result)
  1283. {
  1284. if (mOriginCharset.IsEmpty())
  1285. result.AssignLiteral("UTF-8");
  1286. else
  1287. result = mOriginCharset;
  1288. return NS_OK;
  1289. }
  1290. static bool
  1291. IsSpecialProtocol(const nsACString &input)
  1292. {
  1293. nsACString::const_iterator start, end;
  1294. input.BeginReading(start);
  1295. nsACString::const_iterator iterator(start);
  1296. input.EndReading(end);
  1297. while (iterator != end && *iterator != ':') {
  1298. iterator++;
  1299. }
  1300. nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get()));
  1301. return protocol.LowerCaseEqualsLiteral("http") ||
  1302. protocol.LowerCaseEqualsLiteral("https") ||
  1303. protocol.LowerCaseEqualsLiteral("ftp") ||
  1304. protocol.LowerCaseEqualsLiteral("ws") ||
  1305. protocol.LowerCaseEqualsLiteral("wss") ||
  1306. protocol.LowerCaseEqualsLiteral("file") ||
  1307. protocol.LowerCaseEqualsLiteral("gopher");
  1308. }
  1309. NS_IMETHODIMP
  1310. nsStandardURL::SetSpec(const nsACString &input)
  1311. {
  1312. ENSURE_MUTABLE();
  1313. const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  1314. LOG(("nsStandardURL::SetSpec [spec=%s]\n", flat.get()));
  1315. if (input.Length() > (uint32_t) net_GetURLMaxLength()) {
  1316. return NS_ERROR_MALFORMED_URI;
  1317. }
  1318. // filter out unexpected chars "\r\n\t" if necessary
  1319. nsAutoCString filteredURI;
  1320. net_FilterURIString(flat, filteredURI);
  1321. if (filteredURI.Length() == 0) {
  1322. return NS_ERROR_MALFORMED_URI;
  1323. }
  1324. // Make a backup of the curent URL
  1325. nsStandardURL prevURL(false,false);
  1326. prevURL.CopyMembers(this, eHonorRef, EmptyCString());
  1327. Clear();
  1328. if (IsSpecialProtocol(filteredURI)) {
  1329. // Bug 652186: Replace all backslashes with slashes when parsing paths
  1330. // Stop when we reach the query or the hash.
  1331. nsAutoCString::iterator start;
  1332. nsAutoCString::iterator end;
  1333. filteredURI.BeginWriting(start);
  1334. filteredURI.EndWriting(end);
  1335. while (start != end) {
  1336. if (*start == '?' || *start == '#') {
  1337. break;
  1338. }
  1339. if (*start == '\\') {
  1340. *start = '/';
  1341. }
  1342. start++;
  1343. }
  1344. }
  1345. const char *spec = filteredURI.get();
  1346. int32_t specLength = filteredURI.Length();
  1347. // parse the given URL...
  1348. nsresult rv = ParseURL(spec, specLength);
  1349. if (NS_SUCCEEDED(rv)) {
  1350. // finally, use the URLSegment member variables to build a normalized
  1351. // copy of |spec|
  1352. rv = BuildNormalizedSpec(spec);
  1353. }
  1354. // Make sure that a URLTYPE_AUTHORITY has a non-empty hostname.
  1355. if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
  1356. rv = NS_ERROR_MALFORMED_URI;
  1357. }
  1358. if (NS_FAILED(rv)) {
  1359. Clear();
  1360. // If parsing the spec has failed, restore the old URL
  1361. // so we don't end up with an empty URL.
  1362. CopyMembers(&prevURL, eHonorRef, EmptyCString());
  1363. return rv;
  1364. }
  1365. if (LOG_ENABLED()) {
  1366. LOG((" spec = %s\n", mSpec.get()));
  1367. LOG((" port = %d\n", mPort));
  1368. LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen));
  1369. LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
  1370. LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen));
  1371. LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen));
  1372. LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen));
  1373. LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen));
  1374. LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen));
  1375. LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
  1376. LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen));
  1377. LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
  1378. LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen));
  1379. LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen));
  1380. }
  1381. return rv;
  1382. }
  1383. NS_IMETHODIMP
  1384. nsStandardURL::SetScheme(const nsACString &input)
  1385. {
  1386. ENSURE_MUTABLE();
  1387. const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
  1388. LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
  1389. if (scheme.IsEmpty()) {
  1390. NS_WARNING("cannot remove the scheme from an url");
  1391. return NS_ERROR_UNEXPECTED;
  1392. }
  1393. if (mScheme.mLen < 0) {
  1394. NS_WARNING("uninitialized");
  1395. return NS_ERROR_NOT_INITIALIZED;
  1396. }
  1397. if (!net_IsValidScheme(scheme)) {
  1398. NS_WARNING("the given url scheme contains invalid characters");
  1399. return NS_ERROR_UNEXPECTED;
  1400. }
  1401. if (mSpec.Length() + input.Length() - Scheme().Length() > (uint32_t) net_GetURLMaxLength()) {
  1402. return NS_ERROR_MALFORMED_URI;
  1403. }
  1404. InvalidateCache();
  1405. int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
  1406. if (shift) {
  1407. mScheme.mLen = scheme.Length();
  1408. ShiftFromAuthority(shift);
  1409. }
  1410. // ensure new scheme is lowercase
  1411. //
  1412. // XXX the string code unfortunately doesn't provide a ToLowerCase
  1413. // that operates on a substring.
  1414. net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
  1415. return NS_OK;
  1416. }
  1417. NS_IMETHODIMP
  1418. nsStandardURL::SetUserPass(const nsACString &input)
  1419. {
  1420. ENSURE_MUTABLE();
  1421. const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
  1422. LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
  1423. if (mURLType == URLTYPE_NO_AUTHORITY) {
  1424. if (userpass.IsEmpty())
  1425. return NS_OK;
  1426. NS_WARNING("cannot set user:pass on no-auth url");
  1427. return NS_ERROR_UNEXPECTED;
  1428. }
  1429. if (mAuthority.mLen < 0) {
  1430. NS_WARNING("uninitialized");
  1431. return NS_ERROR_NOT_INITIALIZED;
  1432. }
  1433. if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) {
  1434. return NS_ERROR_MALFORMED_URI;
  1435. }
  1436. InvalidateCache();
  1437. if (userpass.IsEmpty()) {
  1438. // remove user:pass
  1439. if (mUsername.mLen > 0) {
  1440. if (mPassword.mLen > 0)
  1441. mUsername.mLen += (mPassword.mLen + 1);
  1442. mUsername.mLen++;
  1443. mSpec.Cut(mUsername.mPos, mUsername.mLen);
  1444. mAuthority.mLen -= mUsername.mLen;
  1445. ShiftFromHost(-mUsername.mLen);
  1446. mUsername.mLen = -1;
  1447. mPassword.mLen = -1;
  1448. }
  1449. return NS_OK;
  1450. }
  1451. NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
  1452. nsresult rv;
  1453. uint32_t usernamePos, passwordPos;
  1454. int32_t usernameLen, passwordLen;
  1455. rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
  1456. &usernamePos, &usernameLen,
  1457. &passwordPos, &passwordLen);
  1458. if (NS_FAILED(rv)) return rv;
  1459. // build new user:pass in |buf|
  1460. nsAutoCString buf;
  1461. if (usernameLen > 0) {
  1462. GET_SEGMENT_ENCODER(encoder);
  1463. bool ignoredOut;
  1464. usernameLen = encoder.EncodeSegmentCount(userpass.get(),
  1465. URLSegment(usernamePos,
  1466. usernameLen),
  1467. esc_Username | esc_AlwaysCopy,
  1468. buf, ignoredOut);
  1469. if (passwordLen > 0) {
  1470. buf.Append(':');
  1471. passwordLen = encoder.EncodeSegmentCount(userpass.get(),
  1472. URLSegment(passwordPos,
  1473. passwordLen),
  1474. esc_Password |
  1475. esc_AlwaysCopy, buf,
  1476. ignoredOut);
  1477. } else {
  1478. passwordLen = -1;
  1479. }
  1480. if (mUsername.mLen < 0)
  1481. buf.Append('@');
  1482. }
  1483. uint32_t shift = 0;
  1484. if (mUsername.mLen < 0) {
  1485. // no existing user:pass
  1486. if (!buf.IsEmpty()) {
  1487. mSpec.Insert(buf, mHost.mPos);
  1488. mUsername.mPos = mHost.mPos;
  1489. shift = buf.Length();
  1490. }
  1491. }
  1492. else {
  1493. // replace existing user:pass
  1494. uint32_t userpassLen = mUsername.mLen;
  1495. if (mPassword.mLen >= 0)
  1496. userpassLen += (mPassword.mLen + 1);
  1497. mSpec.Replace(mUsername.mPos, userpassLen, buf);
  1498. shift = buf.Length() - userpassLen;
  1499. }
  1500. if (shift) {
  1501. ShiftFromHost(shift);
  1502. mAuthority.mLen += shift;
  1503. }
  1504. // update positions and lengths
  1505. mUsername.mLen = usernameLen;
  1506. mPassword.mLen = passwordLen;
  1507. if (passwordLen > 0) {
  1508. mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
  1509. }
  1510. return NS_OK;
  1511. }
  1512. NS_IMETHODIMP
  1513. nsStandardURL::SetUsername(const nsACString &input)
  1514. {
  1515. ENSURE_MUTABLE();
  1516. const nsPromiseFlatCString &username = PromiseFlatCString(input);
  1517. LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
  1518. if (mURLType == URLTYPE_NO_AUTHORITY) {
  1519. if (username.IsEmpty())
  1520. return NS_OK;
  1521. NS_WARNING("cannot set username on no-auth url");
  1522. return NS_ERROR_UNEXPECTED;
  1523. }
  1524. if (username.IsEmpty())
  1525. return SetUserPass(username);
  1526. if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) {
  1527. return NS_ERROR_MALFORMED_URI;
  1528. }
  1529. InvalidateCache();
  1530. // escape username if necessary
  1531. nsAutoCString buf;
  1532. GET_SEGMENT_ENCODER(encoder);
  1533. const nsACString &escUsername =
  1534. encoder.EncodeSegment(username, esc_Username, buf);
  1535. int32_t shift;
  1536. if (mUsername.mLen < 0) {
  1537. mUsername.mPos = mAuthority.mPos;
  1538. mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
  1539. shift = escUsername.Length() + 1;
  1540. }
  1541. else
  1542. shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
  1543. if (shift) {
  1544. mUsername.mLen = escUsername.Length();
  1545. mAuthority.mLen += shift;
  1546. ShiftFromPassword(shift);
  1547. }
  1548. return NS_OK;
  1549. }
  1550. NS_IMETHODIMP
  1551. nsStandardURL::SetPassword(const nsACString &input)
  1552. {
  1553. ENSURE_MUTABLE();
  1554. const nsPromiseFlatCString &password = PromiseFlatCString(input);
  1555. LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
  1556. if (mURLType == URLTYPE_NO_AUTHORITY) {
  1557. if (password.IsEmpty())
  1558. return NS_OK;
  1559. NS_WARNING("cannot set password on no-auth url");
  1560. return NS_ERROR_UNEXPECTED;
  1561. }
  1562. if (mUsername.mLen <= 0) {
  1563. NS_WARNING("cannot set password without existing username");
  1564. return NS_ERROR_FAILURE;
  1565. }
  1566. if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) {
  1567. return NS_ERROR_MALFORMED_URI;
  1568. }
  1569. InvalidateCache();
  1570. if (password.IsEmpty()) {
  1571. if (mPassword.mLen >= 0) {
  1572. // cut(":password")
  1573. mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
  1574. ShiftFromHost(-(mPassword.mLen + 1));
  1575. mAuthority.mLen -= (mPassword.mLen + 1);
  1576. mPassword.mLen = -1;
  1577. }
  1578. return NS_OK;
  1579. }
  1580. // escape password if necessary
  1581. nsAutoCString buf;
  1582. GET_SEGMENT_ENCODER(encoder);
  1583. const nsACString &escPassword =
  1584. encoder.EncodeSegment(password, esc_Password, buf);
  1585. int32_t shift;
  1586. if (mPassword.mLen < 0) {
  1587. mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
  1588. mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
  1589. shift = escPassword.Length() + 1;
  1590. }
  1591. else
  1592. shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
  1593. if (shift) {
  1594. mPassword.mLen = escPassword.Length();
  1595. mAuthority.mLen += shift;
  1596. ShiftFromHost(shift);
  1597. }
  1598. return NS_OK;
  1599. }
  1600. void
  1601. nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
  1602. nsACString::const_iterator& aEnd)
  1603. {
  1604. for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
  1605. nsACString::const_iterator c(aStart);
  1606. if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
  1607. aEnd = c;
  1608. }
  1609. }
  1610. }
  1611. // If aValue only has a host part and no port number, the port
  1612. // will not be reset!!!
  1613. NS_IMETHODIMP
  1614. nsStandardURL::SetHostPort(const nsACString &aValue)
  1615. {
  1616. ENSURE_MUTABLE();
  1617. // We cannot simply call nsIURI::SetHost because that would treat the name as
  1618. // an IPv6 address (like http:://[server:443]/). We also cannot call
  1619. // nsIURI::SetHostPort because that isn't implemented. Sadfaces.
  1620. nsACString::const_iterator start, end;
  1621. aValue.BeginReading(start);
  1622. aValue.EndReading(end);
  1623. nsACString::const_iterator iter(start);
  1624. bool isIPv6 = false;
  1625. FindHostLimit(start, end);
  1626. if (*start == '[') { // IPv6 address
  1627. if (!FindCharInReadable(']', iter, end)) {
  1628. // the ] character is missing
  1629. return NS_ERROR_MALFORMED_URI;
  1630. }
  1631. // iter now at the ']' character
  1632. isIPv6 = true;
  1633. } else {
  1634. nsACString::const_iterator iter2(start);
  1635. if (FindCharInReadable(']', iter2, end)) {
  1636. // if the first char isn't [ then there should be no ] character
  1637. return NS_ERROR_MALFORMED_URI;
  1638. }
  1639. }
  1640. FindCharInReadable(':', iter, end);
  1641. if (!isIPv6 && iter != end) {
  1642. nsACString::const_iterator iter2(iter);
  1643. iter2++; // Skip over the first ':' character
  1644. if (FindCharInReadable(':', iter2, end)) {
  1645. // If there is more than one ':' character it suggests an IPv6
  1646. // The format should be [2001::1]:80 where the port is optional
  1647. return NS_ERROR_MALFORMED_URI;
  1648. }
  1649. }
  1650. nsresult rv = SetHost(Substring(start, iter));
  1651. NS_ENSURE_SUCCESS(rv, rv);
  1652. // Also set the port if needed.
  1653. if (iter != end) {
  1654. iter++;
  1655. if (iter != end) {
  1656. nsCString portStr(Substring(iter, end));
  1657. nsresult rv;
  1658. int32_t port = portStr.ToInteger(&rv);
  1659. if (NS_SUCCEEDED(rv)) {
  1660. rv = SetPort(port);
  1661. NS_ENSURE_SUCCESS(rv, rv);
  1662. } else {
  1663. // Failure parsing port number
  1664. return NS_ERROR_MALFORMED_URI;
  1665. }
  1666. } else {
  1667. // port number is missing
  1668. return NS_ERROR_MALFORMED_URI;
  1669. }
  1670. }
  1671. return NS_OK;
  1672. }
  1673. // This function is different than SetHostPort in that the port number will be
  1674. // reset as well if aValue parameter does not contain a port port number.
  1675. NS_IMETHODIMP
  1676. nsStandardURL::SetHostAndPort(const nsACString &aValue)
  1677. {
  1678. // Reset the port and than call SetHostPort. SetHostPort does not reset
  1679. // the port number.
  1680. nsresult rv = SetPort(-1);
  1681. NS_ENSURE_SUCCESS(rv, rv);
  1682. return SetHostPort(aValue);
  1683. }
  1684. NS_IMETHODIMP
  1685. nsStandardURL::SetHost(const nsACString &input)
  1686. {
  1687. ENSURE_MUTABLE();
  1688. const nsPromiseFlatCString &hostname = PromiseFlatCString(input);
  1689. nsACString::const_iterator start, end;
  1690. hostname.BeginReading(start);
  1691. hostname.EndReading(end);
  1692. FindHostLimit(start, end);
  1693. const nsCString unescapedHost(Substring(start, end));
  1694. // Do percent decoding on the the input.
  1695. nsAutoCString flat;
  1696. NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(),
  1697. esc_AlwaysCopy | esc_Host, flat);
  1698. const char *host = flat.get();
  1699. LOG(("nsStandardURL::SetHost [host=%s]\n", host));
  1700. if (mURLType == URLTYPE_NO_AUTHORITY) {
  1701. if (flat.IsEmpty())
  1702. return NS_OK;
  1703. NS_WARNING("cannot set host on no-auth url");
  1704. return NS_ERROR_UNEXPECTED;
  1705. } else {
  1706. if (flat.IsEmpty()) {
  1707. // Setting an empty hostname is not allowed for
  1708. // URLTYPE_STANDARD and URLTYPE_AUTHORITY.
  1709. return NS_ERROR_UNEXPECTED;
  1710. }
  1711. }
  1712. if (strlen(host) < flat.Length())
  1713. return NS_ERROR_MALFORMED_URI; // found embedded null
  1714. // For consistency with SetSpec/nsURLParsers, don't allow spaces
  1715. // in the hostname.
  1716. if (strchr(host, ' '))
  1717. return NS_ERROR_MALFORMED_URI;
  1718. if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) {
  1719. return NS_ERROR_MALFORMED_URI;
  1720. }
  1721. InvalidateCache();
  1722. mHostEncoding = eEncoding_ASCII;
  1723. uint32_t len;
  1724. nsAutoCString hostBuf;
  1725. nsresult rv = NormalizeIDN(flat, hostBuf);
  1726. if (NS_FAILED(rv)) {
  1727. return rv;
  1728. }
  1729. if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) {
  1730. nsAutoCString ipString;
  1731. if (NS_SUCCEEDED(NormalizeIPv4(hostBuf, ipString))) {
  1732. hostBuf = ipString;
  1733. }
  1734. }
  1735. // NormalizeIDN always copies if the call was successful
  1736. host = hostBuf.get();
  1737. len = hostBuf.Length();
  1738. if (!ValidIPv6orHostname(host, len)) {
  1739. return NS_ERROR_MALFORMED_URI;
  1740. }
  1741. if (mHost.mLen < 0) {
  1742. int port_length = 0;
  1743. if (mPort != -1) {
  1744. nsAutoCString buf;
  1745. buf.Assign(':');
  1746. buf.AppendInt(mPort);
  1747. port_length = buf.Length();
  1748. }
  1749. if (mAuthority.mLen > 0) {
  1750. mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
  1751. mHost.mLen = 0;
  1752. } else if (mScheme.mLen > 0) {
  1753. mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
  1754. mHost.mLen = 0;
  1755. }
  1756. }
  1757. int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
  1758. if (shift) {
  1759. mHost.mLen = len;
  1760. mAuthority.mLen += shift;
  1761. ShiftFromPath(shift);
  1762. }
  1763. // Now canonicalize the host to lowercase
  1764. net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
  1765. return NS_OK;
  1766. }
  1767. NS_IMETHODIMP
  1768. nsStandardURL::SetPort(int32_t port)
  1769. {
  1770. ENSURE_MUTABLE();
  1771. LOG(("nsStandardURL::SetPort [port=%d]\n", port));
  1772. if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
  1773. return NS_OK;
  1774. // ports must be >= 0 and 16 bit
  1775. // -1 == use default
  1776. if (port < -1 || port > std::numeric_limits<uint16_t>::max())
  1777. return NS_ERROR_MALFORMED_URI;
  1778. if (mURLType == URLTYPE_NO_AUTHORITY) {
  1779. NS_WARNING("cannot set port on no-auth url");
  1780. return NS_ERROR_UNEXPECTED;
  1781. }
  1782. InvalidateCache();
  1783. if (port == mDefaultPort) {
  1784. port = -1;
  1785. }
  1786. ReplacePortInSpec(port);
  1787. mPort = port;
  1788. return NS_OK;
  1789. }
  1790. /**
  1791. * Replaces the existing port in mSpec with aNewPort.
  1792. *
  1793. * The caller is responsible for:
  1794. * - Calling InvalidateCache (since our mSpec is changing).
  1795. * - Checking whether aNewPort is mDefaultPort (in which case the
  1796. * caller should pass aNewPort=-1).
  1797. */
  1798. void
  1799. nsStandardURL::ReplacePortInSpec(int32_t aNewPort)
  1800. {
  1801. MOZ_ASSERT(mMutable, "Caller should ensure we're mutable");
  1802. NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1,
  1803. "Caller should check its passed-in value and pass -1 instead of "
  1804. "mDefaultPort, to avoid encoding default port into mSpec");
  1805. // Create the (possibly empty) string that we're planning to replace:
  1806. nsAutoCString buf;
  1807. if (mPort != -1) {
  1808. buf.Assign(':');
  1809. buf.AppendInt(mPort);
  1810. }
  1811. // Find the position & length of that string:
  1812. const uint32_t replacedLen = buf.Length();
  1813. const uint32_t replacedStart =
  1814. mAuthority.mPos + mAuthority.mLen - replacedLen;
  1815. // Create the (possibly empty) replacement string:
  1816. if (aNewPort == -1) {
  1817. buf.Truncate();
  1818. } else {
  1819. buf.Assign(':');
  1820. buf.AppendInt(aNewPort);
  1821. }
  1822. // Perform the replacement:
  1823. mSpec.Replace(replacedStart, replacedLen, buf);
  1824. // Bookkeeping to reflect the new length:
  1825. int32_t shift = buf.Length() - replacedLen;
  1826. mAuthority.mLen += shift;
  1827. ShiftFromPath(shift);
  1828. }
  1829. NS_IMETHODIMP
  1830. nsStandardURL::SetPath(const nsACString &input)
  1831. {
  1832. ENSURE_MUTABLE();
  1833. const nsPromiseFlatCString &path = PromiseFlatCString(input);
  1834. LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
  1835. InvalidateCache();
  1836. if (!path.IsEmpty()) {
  1837. nsAutoCString spec;
  1838. spec.Assign(mSpec.get(), mPath.mPos);
  1839. if (path.First() != '/')
  1840. spec.Append('/');
  1841. spec.Append(path);
  1842. return SetSpec(spec);
  1843. }
  1844. else if (mPath.mLen >= 1) {
  1845. mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
  1846. // these contain only a '/'
  1847. mPath.mLen = 1;
  1848. mDirectory.mLen = 1;
  1849. mFilepath.mLen = 1;
  1850. // these are no longer defined
  1851. mBasename.mLen = -1;
  1852. mExtension.mLen = -1;
  1853. mQuery.mLen = -1;
  1854. mRef.mLen = -1;
  1855. }
  1856. return NS_OK;
  1857. }
  1858. NS_IMETHODIMP
  1859. nsStandardURL::Equals(nsIURI *unknownOther, bool *result)
  1860. {
  1861. return EqualsInternal(unknownOther, eHonorRef, result);
  1862. }
  1863. NS_IMETHODIMP
  1864. nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result)
  1865. {
  1866. return EqualsInternal(unknownOther, eIgnoreRef, result);
  1867. }
  1868. nsresult
  1869. nsStandardURL::EqualsInternal(nsIURI *unknownOther,
  1870. nsStandardURL::RefHandlingEnum refHandlingMode,
  1871. bool *result)
  1872. {
  1873. NS_ENSURE_ARG_POINTER(unknownOther);
  1874. NS_PRECONDITION(result, "null pointer");
  1875. RefPtr<nsStandardURL> other;
  1876. nsresult rv = unknownOther->QueryInterface(kThisImplCID,
  1877. getter_AddRefs(other));
  1878. if (NS_FAILED(rv)) {
  1879. *result = false;
  1880. return NS_OK;
  1881. }
  1882. // First, check whether one URIs is an nsIFileURL while the other
  1883. // is not. If that's the case, they're different.
  1884. if (mSupportsFileURL != other->mSupportsFileURL) {
  1885. *result = false;
  1886. return NS_OK;
  1887. }
  1888. // Next check parts of a URI that, if different, automatically make the
  1889. // URIs different
  1890. if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
  1891. // Check for host manually, since conversion to file will
  1892. // ignore the host!
  1893. !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
  1894. !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
  1895. !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
  1896. !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
  1897. Port() != other->Port()) {
  1898. // No need to compare files or other URI parts -- these are different
  1899. // beasties
  1900. *result = false;
  1901. return NS_OK;
  1902. }
  1903. if (refHandlingMode == eHonorRef &&
  1904. !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
  1905. *result = false;
  1906. return NS_OK;
  1907. }
  1908. // Then check for exact identity of URIs. If we have it, they're equal
  1909. if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
  1910. SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
  1911. SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
  1912. *result = true;
  1913. return NS_OK;
  1914. }
  1915. // At this point, the URIs are not identical, but they only differ in the
  1916. // directory/filename/extension. If these are file URLs, then get the
  1917. // corresponding file objects and compare those, since two filenames that
  1918. // differ, eg, only in case could still be equal.
  1919. if (mSupportsFileURL) {
  1920. // Assume not equal for failure cases... but failures in GetFile are
  1921. // really failures, more or less, so propagate them to caller.
  1922. *result = false;
  1923. rv = EnsureFile();
  1924. nsresult rv2 = other->EnsureFile();
  1925. // special case for resource:// urls that don't resolve to files
  1926. if (rv == NS_ERROR_NO_INTERFACE && rv == rv2)
  1927. return NS_OK;
  1928. if (NS_FAILED(rv)) {
  1929. LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
  1930. this, mSpec.get()));
  1931. return rv;
  1932. }
  1933. NS_ASSERTION(mFile, "EnsureFile() lied!");
  1934. rv = rv2;
  1935. if (NS_FAILED(rv)) {
  1936. LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
  1937. other.get(), other->mSpec.get()));
  1938. return rv;
  1939. }
  1940. NS_ASSERTION(other->mFile, "EnsureFile() lied!");
  1941. return mFile->Equals(other->mFile, result);
  1942. }
  1943. // The URLs are not identical, and they do not correspond to the
  1944. // same file, so they are different.
  1945. *result = false;
  1946. return NS_OK;
  1947. }
  1948. NS_IMETHODIMP
  1949. nsStandardURL::SchemeIs(const char *scheme, bool *result)
  1950. {
  1951. NS_PRECONDITION(result, "null pointer");
  1952. *result = SegmentIs(mScheme, scheme);
  1953. return NS_OK;
  1954. }
  1955. /* virtual */ nsStandardURL*
  1956. nsStandardURL::StartClone()
  1957. {
  1958. nsStandardURL *clone = new nsStandardURL();
  1959. return clone;
  1960. }
  1961. NS_IMETHODIMP
  1962. nsStandardURL::Clone(nsIURI **result)
  1963. {
  1964. return CloneInternal(eHonorRef, EmptyCString(), result);
  1965. }
  1966. NS_IMETHODIMP
  1967. nsStandardURL::CloneIgnoringRef(nsIURI **result)
  1968. {
  1969. return CloneInternal(eIgnoreRef, EmptyCString(), result);
  1970. }
  1971. NS_IMETHODIMP
  1972. nsStandardURL::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
  1973. {
  1974. return CloneInternal(eReplaceRef, newRef, result);
  1975. }
  1976. nsresult
  1977. nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
  1978. const nsACString& newRef,
  1979. nsIURI **result)
  1980. {
  1981. RefPtr<nsStandardURL> clone = StartClone();
  1982. if (!clone)
  1983. return NS_ERROR_OUT_OF_MEMORY;
  1984. // Copy local members into clone.
  1985. // Also copies the cached members mFile, mHostA
  1986. clone->CopyMembers(this, refHandlingMode, newRef, true);
  1987. clone.forget(result);
  1988. return NS_OK;
  1989. }
  1990. nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
  1991. nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
  1992. bool copyCached)
  1993. {
  1994. mSpec = source->mSpec;
  1995. mDefaultPort = source->mDefaultPort;
  1996. mPort = source->mPort;
  1997. mScheme = source->mScheme;
  1998. mAuthority = source->mAuthority;
  1999. mUsername = source->mUsername;
  2000. mPassword = source->mPassword;
  2001. mHost = source->mHost;
  2002. mPath = source->mPath;
  2003. mFilepath = source->mFilepath;
  2004. mDirectory = source->mDirectory;
  2005. mBasename = source->mBasename;
  2006. mExtension = source->mExtension;
  2007. mQuery = source->mQuery;
  2008. mRef = source->mRef;
  2009. mOriginCharset = source->mOriginCharset;
  2010. mURLType = source->mURLType;
  2011. mParser = source->mParser;
  2012. mMutable = true;
  2013. mSupportsFileURL = source->mSupportsFileURL;
  2014. mHostEncoding = source->mHostEncoding;
  2015. if (copyCached) {
  2016. mFile = source->mFile;
  2017. mHostA = source->mHostA ? strdup(source->mHostA) : nullptr;
  2018. mSpecEncoding = source->mSpecEncoding;
  2019. } else {
  2020. // The same state as after calling InvalidateCache()
  2021. mFile = nullptr;
  2022. mHostA = nullptr;
  2023. mSpecEncoding = eEncoding_Unknown;
  2024. }
  2025. if (refHandlingMode == eIgnoreRef) {
  2026. SetRef(EmptyCString());
  2027. } else if (refHandlingMode == eReplaceRef) {
  2028. SetRef(newRef);
  2029. }
  2030. return NS_OK;
  2031. }
  2032. NS_IMETHODIMP
  2033. nsStandardURL::Resolve(const nsACString &in, nsACString &out)
  2034. {
  2035. const nsPromiseFlatCString &flat = PromiseFlatCString(in);
  2036. // filter out unexpected chars "\r\n\t" if necessary
  2037. nsAutoCString buf;
  2038. net_FilterURIString(flat, buf);
  2039. const char *relpath = buf.get();
  2040. int32_t relpathLen = buf.Length();
  2041. char *result = nullptr;
  2042. LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
  2043. this, mSpec.get(), relpath));
  2044. NS_ASSERTION(mParser, "no parser: unitialized");
  2045. // NOTE: there is no need for this function to produce normalized
  2046. // output. normalization will occur when the result is used to
  2047. // initialize a nsStandardURL object.
  2048. if (mScheme.mLen < 0) {
  2049. NS_WARNING("unable to Resolve URL: this URL not initialized");
  2050. return NS_ERROR_NOT_INITIALIZED;
  2051. }
  2052. nsresult rv;
  2053. URLSegment scheme;
  2054. char *resultPath = nullptr;
  2055. bool relative = false;
  2056. uint32_t offset = 0;
  2057. netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
  2058. // relative urls should never contain a host, so we always want to use
  2059. // the noauth url parser.
  2060. // use it to extract a possible scheme
  2061. rv = mParser->ParseURL(relpath,
  2062. relpathLen,
  2063. &scheme.mPos, &scheme.mLen,
  2064. nullptr, nullptr,
  2065. nullptr, nullptr);
  2066. // if the parser fails (for example because there is no valid scheme)
  2067. // reset the scheme and assume a relative url
  2068. if (NS_FAILED(rv)) scheme.Reset();
  2069. nsAutoCString protocol(Segment(scheme));
  2070. nsAutoCString baseProtocol(Scheme());
  2071. // We need to do backslash replacement for the following cases:
  2072. // 1. The input is an absolute path with a http/https/ftp scheme
  2073. // 2. The input is a relative path, and the base URL has a http/https/ftp scheme
  2074. if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) ||
  2075. IsSpecialProtocol(protocol)) {
  2076. nsAutoCString::iterator start;
  2077. nsAutoCString::iterator end;
  2078. buf.BeginWriting(start);
  2079. buf.EndWriting(end);
  2080. while (start != end) {
  2081. if (*start == '?' || *start == '#') {
  2082. break;
  2083. }
  2084. if (*start == '\\') {
  2085. *start = '/';
  2086. }
  2087. start++;
  2088. }
  2089. }
  2090. if (scheme.mLen >= 0) {
  2091. // add some flags to coalesceFlag if it is an ftp-url
  2092. // need this later on when coalescing the resulting URL
  2093. if (SegmentIs(relpath, scheme, "ftp", true)) {
  2094. coalesceFlag = (netCoalesceFlags) (coalesceFlag
  2095. | NET_COALESCE_ALLOW_RELATIVE_ROOT
  2096. | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
  2097. }
  2098. // this URL appears to be absolute
  2099. // but try to find out more
  2100. if (SegmentIs(mScheme, relpath, scheme, true)) {
  2101. // mScheme and Scheme are the same
  2102. // but this can still be relative
  2103. if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen,
  2104. "://",3) == 0) {
  2105. // now this is really absolute
  2106. // because a :// follows the scheme
  2107. result = NS_strdup(relpath);
  2108. } else {
  2109. // This is a deprecated form of relative urls like
  2110. // http:file or http:/path/file
  2111. // we will support it for now ...
  2112. relative = true;
  2113. offset = scheme.mLen + 1;
  2114. }
  2115. } else {
  2116. // the schemes are not the same, we are also done
  2117. // because we have to assume this is absolute
  2118. result = NS_strdup(relpath);
  2119. }
  2120. } else {
  2121. // add some flags to coalesceFlag if it is an ftp-url
  2122. // need this later on when coalescing the resulting URL
  2123. if (SegmentIs(mScheme,"ftp")) {
  2124. coalesceFlag = (netCoalesceFlags) (coalesceFlag
  2125. | NET_COALESCE_ALLOW_RELATIVE_ROOT
  2126. | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
  2127. }
  2128. if (relpath[0] == '/' && relpath[1] == '/') {
  2129. // this URL //host/path is almost absolute
  2130. result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
  2131. } else {
  2132. // then it must be relative
  2133. relative = true;
  2134. }
  2135. }
  2136. if (relative) {
  2137. uint32_t len = 0;
  2138. const char *realrelpath = relpath + offset;
  2139. switch (*realrelpath) {
  2140. case '/':
  2141. // overwrite everything after the authority
  2142. len = mAuthority.mPos + mAuthority.mLen;
  2143. break;
  2144. case '?':
  2145. // overwrite the existing ?query and #ref
  2146. if (mQuery.mLen >= 0)
  2147. len = mQuery.mPos - 1;
  2148. else if (mRef.mLen >= 0)
  2149. len = mRef.mPos - 1;
  2150. else
  2151. len = mPath.mPos + mPath.mLen;
  2152. break;
  2153. case '#':
  2154. case '\0':
  2155. // overwrite the existing #ref
  2156. if (mRef.mLen < 0)
  2157. len = mPath.mPos + mPath.mLen;
  2158. else
  2159. len = mRef.mPos - 1;
  2160. break;
  2161. default:
  2162. if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
  2163. if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
  2164. nsCaseInsensitiveCStringComparator())) {
  2165. // if ftp URL ends with %2F then simply
  2166. // append relative part because %2F also
  2167. // marks the root directory with ftp-urls
  2168. len = mFilepath.mPos + mFilepath.mLen;
  2169. } else {
  2170. // overwrite everything after the directory
  2171. len = mDirectory.mPos + mDirectory.mLen;
  2172. }
  2173. } else {
  2174. // overwrite everything after the directory
  2175. len = mDirectory.mPos + mDirectory.mLen;
  2176. }
  2177. }
  2178. result = AppendToSubstring(0, len, realrelpath);
  2179. // locate result path
  2180. resultPath = result + mPath.mPos;
  2181. }
  2182. if (!result)
  2183. return NS_ERROR_OUT_OF_MEMORY;
  2184. if (resultPath)
  2185. net_CoalesceDirs(coalesceFlag, resultPath);
  2186. else {
  2187. // locate result path
  2188. resultPath = PL_strstr(result, "://");
  2189. if (resultPath) {
  2190. resultPath = PL_strchr(resultPath + 3, '/');
  2191. if (resultPath)
  2192. net_CoalesceDirs(coalesceFlag,resultPath);
  2193. }
  2194. }
  2195. out.Adopt(result);
  2196. return NS_OK;
  2197. }
  2198. // result may contain unescaped UTF-8 characters
  2199. NS_IMETHODIMP
  2200. nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
  2201. {
  2202. NS_ENSURE_ARG_POINTER(uri2);
  2203. // if uri's are equal, then return uri as is
  2204. bool isEquals = false;
  2205. if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
  2206. return GetSpec(aResult);
  2207. aResult.Truncate();
  2208. // check pre-path; if they don't match, then return empty string
  2209. nsStandardURL *stdurl2;
  2210. nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
  2211. isEquals = NS_SUCCEEDED(rv)
  2212. && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
  2213. && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
  2214. && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
  2215. && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
  2216. && (Port() == stdurl2->Port());
  2217. if (!isEquals)
  2218. {
  2219. if (NS_SUCCEEDED(rv))
  2220. NS_RELEASE(stdurl2);
  2221. return NS_OK;
  2222. }
  2223. // scan for first mismatched character
  2224. const char *thisIndex, *thatIndex, *startCharPos;
  2225. startCharPos = mSpec.get() + mDirectory.mPos;
  2226. thisIndex = startCharPos;
  2227. thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
  2228. while ((*thisIndex == *thatIndex) && *thisIndex)
  2229. {
  2230. thisIndex++;
  2231. thatIndex++;
  2232. }
  2233. // backup to just after previous slash so we grab an appropriate path
  2234. // segment such as a directory (not partial segments)
  2235. // todo: also check for file matches which include '?' and '#'
  2236. while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/'))
  2237. thisIndex--;
  2238. // grab spec from beginning to thisIndex
  2239. aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
  2240. NS_RELEASE(stdurl2);
  2241. return rv;
  2242. }
  2243. NS_IMETHODIMP
  2244. nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
  2245. {
  2246. NS_ENSURE_ARG_POINTER(uri2);
  2247. aResult.Truncate();
  2248. // if uri's are equal, then return empty string
  2249. bool isEquals = false;
  2250. if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
  2251. return NS_OK;
  2252. nsStandardURL *stdurl2;
  2253. nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
  2254. isEquals = NS_SUCCEEDED(rv)
  2255. && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
  2256. && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
  2257. && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
  2258. && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
  2259. && (Port() == stdurl2->Port());
  2260. if (!isEquals)
  2261. {
  2262. if (NS_SUCCEEDED(rv))
  2263. NS_RELEASE(stdurl2);
  2264. return uri2->GetSpec(aResult);
  2265. }
  2266. // scan for first mismatched character
  2267. const char *thisIndex, *thatIndex, *startCharPos;
  2268. startCharPos = mSpec.get() + mDirectory.mPos;
  2269. thisIndex = startCharPos;
  2270. thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
  2271. #ifdef XP_WIN
  2272. bool isFileScheme = SegmentIs(mScheme, "file");
  2273. if (isFileScheme)
  2274. {
  2275. // on windows, we need to match the first segment of the path
  2276. // if these don't match then we need to return an absolute path
  2277. // skip over any leading '/' in path
  2278. while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
  2279. {
  2280. thisIndex++;
  2281. thatIndex++;
  2282. }
  2283. // look for end of first segment
  2284. while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
  2285. {
  2286. thisIndex++;
  2287. thatIndex++;
  2288. }
  2289. // if we didn't match through the first segment, return absolute path
  2290. if ((*thisIndex != '/') || (*thatIndex != '/'))
  2291. {
  2292. NS_RELEASE(stdurl2);
  2293. return uri2->GetSpec(aResult);
  2294. }
  2295. }
  2296. #endif
  2297. while ((*thisIndex == *thatIndex) && *thisIndex)
  2298. {
  2299. thisIndex++;
  2300. thatIndex++;
  2301. }
  2302. // backup to just after previous slash so we grab an appropriate path
  2303. // segment such as a directory (not partial segments)
  2304. // todo: also check for file matches with '#' and '?'
  2305. while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
  2306. thatIndex--;
  2307. const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen;
  2308. // need to account for slashes and add corresponding "../"
  2309. for (; thisIndex <= limit && *thisIndex; ++thisIndex)
  2310. {
  2311. if (*thisIndex == '/')
  2312. aResult.AppendLiteral("../");
  2313. }
  2314. // grab spec from thisIndex to end
  2315. uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
  2316. aResult.Append(Substring(stdurl2->mSpec, startPos,
  2317. stdurl2->mSpec.Length() - startPos));
  2318. NS_RELEASE(stdurl2);
  2319. return rv;
  2320. }
  2321. //----------------------------------------------------------------------------
  2322. // nsStandardURL::nsIURL
  2323. //----------------------------------------------------------------------------
  2324. // result may contain unescaped UTF-8 characters
  2325. NS_IMETHODIMP
  2326. nsStandardURL::GetFilePath(nsACString &result)
  2327. {
  2328. result = Filepath();
  2329. return NS_OK;
  2330. }
  2331. // result may contain unescaped UTF-8 characters
  2332. NS_IMETHODIMP
  2333. nsStandardURL::GetQuery(nsACString &result)
  2334. {
  2335. result = Query();
  2336. return NS_OK;
  2337. }
  2338. // result may contain unescaped UTF-8 characters
  2339. NS_IMETHODIMP
  2340. nsStandardURL::GetRef(nsACString &result)
  2341. {
  2342. result = Ref();
  2343. return NS_OK;
  2344. }
  2345. NS_IMETHODIMP
  2346. nsStandardURL::GetHasRef(bool *result)
  2347. {
  2348. *result = (mRef.mLen >= 0);
  2349. return NS_OK;
  2350. }
  2351. // result may contain unescaped UTF-8 characters
  2352. NS_IMETHODIMP
  2353. nsStandardURL::GetDirectory(nsACString &result)
  2354. {
  2355. result = Directory();
  2356. return NS_OK;
  2357. }
  2358. // result may contain unescaped UTF-8 characters
  2359. NS_IMETHODIMP
  2360. nsStandardURL::GetFileName(nsACString &result)
  2361. {
  2362. result = Filename();
  2363. return NS_OK;
  2364. }
  2365. // result may contain unescaped UTF-8 characters
  2366. NS_IMETHODIMP
  2367. nsStandardURL::GetFileBaseName(nsACString &result)
  2368. {
  2369. result = Basename();
  2370. return NS_OK;
  2371. }
  2372. // result may contain unescaped UTF-8 characters
  2373. NS_IMETHODIMP
  2374. nsStandardURL::GetFileExtension(nsACString &result)
  2375. {
  2376. result = Extension();
  2377. return NS_OK;
  2378. }
  2379. NS_IMETHODIMP
  2380. nsStandardURL::SetFilePath(const nsACString &input)
  2381. {
  2382. ENSURE_MUTABLE();
  2383. const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2384. const char *filepath = flat.get();
  2385. LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
  2386. // if there isn't a filepath, then there can't be anything
  2387. // after the path either. this url is likely uninitialized.
  2388. if (mFilepath.mLen < 0)
  2389. return SetPath(flat);
  2390. if (filepath && *filepath) {
  2391. nsAutoCString spec;
  2392. uint32_t dirPos, basePos, extPos;
  2393. int32_t dirLen, baseLen, extLen;
  2394. nsresult rv;
  2395. rv = mParser->ParseFilePath(filepath, flat.Length(),
  2396. &dirPos, &dirLen,
  2397. &basePos, &baseLen,
  2398. &extPos, &extLen);
  2399. if (NS_FAILED(rv)) return rv;
  2400. // build up new candidate spec
  2401. spec.Assign(mSpec.get(), mPath.mPos);
  2402. // ensure leading '/'
  2403. if (filepath[dirPos] != '/')
  2404. spec.Append('/');
  2405. GET_SEGMENT_ENCODER(encoder);
  2406. // append encoded filepath components
  2407. if (dirLen > 0)
  2408. encoder.EncodeSegment(Substring(filepath + dirPos,
  2409. filepath + dirPos + dirLen),
  2410. esc_Directory | esc_AlwaysCopy, spec);
  2411. if (baseLen > 0)
  2412. encoder.EncodeSegment(Substring(filepath + basePos,
  2413. filepath + basePos + baseLen),
  2414. esc_FileBaseName | esc_AlwaysCopy, spec);
  2415. if (extLen >= 0) {
  2416. spec.Append('.');
  2417. if (extLen > 0)
  2418. encoder.EncodeSegment(Substring(filepath + extPos,
  2419. filepath + extPos + extLen),
  2420. esc_FileExtension | esc_AlwaysCopy,
  2421. spec);
  2422. }
  2423. // compute the ending position of the current filepath
  2424. if (mFilepath.mLen >= 0) {
  2425. uint32_t end = mFilepath.mPos + mFilepath.mLen;
  2426. if (mSpec.Length() > end)
  2427. spec.Append(mSpec.get() + end, mSpec.Length() - end);
  2428. }
  2429. return SetSpec(spec);
  2430. }
  2431. if (mPath.mLen > 1) {
  2432. mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
  2433. // left shift query, and ref
  2434. ShiftFromQuery(1 - mFilepath.mLen);
  2435. // One character for '/', and if we have a query or ref we add their
  2436. // length and one extra for each '?' or '#' characters
  2437. mPath.mLen = 1 + (mQuery.mLen >= 0 ? (mQuery.mLen + 1) : 0) +
  2438. (mRef.mLen >= 0 ? (mRef.mLen + 1) : 0);
  2439. // these contain only a '/'
  2440. mDirectory.mLen = 1;
  2441. mFilepath.mLen = 1;
  2442. // these are no longer defined
  2443. mBasename.mLen = -1;
  2444. mExtension.mLen = -1;
  2445. }
  2446. return NS_OK;
  2447. }
  2448. NS_IMETHODIMP
  2449. nsStandardURL::SetQuery(const nsACString &input)
  2450. {
  2451. ENSURE_MUTABLE();
  2452. const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2453. const char *query = flat.get();
  2454. LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
  2455. if (mPath.mLen < 0)
  2456. return SetPath(flat);
  2457. if (mSpec.Length() + input.Length() - Query().Length() > (uint32_t) net_GetURLMaxLength()) {
  2458. return NS_ERROR_MALFORMED_URI;
  2459. }
  2460. InvalidateCache();
  2461. if (!query || !*query) {
  2462. // remove existing query
  2463. if (mQuery.mLen >= 0) {
  2464. // remove query and leading '?'
  2465. mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
  2466. ShiftFromRef(-(mQuery.mLen + 1));
  2467. mPath.mLen -= (mQuery.mLen + 1);
  2468. mQuery.mPos = 0;
  2469. mQuery.mLen = -1;
  2470. }
  2471. return NS_OK;
  2472. }
  2473. int32_t queryLen = flat.Length();
  2474. if (query[0] == '?') {
  2475. query++;
  2476. queryLen--;
  2477. }
  2478. if (mQuery.mLen < 0) {
  2479. if (mRef.mLen < 0)
  2480. mQuery.mPos = mSpec.Length();
  2481. else
  2482. mQuery.mPos = mRef.mPos - 1;
  2483. mSpec.Insert('?', mQuery.mPos);
  2484. mQuery.mPos++;
  2485. mQuery.mLen = 0;
  2486. // the insertion pushes these out by 1
  2487. mPath.mLen++;
  2488. mRef.mPos++;
  2489. }
  2490. // encode query if necessary
  2491. nsAutoCString buf;
  2492. bool encoded;
  2493. GET_QUERY_ENCODER(encoder);
  2494. encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
  2495. buf, encoded);
  2496. if (encoded) {
  2497. query = buf.get();
  2498. queryLen = buf.Length();
  2499. }
  2500. int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
  2501. if (shift) {
  2502. mQuery.mLen = queryLen;
  2503. mPath.mLen += shift;
  2504. ShiftFromRef(shift);
  2505. }
  2506. return NS_OK;
  2507. }
  2508. NS_IMETHODIMP
  2509. nsStandardURL::SetRef(const nsACString &input)
  2510. {
  2511. ENSURE_MUTABLE();
  2512. const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2513. const char *ref = flat.get();
  2514. LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
  2515. if (mPath.mLen < 0)
  2516. return SetPath(flat);
  2517. if (mSpec.Length() + input.Length() - Ref().Length() > (uint32_t) net_GetURLMaxLength()) {
  2518. return NS_ERROR_MALFORMED_URI;
  2519. }
  2520. InvalidateCache();
  2521. if (!ref || !*ref) {
  2522. // remove existing ref
  2523. if (mRef.mLen >= 0) {
  2524. // remove ref and leading '#'
  2525. mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
  2526. mPath.mLen -= (mRef.mLen + 1);
  2527. mRef.mPos = 0;
  2528. mRef.mLen = -1;
  2529. }
  2530. return NS_OK;
  2531. }
  2532. int32_t refLen = flat.Length();
  2533. if (ref[0] == '#') {
  2534. ref++;
  2535. refLen--;
  2536. }
  2537. if (mRef.mLen < 0) {
  2538. mSpec.Append('#');
  2539. ++mPath.mLen; // Include the # in the path.
  2540. mRef.mPos = mSpec.Length();
  2541. mRef.mLen = 0;
  2542. }
  2543. // If precent encoding is necessary, `ref` will point to `buf`'s content.
  2544. // `buf` needs to outlive any use of the `ref` pointer.
  2545. nsAutoCString buf;
  2546. if (nsContentUtils::EncodeDecodeURLHash()) {
  2547. // encode ref if necessary
  2548. bool encoded;
  2549. GET_SEGMENT_ENCODER(encoder);
  2550. encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
  2551. buf, encoded);
  2552. if (encoded) {
  2553. ref = buf.get();
  2554. refLen = buf.Length();
  2555. }
  2556. }
  2557. int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
  2558. mPath.mLen += shift;
  2559. mRef.mLen = refLen;
  2560. return NS_OK;
  2561. }
  2562. NS_IMETHODIMP
  2563. nsStandardURL::SetDirectory(const nsACString &input)
  2564. {
  2565. NS_NOTYETIMPLEMENTED("");
  2566. return NS_ERROR_NOT_IMPLEMENTED;
  2567. }
  2568. NS_IMETHODIMP
  2569. nsStandardURL::SetFileName(const nsACString &input)
  2570. {
  2571. ENSURE_MUTABLE();
  2572. const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2573. const char *filename = flat.get();
  2574. LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
  2575. if (mPath.mLen < 0)
  2576. return SetPath(flat);
  2577. if (mSpec.Length() + input.Length() - Filename().Length() > (uint32_t) net_GetURLMaxLength()) {
  2578. return NS_ERROR_MALFORMED_URI;
  2579. }
  2580. int32_t shift = 0;
  2581. if (!(filename && *filename)) {
  2582. // remove the filename
  2583. if (mBasename.mLen > 0) {
  2584. if (mExtension.mLen >= 0)
  2585. mBasename.mLen += (mExtension.mLen + 1);
  2586. mSpec.Cut(mBasename.mPos, mBasename.mLen);
  2587. shift = -mBasename.mLen;
  2588. mBasename.mLen = 0;
  2589. mExtension.mLen = -1;
  2590. }
  2591. }
  2592. else {
  2593. nsresult rv;
  2594. URLSegment basename, extension;
  2595. // let the parser locate the basename and extension
  2596. rv = mParser->ParseFileName(filename, flat.Length(),
  2597. &basename.mPos, &basename.mLen,
  2598. &extension.mPos, &extension.mLen);
  2599. if (NS_FAILED(rv)) return rv;
  2600. if (basename.mLen < 0) {
  2601. // remove existing filename
  2602. if (mBasename.mLen >= 0) {
  2603. uint32_t len = mBasename.mLen;
  2604. if (mExtension.mLen >= 0)
  2605. len += (mExtension.mLen + 1);
  2606. mSpec.Cut(mBasename.mPos, len);
  2607. shift = -int32_t(len);
  2608. mBasename.mLen = 0;
  2609. mExtension.mLen = -1;
  2610. }
  2611. }
  2612. else {
  2613. nsAutoCString newFilename;
  2614. bool ignoredOut;
  2615. GET_SEGMENT_ENCODER(encoder);
  2616. basename.mLen = encoder.EncodeSegmentCount(filename, basename,
  2617. esc_FileBaseName |
  2618. esc_AlwaysCopy,
  2619. newFilename,
  2620. ignoredOut);
  2621. if (extension.mLen >= 0) {
  2622. newFilename.Append('.');
  2623. extension.mLen = encoder.EncodeSegmentCount(filename, extension,
  2624. esc_FileExtension |
  2625. esc_AlwaysCopy,
  2626. newFilename,
  2627. ignoredOut);
  2628. }
  2629. if (mBasename.mLen < 0) {
  2630. // insert new filename
  2631. mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
  2632. mSpec.Insert(newFilename, mBasename.mPos);
  2633. shift = newFilename.Length();
  2634. }
  2635. else {
  2636. // replace existing filename
  2637. uint32_t oldLen = uint32_t(mBasename.mLen);
  2638. if (mExtension.mLen >= 0)
  2639. oldLen += (mExtension.mLen + 1);
  2640. mSpec.Replace(mBasename.mPos, oldLen, newFilename);
  2641. shift = newFilename.Length() - oldLen;
  2642. }
  2643. mBasename.mLen = basename.mLen;
  2644. mExtension.mLen = extension.mLen;
  2645. if (mExtension.mLen >= 0)
  2646. mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
  2647. }
  2648. }
  2649. if (shift) {
  2650. ShiftFromQuery(shift);
  2651. mFilepath.mLen += shift;
  2652. mPath.mLen += shift;
  2653. }
  2654. return NS_OK;
  2655. }
  2656. NS_IMETHODIMP
  2657. nsStandardURL::SetFileBaseName(const nsACString &input)
  2658. {
  2659. nsAutoCString extension;
  2660. nsresult rv = GetFileExtension(extension);
  2661. NS_ENSURE_SUCCESS(rv, rv);
  2662. nsAutoCString newFileName(input);
  2663. if (!extension.IsEmpty()) {
  2664. newFileName.Append('.');
  2665. newFileName.Append(extension);
  2666. }
  2667. return SetFileName(newFileName);
  2668. }
  2669. NS_IMETHODIMP
  2670. nsStandardURL::SetFileExtension(const nsACString &input)
  2671. {
  2672. nsAutoCString newFileName;
  2673. nsresult rv = GetFileBaseName(newFileName);
  2674. NS_ENSURE_SUCCESS(rv, rv);
  2675. if (!input.IsEmpty()) {
  2676. newFileName.Append('.');
  2677. newFileName.Append(input);
  2678. }
  2679. return SetFileName(newFileName);
  2680. }
  2681. //----------------------------------------------------------------------------
  2682. // nsStandardURL::nsIFileURL
  2683. //----------------------------------------------------------------------------
  2684. nsresult
  2685. nsStandardURL::EnsureFile()
  2686. {
  2687. NS_PRECONDITION(mSupportsFileURL,
  2688. "EnsureFile() called on a URL that doesn't support files!");
  2689. if (mFile) {
  2690. // Nothing to do
  2691. return NS_OK;
  2692. }
  2693. // Parse the spec if we don't have a cached result
  2694. if (mSpec.IsEmpty()) {
  2695. NS_WARNING("url not initialized");
  2696. return NS_ERROR_NOT_INITIALIZED;
  2697. }
  2698. if (!SegmentIs(mScheme, "file")) {
  2699. NS_WARNING("not a file URL");
  2700. return NS_ERROR_FAILURE;
  2701. }
  2702. return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
  2703. }
  2704. NS_IMETHODIMP
  2705. nsStandardURL::GetFile(nsIFile **result)
  2706. {
  2707. NS_PRECONDITION(mSupportsFileURL,
  2708. "GetFile() called on a URL that doesn't support files!");
  2709. nsresult rv = EnsureFile();
  2710. if (NS_FAILED(rv))
  2711. return rv;
  2712. if (LOG_ENABLED()) {
  2713. nsAutoCString path;
  2714. mFile->GetNativePath(path);
  2715. LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
  2716. this, mSpec.get(), path.get()));
  2717. }
  2718. // clone the file, so the caller can modify it.
  2719. // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
  2720. // nsIFile returned from this method; but it seems that some folks do
  2721. // (see bug 161921). until we can be sure that all the consumers are
  2722. // behaving themselves, we'll stay on the safe side and clone the file.
  2723. // see bug 212724 about fixing the consumers.
  2724. return mFile->Clone(result);
  2725. }
  2726. NS_IMETHODIMP
  2727. nsStandardURL::SetFile(nsIFile *file)
  2728. {
  2729. ENSURE_MUTABLE();
  2730. NS_ENSURE_ARG_POINTER(file);
  2731. nsresult rv;
  2732. nsAutoCString url;
  2733. rv = net_GetURLSpecFromFile(file, url);
  2734. if (NS_FAILED(rv)) return rv;
  2735. uint32_t oldURLType = mURLType;
  2736. uint32_t oldDefaultPort = mDefaultPort;
  2737. rv = Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, url, nullptr, nullptr);
  2738. if (NS_FAILED(rv)) {
  2739. // Restore the old url type and default port if the call to Init fails.
  2740. mURLType = oldURLType;
  2741. mDefaultPort = oldDefaultPort;
  2742. return rv;
  2743. }
  2744. // must clone |file| since its value is not guaranteed to remain constant
  2745. InvalidateCache();
  2746. if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
  2747. NS_WARNING("nsIFile::Clone failed");
  2748. // failure to clone is not fatal (GetFile will generate mFile)
  2749. mFile = nullptr;
  2750. }
  2751. return NS_OK;
  2752. }
  2753. //----------------------------------------------------------------------------
  2754. // nsStandardURL::nsIStandardURL
  2755. //----------------------------------------------------------------------------
  2756. inline bool
  2757. IsUTFCharset(const char *aCharset)
  2758. {
  2759. return ((aCharset[0] == 'U' || aCharset[0] == 'u') &&
  2760. (aCharset[1] == 'T' || aCharset[1] == 't') &&
  2761. (aCharset[2] == 'F' || aCharset[2] == 'f'));
  2762. }
  2763. NS_IMETHODIMP
  2764. nsStandardURL::Init(uint32_t urlType,
  2765. int32_t defaultPort,
  2766. const nsACString &spec,
  2767. const char *charset,
  2768. nsIURI *baseURI)
  2769. {
  2770. ENSURE_MUTABLE();
  2771. if (spec.Length() > (uint32_t) net_GetURLMaxLength() ||
  2772. defaultPort > std::numeric_limits<uint16_t>::max()) {
  2773. return NS_ERROR_MALFORMED_URI;
  2774. }
  2775. InvalidateCache();
  2776. switch (urlType) {
  2777. case URLTYPE_STANDARD:
  2778. mParser = net_GetStdURLParser();
  2779. break;
  2780. case URLTYPE_AUTHORITY:
  2781. mParser = net_GetAuthURLParser();
  2782. break;
  2783. case URLTYPE_NO_AUTHORITY:
  2784. mParser = net_GetNoAuthURLParser();
  2785. break;
  2786. default:
  2787. NS_NOTREACHED("bad urlType");
  2788. return NS_ERROR_INVALID_ARG;
  2789. }
  2790. mDefaultPort = defaultPort;
  2791. mURLType = urlType;
  2792. mOriginCharset.Truncate();
  2793. //if charset override is absent, use UTF8 as url encoding
  2794. if (charset != nullptr && *charset != '\0' && !IsUTFCharset(charset)) {
  2795. mOriginCharset = charset;
  2796. }
  2797. if (baseURI && net_IsAbsoluteURL(spec)) {
  2798. baseURI = nullptr;
  2799. }
  2800. if (!baseURI)
  2801. return SetSpec(spec);
  2802. nsAutoCString buf;
  2803. nsresult rv = baseURI->Resolve(spec, buf);
  2804. if (NS_FAILED(rv)) return rv;
  2805. return SetSpec(buf);
  2806. }
  2807. NS_IMETHODIMP
  2808. nsStandardURL::SetDefaultPort(int32_t aNewDefaultPort)
  2809. {
  2810. ENSURE_MUTABLE();
  2811. InvalidateCache();
  2812. // should never be more than 16 bit
  2813. if (aNewDefaultPort >= std::numeric_limits<uint16_t>::max()) {
  2814. return NS_ERROR_MALFORMED_URI;
  2815. }
  2816. // If we're already using the new default-port as a custom port, then clear
  2817. // it off of our mSpec & set mPort to -1, to indicate that we'll be using
  2818. // the default from now on (which happens to match what we already had).
  2819. if (mPort == aNewDefaultPort) {
  2820. ReplacePortInSpec(-1);
  2821. mPort = -1;
  2822. }
  2823. mDefaultPort = aNewDefaultPort;
  2824. return NS_OK;
  2825. }
  2826. NS_IMETHODIMP
  2827. nsStandardURL::GetMutable(bool *value)
  2828. {
  2829. *value = mMutable;
  2830. return NS_OK;
  2831. }
  2832. NS_IMETHODIMP
  2833. nsStandardURL::SetMutable(bool value)
  2834. {
  2835. NS_ENSURE_ARG(mMutable || !value);
  2836. mMutable = value;
  2837. return NS_OK;
  2838. }
  2839. //----------------------------------------------------------------------------
  2840. // nsStandardURL::nsISerializable
  2841. //----------------------------------------------------------------------------
  2842. NS_IMETHODIMP
  2843. nsStandardURL::Read(nsIObjectInputStream *stream)
  2844. {
  2845. NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
  2846. NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
  2847. "Shouldn't have spec encoding here");
  2848. nsresult rv;
  2849. uint32_t urlType;
  2850. rv = stream->Read32(&urlType);
  2851. if (NS_FAILED(rv)) return rv;
  2852. mURLType = urlType;
  2853. switch (mURLType) {
  2854. case URLTYPE_STANDARD:
  2855. mParser = net_GetStdURLParser();
  2856. break;
  2857. case URLTYPE_AUTHORITY:
  2858. mParser = net_GetAuthURLParser();
  2859. break;
  2860. case URLTYPE_NO_AUTHORITY:
  2861. mParser = net_GetNoAuthURLParser();
  2862. break;
  2863. default:
  2864. NS_NOTREACHED("bad urlType");
  2865. return NS_ERROR_FAILURE;
  2866. }
  2867. rv = stream->Read32((uint32_t *) &mPort);
  2868. if (NS_FAILED(rv)) return rv;
  2869. rv = stream->Read32((uint32_t *) &mDefaultPort);
  2870. if (NS_FAILED(rv)) return rv;
  2871. rv = NS_ReadOptionalCString(stream, mSpec);
  2872. if (NS_FAILED(rv)) return rv;
  2873. rv = ReadSegment(stream, mScheme);
  2874. if (NS_FAILED(rv)) return rv;
  2875. rv = ReadSegment(stream, mAuthority);
  2876. if (NS_FAILED(rv)) return rv;
  2877. rv = ReadSegment(stream, mUsername);
  2878. if (NS_FAILED(rv)) return rv;
  2879. rv = ReadSegment(stream, mPassword);
  2880. if (NS_FAILED(rv)) return rv;
  2881. rv = ReadSegment(stream, mHost);
  2882. if (NS_FAILED(rv)) return rv;
  2883. rv = ReadSegment(stream, mPath);
  2884. if (NS_FAILED(rv)) return rv;
  2885. rv = ReadSegment(stream, mFilepath);
  2886. if (NS_FAILED(rv)) return rv;
  2887. rv = ReadSegment(stream, mDirectory);
  2888. if (NS_FAILED(rv)) return rv;
  2889. rv = ReadSegment(stream, mBasename);
  2890. if (NS_FAILED(rv)) return rv;
  2891. rv = ReadSegment(stream, mExtension);
  2892. if (NS_FAILED(rv)) return rv;
  2893. // handle forward compatibility from older serializations that included mParam
  2894. URLSegment old_param;
  2895. rv = ReadSegment(stream, old_param);
  2896. if (NS_FAILED(rv)) return rv;
  2897. rv = ReadSegment(stream, mQuery);
  2898. if (NS_FAILED(rv)) return rv;
  2899. rv = ReadSegment(stream, mRef);
  2900. if (NS_FAILED(rv)) return rv;
  2901. rv = NS_ReadOptionalCString(stream, mOriginCharset);
  2902. if (NS_FAILED(rv)) return rv;
  2903. bool isMutable;
  2904. rv = stream->ReadBoolean(&isMutable);
  2905. if (NS_FAILED(rv)) return rv;
  2906. mMutable = isMutable;
  2907. bool supportsFileURL;
  2908. rv = stream->ReadBoolean(&supportsFileURL);
  2909. if (NS_FAILED(rv)) return rv;
  2910. mSupportsFileURL = supportsFileURL;
  2911. uint32_t hostEncoding;
  2912. rv = stream->Read32(&hostEncoding);
  2913. if (NS_FAILED(rv)) return rv;
  2914. if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) {
  2915. NS_WARNING("Unexpected host encoding");
  2916. return NS_ERROR_UNEXPECTED;
  2917. }
  2918. mHostEncoding = hostEncoding;
  2919. // wait until object is set up, then modify path to include the param
  2920. if (old_param.mLen >= 0) { // note that mLen=0 is ";"
  2921. // If this wasn't empty, it marks characters between the end of the
  2922. // file and start of the query - mPath should include the param,
  2923. // query and ref already. Bump the mFilePath and
  2924. // directory/basename/extension components to include this.
  2925. mFilepath.Merge(mSpec, ';', old_param);
  2926. mDirectory.Merge(mSpec, ';', old_param);
  2927. mBasename.Merge(mSpec, ';', old_param);
  2928. mExtension.Merge(mSpec, ';', old_param);
  2929. }
  2930. return NS_OK;
  2931. }
  2932. NS_IMETHODIMP
  2933. nsStandardURL::Write(nsIObjectOutputStream *stream)
  2934. {
  2935. MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
  2936. "The spec should never be this long, we missed a check.");
  2937. nsresult rv;
  2938. rv = stream->Write32(mURLType);
  2939. if (NS_FAILED(rv)) return rv;
  2940. rv = stream->Write32(uint32_t(mPort));
  2941. if (NS_FAILED(rv)) return rv;
  2942. rv = stream->Write32(uint32_t(mDefaultPort));
  2943. if (NS_FAILED(rv)) return rv;
  2944. rv = NS_WriteOptionalStringZ(stream, mSpec.get());
  2945. if (NS_FAILED(rv)) return rv;
  2946. rv = WriteSegment(stream, mScheme);
  2947. if (NS_FAILED(rv)) return rv;
  2948. rv = WriteSegment(stream, mAuthority);
  2949. if (NS_FAILED(rv)) return rv;
  2950. rv = WriteSegment(stream, mUsername);
  2951. if (NS_FAILED(rv)) return rv;
  2952. rv = WriteSegment(stream, mPassword);
  2953. if (NS_FAILED(rv)) return rv;
  2954. rv = WriteSegment(stream, mHost);
  2955. if (NS_FAILED(rv)) return rv;
  2956. rv = WriteSegment(stream, mPath);
  2957. if (NS_FAILED(rv)) return rv;
  2958. rv = WriteSegment(stream, mFilepath);
  2959. if (NS_FAILED(rv)) return rv;
  2960. rv = WriteSegment(stream, mDirectory);
  2961. if (NS_FAILED(rv)) return rv;
  2962. rv = WriteSegment(stream, mBasename);
  2963. if (NS_FAILED(rv)) return rv;
  2964. rv = WriteSegment(stream, mExtension);
  2965. if (NS_FAILED(rv)) return rv;
  2966. // for backwards compatibility since we removed mParam. Note that this will mean that
  2967. // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they
  2968. // after the removal of special handling). It only matters if you downgrade a browser to before
  2969. // the patch.
  2970. URLSegment empty;
  2971. rv = WriteSegment(stream, empty);
  2972. if (NS_FAILED(rv)) return rv;
  2973. rv = WriteSegment(stream, mQuery);
  2974. if (NS_FAILED(rv)) return rv;
  2975. rv = WriteSegment(stream, mRef);
  2976. if (NS_FAILED(rv)) return rv;
  2977. rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get());
  2978. if (NS_FAILED(rv)) return rv;
  2979. rv = stream->WriteBoolean(mMutable);
  2980. if (NS_FAILED(rv)) return rv;
  2981. rv = stream->WriteBoolean(mSupportsFileURL);
  2982. if (NS_FAILED(rv)) return rv;
  2983. rv = stream->Write32(mHostEncoding);
  2984. if (NS_FAILED(rv)) return rv;
  2985. // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  2986. return NS_OK;
  2987. }
  2988. //---------------------------------------------------------------------------
  2989. // nsStandardURL::nsIIPCSerializableURI
  2990. //---------------------------------------------------------------------------
  2991. inline
  2992. ipc::StandardURLSegment
  2993. ToIPCSegment(const nsStandardURL::URLSegment& aSegment)
  2994. {
  2995. return ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen);
  2996. }
  2997. inline
  2998. MOZ_MUST_USE bool
  2999. FromIPCSegment(const nsACString& aSpec, const ipc::StandardURLSegment& aSegment, nsStandardURL::URLSegment& aTarget)
  3000. {
  3001. // This seems to be just an empty segment.
  3002. if (aSegment.length() == -1) {
  3003. aTarget = nsStandardURL::URLSegment();
  3004. return true;
  3005. }
  3006. // A value of -1 means an empty segment, but < -1 is undefined.
  3007. if (NS_WARN_IF(aSegment.length() < -1)) {
  3008. return false;
  3009. }
  3010. CheckedInt<uint32_t> segmentLen = aSegment.position();
  3011. segmentLen += aSegment.length();
  3012. // Make sure the segment does not extend beyond the spec.
  3013. if (NS_WARN_IF(!segmentLen.isValid() || segmentLen.value() > aSpec.Length())) {
  3014. return false;
  3015. }
  3016. aTarget.mPos = aSegment.position();
  3017. aTarget.mLen = aSegment.length();
  3018. return true;
  3019. }
  3020. void
  3021. nsStandardURL::Serialize(URIParams& aParams)
  3022. {
  3023. MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
  3024. "The spec should never be this long, we missed a check.");
  3025. StandardURLParams params;
  3026. params.urlType() = mURLType;
  3027. params.port() = mPort;
  3028. params.defaultPort() = mDefaultPort;
  3029. params.spec() = mSpec;
  3030. params.scheme() = ToIPCSegment(mScheme);
  3031. params.authority() = ToIPCSegment(mAuthority);
  3032. params.username() = ToIPCSegment(mUsername);
  3033. params.password() = ToIPCSegment(mPassword);
  3034. params.host() = ToIPCSegment(mHost);
  3035. params.path() = ToIPCSegment(mPath);
  3036. params.filePath() = ToIPCSegment(mFilepath);
  3037. params.directory() = ToIPCSegment(mDirectory);
  3038. params.baseName() = ToIPCSegment(mBasename);
  3039. params.extension() = ToIPCSegment(mExtension);
  3040. params.query() = ToIPCSegment(mQuery);
  3041. params.ref() = ToIPCSegment(mRef);
  3042. params.originCharset() = mOriginCharset;
  3043. params.isMutable() = !!mMutable;
  3044. params.supportsFileURL() = !!mSupportsFileURL;
  3045. params.hostEncoding() = mHostEncoding;
  3046. // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  3047. aParams = params;
  3048. }
  3049. bool
  3050. nsStandardURL::Deserialize(const URIParams& aParams)
  3051. {
  3052. NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
  3053. NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
  3054. "Shouldn't have spec encoding here");
  3055. NS_PRECONDITION(!mFile, "Shouldn't have cached file");
  3056. if (aParams.type() != URIParams::TStandardURLParams) {
  3057. NS_ERROR("Received unknown parameters from the other process!");
  3058. return false;
  3059. }
  3060. // If we exit early, make sure to clear the URL so we don't fail the sanity
  3061. // check in the destructor
  3062. auto clearOnExit = MakeScopeExit([&] { Clear(); });
  3063. const StandardURLParams& params = aParams.get_StandardURLParams();
  3064. mURLType = params.urlType();
  3065. switch (mURLType) {
  3066. case URLTYPE_STANDARD:
  3067. mParser = net_GetStdURLParser();
  3068. break;
  3069. case URLTYPE_AUTHORITY:
  3070. mParser = net_GetAuthURLParser();
  3071. break;
  3072. case URLTYPE_NO_AUTHORITY:
  3073. mParser = net_GetNoAuthURLParser();
  3074. break;
  3075. default:
  3076. NS_NOTREACHED("bad urlType");
  3077. return false;
  3078. }
  3079. if (params.hostEncoding() != eEncoding_ASCII &&
  3080. params.hostEncoding() != eEncoding_UTF8) {
  3081. NS_WARNING("Unexpected host encoding");
  3082. return false;
  3083. }
  3084. mPort = params.port();
  3085. mDefaultPort = params.defaultPort();
  3086. mSpec = params.spec();
  3087. NS_ENSURE_TRUE(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), false);
  3088. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.scheme(), mScheme), false);
  3089. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.authority(), mAuthority), false);
  3090. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.username(), mUsername), false);
  3091. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.password(), mPassword), false);
  3092. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.host(), mHost), false);
  3093. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.path(), mPath), false);
  3094. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.filePath(), mFilepath), false);
  3095. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.directory(), mDirectory), false);
  3096. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.baseName(), mBasename), false);
  3097. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.extension(), mExtension), false);
  3098. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.query(), mQuery), false);
  3099. NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.ref(), mRef), false);
  3100. mOriginCharset = params.originCharset();
  3101. mMutable = params.isMutable();
  3102. mSupportsFileURL = params.supportsFileURL();
  3103. mHostEncoding = params.hostEncoding();
  3104. // Some segment sanity checks
  3105. NS_ENSURE_TRUE(mScheme.mPos == 0, false);
  3106. NS_ENSURE_TRUE(mScheme.mLen > 0, false);
  3107. // Make sure scheme is followed by :// (3 characters)
  3108. NS_ENSURE_TRUE(mScheme.mLen < INT32_MAX - 3, false); // avoid overflow
  3109. NS_ENSURE_TRUE(mSpec.Length() >= (uint32_t) mScheme.mLen + 3, false);
  3110. NS_ENSURE_TRUE(nsDependentCSubstring(mSpec, mScheme.mLen, 3).EqualsLiteral("://"), false);
  3111. NS_ENSURE_TRUE(mPath.mLen != -1 && mSpec.CharAt(mPath.mPos) == '/', false);
  3112. NS_ENSURE_TRUE(mPath.mPos == mFilepath.mPos, false);
  3113. NS_ENSURE_TRUE(mQuery.mLen == -1 || mSpec.CharAt(mQuery.mPos - 1) == '?', false);
  3114. NS_ENSURE_TRUE(mRef.mLen == -1 || mSpec.CharAt(mRef.mPos - 1) == '#', false);
  3115. // Sanity-check the result
  3116. if (!IsValid()) {
  3117. return false;
  3118. }
  3119. clearOnExit.release();
  3120. // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  3121. return true;
  3122. }
  3123. //----------------------------------------------------------------------------
  3124. // nsStandardURL::nsIClassInfo
  3125. //----------------------------------------------------------------------------
  3126. NS_IMETHODIMP
  3127. nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array)
  3128. {
  3129. *count = 0;
  3130. *array = nullptr;
  3131. return NS_OK;
  3132. }
  3133. NS_IMETHODIMP
  3134. nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval)
  3135. {
  3136. *_retval = nullptr;
  3137. return NS_OK;
  3138. }
  3139. NS_IMETHODIMP
  3140. nsStandardURL::GetContractID(char * *aContractID)
  3141. {
  3142. *aContractID = nullptr;
  3143. return NS_OK;
  3144. }
  3145. NS_IMETHODIMP
  3146. nsStandardURL::GetClassDescription(char * *aClassDescription)
  3147. {
  3148. *aClassDescription = nullptr;
  3149. return NS_OK;
  3150. }
  3151. NS_IMETHODIMP
  3152. nsStandardURL::GetClassID(nsCID * *aClassID)
  3153. {
  3154. *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID));
  3155. if (!*aClassID)
  3156. return NS_ERROR_OUT_OF_MEMORY;
  3157. return GetClassIDNoAlloc(*aClassID);
  3158. }
  3159. NS_IMETHODIMP
  3160. nsStandardURL::GetFlags(uint32_t *aFlags)
  3161. {
  3162. *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
  3163. return NS_OK;
  3164. }
  3165. NS_IMETHODIMP
  3166. nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
  3167. {
  3168. *aClassIDNoAlloc = kStandardURLCID;
  3169. return NS_OK;
  3170. }
  3171. //----------------------------------------------------------------------------
  3172. // nsStandardURL::nsISizeOf
  3173. //----------------------------------------------------------------------------
  3174. size_t
  3175. nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  3176. {
  3177. return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
  3178. mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
  3179. aMallocSizeOf(mHostA);
  3180. // Measurement of the following members may be added later if DMD finds it is
  3181. // worthwhile:
  3182. // - mParser
  3183. // - mFile
  3184. }
  3185. size_t
  3186. nsStandardURL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
  3187. return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  3188. }
  3189. } // namespace net
  3190. } // namespace mozilla