1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* vim:set ts=4 sw=4 sts=4 et cindent: */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "IPCMessageUtils.h"
- #include "nsStandardURL.h"
- #include "nsCRT.h"
- #include "nsEscape.h"
- #include "nsIFile.h"
- #include "nsIObjectInputStream.h"
- #include "nsIObjectOutputStream.h"
- #include "nsIPrefService.h"
- #include "nsIPrefBranch.h"
- #include "nsIIDNService.h"
- #include "mozilla/Logging.h"
- #include "nsAutoPtr.h"
- #include "nsIURLParser.h"
- #include "nsNetCID.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/ipc/URIUtils.h"
- #include <algorithm>
- #include "mozilla/dom/EncodingUtils.h"
- #include "nsContentUtils.h"
- #include "prprf.h"
- #include "nsReadableUtils.h"
- #include "nsPrintfCString.h"
- using mozilla::dom::EncodingUtils;
- using namespace mozilla::ipc;
- namespace mozilla {
- namespace net {
- static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
- static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
- nsIIDNService *nsStandardURL::gIDN = nullptr;
- bool nsStandardURL::gInitialized = false;
- bool nsStandardURL::gEscapeUTF8 = true;
- bool nsStandardURL::gAlwaysEncodeInUTF8 = true;
- char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
- //
- // setenv MOZ_LOG nsStandardURL:5
- //
- static LazyLogModule gStandardURLLog("nsStandardURL");
- // The Chromium code defines its own LOG macro which we don't want
- #undef LOG
- #define LOG(args) MOZ_LOG(gStandardURLLog, LogLevel::Debug, args)
- #undef LOG_ENABLED
- #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug)
- //----------------------------------------------------------------------------
- #define ENSURE_MUTABLE() \
- PR_BEGIN_MACRO \
- if (!mMutable) { \
- NS_WARNING("attempt to modify an immutable nsStandardURL"); \
- return NS_ERROR_ABORT; \
- } \
- PR_END_MACRO
- //----------------------------------------------------------------------------
- static nsresult
- EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
- {
- nsresult rv;
- int32_t len = str.Length();
- int32_t maxlen;
- rv = encoder->GetMaxLength(str.get(), len, &maxlen);
- if (NS_FAILED(rv))
- return rv;
- char buf[256], *p = buf;
- if (uint32_t(maxlen) > sizeof(buf) - 1) {
- p = (char *) malloc(maxlen + 1);
- if (!p)
- return NS_ERROR_OUT_OF_MEMORY;
- }
- rv = encoder->Convert(str.get(), &len, p, &maxlen);
- if (NS_FAILED(rv))
- goto end;
- if (rv == NS_ERROR_UENC_NOMAPPING) {
- NS_WARNING("unicode conversion failed");
- rv = NS_ERROR_UNEXPECTED;
- goto end;
- }
- p[maxlen] = 0;
- result.Assign(p);
- len = sizeof(buf) - 1;
- rv = encoder->Finish(buf, &len);
- if (NS_FAILED(rv))
- goto end;
- buf[len] = 0;
- result.Append(buf);
- end:
- encoder->Reset();
- if (p != buf)
- free(p);
- return rv;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsPrefObserver
- //----------------------------------------------------------------------------
- #define NS_NET_PREF_ESCAPEUTF8 "network.standard-url.escape-utf8"
- #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
- NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver)
- NS_IMETHODIMP nsStandardURL::
- nsPrefObserver::Observe(nsISupports *subject,
- const char *topic,
- const char16_t *data)
- {
- if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
- nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
- if (prefBranch) {
- PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
- }
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsSegmentEncoder
- //----------------------------------------------------------------------------
- nsStandardURL::
- nsSegmentEncoder::nsSegmentEncoder(const char *charset)
- : mCharset(charset)
- {
- }
- int32_t nsStandardURL::
- nsSegmentEncoder::EncodeSegmentCount(const char *str,
- const URLSegment &seg,
- int16_t mask,
- nsAFlatCString &result,
- bool &appended,
- uint32_t extraLen)
- {
- // extraLen is characters outside the segment that will be
- // added when the segment is not empty (like the @ following
- // a username).
- appended = false;
- if (!str)
- return 0;
- int32_t len = 0;
- if (seg.mLen > 0) {
- uint32_t pos = seg.mPos;
- len = seg.mLen;
- // first honor the origin charset if appropriate. as an optimization,
- // only do this if the segment is non-ASCII. Further, if mCharset is
- // null or the empty string then the origin charset is UTF-8 and there
- // is nothing to do.
- nsAutoCString encBuf;
- if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) {
- // we have to encode this segment
- if (mEncoder || InitUnicodeEncoder()) {
- NS_ConvertUTF8toUTF16 ucsBuf(Substring(str + pos, str + pos + len));
- if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) {
- str = encBuf.get();
- pos = 0;
- len = encBuf.Length();
- }
- // else some failure occurred... assume UTF-8 is ok.
- }
- }
- // escape per RFC2396 unless UTF-8 and allowed by preferences
- int16_t escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII;
- uint32_t initLen = result.Length();
- // now perform any required escaping
- if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) {
- len = result.Length() - initLen;
- appended = true;
- }
- else if (str == encBuf.get()) {
- result += encBuf; // append only!!
- len = encBuf.Length();
- appended = true;
- }
- len += extraLen;
- }
- return len;
- }
- const nsACString &nsStandardURL::
- nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
- int16_t mask,
- nsAFlatCString &result)
- {
- const char *text;
- bool encoded;
- EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
- if (encoded)
- return result;
- return str;
- }
- bool nsStandardURL::
- nsSegmentEncoder::InitUnicodeEncoder()
- {
- NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!");
- // "replacement" won't survive another label resolution
- nsDependentCString label(mCharset);
- if (label.EqualsLiteral("replacement")) {
- mEncoder = EncodingUtils::EncoderForEncoding(label);
- return true;
- }
- nsAutoCString encoding;
- if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
- return false;
- }
- mEncoder = EncodingUtils::EncoderForEncoding(encoding);
- return true;
- }
- #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
- nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get())
- #define GET_SEGMENT_ENCODER(name) \
- GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8)
- #define GET_QUERY_ENCODER(name) \
- GET_SEGMENT_ENCODER_INTERNAL(name, false)
- //----------------------------------------------------------------------------
- // nsStandardURL <public>
- //----------------------------------------------------------------------------
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- static PRCList gAllURLs;
- #endif
- nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
- : mDefaultPort(-1)
- , mPort(-1)
- , mHostA(nullptr)
- , mHostEncoding(eEncoding_ASCII)
- , mSpecEncoding(eEncoding_Unknown)
- , mURLType(URLTYPE_STANDARD)
- , mMutable(true)
- , mSupportsFileURL(aSupportsFileURL)
- {
- LOG(("Creating nsStandardURL @%p\n", this));
- if (!gInitialized) {
- gInitialized = true;
- InitGlobalObjects();
- }
- // default parser in case nsIStandardURL::Init is never called
- mParser = net_GetStdURLParser();
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- memset(&mDebugCList, 0, sizeof(mDebugCList));
- if (NS_IsMainThread()) {
- if (aTrackURL) {
- PR_APPEND_LINK(&mDebugCList, &gAllURLs);
- } else {
- PR_INIT_CLIST(&mDebugCList);
- }
- }
- #endif
- }
- nsStandardURL::~nsStandardURL()
- {
- LOG(("Destroying nsStandardURL @%p\n", this));
- if (mHostA) {
- free(mHostA);
- }
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- if (NS_IsMainThread()) {
- if (!PR_CLIST_IS_EMPTY(&mDebugCList)) {
- PR_REMOVE_LINK(&mDebugCList);
- }
- }
- #endif
- }
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- struct DumpLeakedURLs {
- DumpLeakedURLs() {}
- ~DumpLeakedURLs();
- };
- DumpLeakedURLs::~DumpLeakedURLs()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!PR_CLIST_IS_EMPTY(&gAllURLs)) {
- printf("Leaked URLs:\n");
- for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) {
- nsStandardURL *url = reinterpret_cast<nsStandardURL*>(reinterpret_cast<char*>(l) - offsetof(nsStandardURL, mDebugCList));
- url->PrintSpec();
- }
- }
- }
- #endif
- bool nsStandardURL::IsValid() {
- auto checkSegment = [&](const nsStandardURL::URLSegment& aSeg) {
- // Bad value
- if (NS_WARN_IF(aSeg.mLen < -1)) {
- return false;
- }
- if (aSeg.mLen == -1) {
- return true;
- }
- // Position outside of string
- if (NS_WARN_IF(aSeg.mPos + aSeg.mLen > mSpec.Length())) {
- return false;
- }
- // Overflow
- if (NS_WARN_IF(aSeg.mPos + aSeg.mLen < aSeg.mPos)) {
- return false;
- }
- return true;
- };
- bool allSegmentsValid = checkSegment(mScheme) && checkSegment(mAuthority) &&
- checkSegment(mUsername) && checkSegment(mPassword) &&
- checkSegment(mHost) && checkSegment(mPath) &&
- checkSegment(mFilepath) && checkSegment(mDirectory) &&
- checkSegment(mBasename) && checkSegment(mExtension) &&
- checkSegment(mQuery) && checkSegment(mRef);
- if (!allSegmentsValid) {
- return false;
- }
- if (mScheme.mPos != 0) {
- return false;
- }
- return true;
- }
- void
- nsStandardURL::InitGlobalObjects()
- {
- nsCOMPtr<nsIPrefBranch> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
- if (prefBranch) {
- nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
- prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false);
- prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false);
- PrefsChanged(prefBranch, nullptr);
- }
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- PR_INIT_CLIST(&gAllURLs);
- #endif
- }
- void
- nsStandardURL::ShutdownGlobalObjects()
- {
- NS_IF_RELEASE(gIDN);
- #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
- if (gInitialized) {
- // This instanciates a dummy class, and will trigger the class
- // destructor when libxul is unloaded. This is equivalent to atexit(),
- // but gracefully handles dlclose().
- static DumpLeakedURLs d;
- }
- #endif
- }
- //----------------------------------------------------------------------------
- // nsStandardURL <private>
- //----------------------------------------------------------------------------
- void
- nsStandardURL::Clear()
- {
- mSpec.Truncate();
- mPort = -1;
- mScheme.Reset();
- mAuthority.Reset();
- mUsername.Reset();
- mPassword.Reset();
- mHost.Reset();
- mHostEncoding = eEncoding_ASCII;
- mPath.Reset();
- mFilepath.Reset();
- mDirectory.Reset();
- mBasename.Reset();
- mExtension.Reset();
- mQuery.Reset();
- mRef.Reset();
- InvalidateCache();
- }
- void
- nsStandardURL::InvalidateCache(bool invalidateCachedFile)
- {
- if (invalidateCachedFile)
- mFile = nullptr;
- if (mHostA) {
- free(mHostA);
- mHostA = nullptr;
- }
- mSpecEncoding = eEncoding_Unknown;
- }
- // |base| should be 8, 10 or 16. Not check the precondition for performance.
- /* static */ inline bool
- nsStandardURL::IsValidOfBase(unsigned char c, const uint32_t base) {
- MOZ_ASSERT(base == 8 || base == 10 || base == 16, "invalid base");
- if ('0' <= c && c <= '7') {
- return true;
- } else if (c == '8' || c== '9') {
- return base != 8;
- } else if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) {
- return base == 16;
- }
- return false;
- }
- /* static */ inline nsresult
- nsStandardURL::ParseIPv4Number(nsCString &input, uint32_t &number)
- {
- if (input.Length() == 0) {
- return NS_ERROR_FAILURE;
- }
- uint32_t base;
- uint32_t prefixLength = 0;
- if (input[0] == '0') {
- if (input.Length() == 1) {
- base = 10;
- } else if (input[1] == 'x' || input[1] == 'X') {
- base = 16;
- prefixLength = 2;
- } else {
- base = 8;
- prefixLength = 1;
- }
- } else {
- base = 10;
- }
- if (prefixLength == input.Length()) {
- return NS_ERROR_FAILURE;
- }
- // ignore leading zeros to calculate the valid length of number
- while (prefixLength < input.Length() && input[prefixLength] == '0') {
- prefixLength++;
- }
- // all zero case
- if (prefixLength == input.Length()) {
- number = 0;
- return NS_OK;
- }
- // overflow case
- if (input.Length() - prefixLength > 16) {
- return NS_ERROR_FAILURE;
- }
- for (uint32_t i = prefixLength; i < input.Length(); ++i) {
- if (!IsValidOfBase(input[i], base)) {
- return NS_ERROR_FAILURE;
- }
- }
- const char* fmt = "";
- switch (base) {
- case 8:
- fmt = "%llo";
- break;
- case 10:
- fmt = "%lli";
- break;
- case 16:
- fmt = "%llx";
- break;
- default:
- return NS_ERROR_FAILURE;
- }
- uint64_t number64;
- if (PR_sscanf(input.get(), fmt, &number64) == 1 &&
- number64 <= 0xffffffffu) {
- number = number64;
- return NS_OK;
- }
- return NS_ERROR_FAILURE;
- }
- // IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser
- /* static */ nsresult
- nsStandardURL::NormalizeIPv4(const nsCSubstring &host, nsCString &result)
- {
- if (host.Length() == 0 ||
- host[0] < '0' || '9' < host[0] || // bail-out fast
- FindInReadable(NS_LITERAL_CSTRING(".."), host)) {
- return NS_ERROR_FAILURE;
- }
- nsTArray<nsCString> parts;
- if (!ParseString(host, '.', parts) ||
- parts.Length() == 0 ||
- parts.Length() > 4) {
- return NS_ERROR_FAILURE;
- }
- uint32_t n = 0;
- nsTArray<int32_t> numbers;
- for (uint32_t i = 0; i < parts.Length(); ++i) {
- if (NS_FAILED(ParseIPv4Number(parts[i], n))) {
- return NS_ERROR_FAILURE;
- }
- numbers.AppendElement(n);
- }
- uint32_t ipv4 = numbers.LastElement();
- static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu,
- 0xffffu, 0xffu};
- if (ipv4 > upperBounds[numbers.Length() - 1]) {
- return NS_ERROR_FAILURE;
- }
- for (uint32_t i = 0; i < numbers.Length() - 1; ++i) {
- if (numbers[i] > 255) {
- return NS_ERROR_FAILURE;
- }
- ipv4 += numbers[i] << (8 * (3 - i));
- }
- uint8_t ipSegments[4];
- NetworkEndian::writeUint32(ipSegments, ipv4);
- result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1],
- ipSegments[2], ipSegments[3]);
- return NS_OK;
- }
- /**
- * Returns |true| if |aString| contains only ASCII characters according
- * to our CRT.
- *
- * @param aString an 8-bit wide string to scan
- */
- inline bool IsAsciiString(mozilla::Span<const char> aString) {
- for (char c : aString) {
- if (!nsCRT::IsAscii(c)) {
- return false;
- }
- }
- return true;
- }
- nsresult
- nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result)
- {
- nsresult rv = NS_ERROR_UNEXPECTED;
- // Clear result even if we bail.
- result.Truncate();
-
- if (!gIDN) {
- nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
- if (serv) {
- NS_ADDREF(gIDN = serv.get());
- }
- }
- if (!gIDN) {
- return NS_ERROR_UNEXPECTED;
- }
- NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding");
- bool isASCII;
- nsAutoCString normalized;
- // If the input is ASCII, and not ACE encoded, then there's no processing
- // needed. This is needed because we want to allow ascii labels longer than
- // 64 characters for some schemes.
- bool isACE = false;
- if (IsAsciiString(host) && NS_SUCCEEDED(gIDN->IsACE(host, &isACE)) && !isACE) {
- result = host;
- return NS_OK;
- }
- // If the input is an ACE encoded string it MUST be ASCII or it's malformed
- // according to the spec.
- if (!IsAsciiString(host) && isACE) {
- return NS_ERROR_MALFORMED_URI;
- }
- // Even if it's already ACE, we must still call ConvertUTF8toACE in order
- // for the input normalization to take place.
- rv = gIDN->ConvertUTF8toACE(host, normalized);
- if (NS_FAILED(rv)) {
- return rv;
- }
- // If the ASCII representation doesn't contain the xn-- token then we don't
- // need to call ConvertToDisplayIDN as that would not change anything.
- if (!StringBeginsWith(normalized, NS_LITERAL_CSTRING("xn--")) &&
- normalized.Find(NS_LITERAL_CSTRING(".xn--")) == kNotFound) {
- return NS_OK;
- }
-
- // Finally, convert to IDN
- rv = gIDN->ConvertToDisplayIDN(normalized, &isASCII, result);
- if (NS_SUCCEEDED(rv) && !isASCII) {
- mHostEncoding = eEncoding_UTF8;
- }
- return rv;
- }
- bool
- nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length)
- {
- if (!host || !*host) {
- // Should not be NULL or empty string
- return false;
- }
- if (length != strlen(host)) {
- // Embedded null
- return false;
- }
- bool openBracket = host[0] == '[';
- bool closeBracket = host[length - 1] == ']';
- if (openBracket && closeBracket) {
- return net_IsValidIPv6Addr(host + 1, length - 2);
- }
- if (openBracket || closeBracket) {
- // Fail if only one of the brackets is present
- return false;
- }
- const char *end = host + length;
- if (end != net_FindCharInSet(host, end, CONTROL_CHARACTERS " #/:?@[\\]*<>|\"^")) {
- // We still allow % because it is in the ID of addons.
- // Any percent encoded ASCII characters that are not allowed in the
- // hostname are not percent decoded, and will be parsed just fine.
- return false;
- }
- return true;
- }
- void
- nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
- {
- net_CoalesceDirs(coalesceFlag, path);
- int32_t newLen = strlen(path);
- if (newLen < mPath.mLen) {
- int32_t diff = newLen - mPath.mLen;
- mPath.mLen = newLen;
- mDirectory.mLen += diff;
- mFilepath.mLen += diff;
- ShiftFromBasename(diff);
- }
- }
- uint32_t
- nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str,
- const URLSegment &segInput, URLSegment &segOutput,
- const nsCString *escapedStr,
- bool useEscaped, int32_t *diff)
- {
- MOZ_ASSERT(segInput.mLen == segOutput.mLen);
- if (diff) *diff = 0;
- if (segInput.mLen > 0) {
- if (useEscaped) {
- MOZ_ASSERT(diff);
- segOutput.mLen = escapedStr->Length();
- *diff = segOutput.mLen - segInput.mLen;
- memcpy(buf + i, escapedStr->get(), segOutput.mLen);
- } else {
- memcpy(buf + i, str + segInput.mPos, segInput.mLen);
- }
- segOutput.mPos = i;
- i += segOutput.mLen;
- } else {
- segOutput.mPos = i;
- }
- return i;
- }
- uint32_t
- nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len)
- {
- memcpy(buf + i, str, len);
- return i + len;
- }
- // basic algorithm:
- // 1- escape url segments (for improved GetSpec efficiency)
- // 2- allocate spec buffer
- // 3- write url segments
- // 4- update url segment positions and lengths
- nsresult
- nsStandardURL::BuildNormalizedSpec(const char *spec)
- {
- // Assumptions: all member URLSegments must be relative the |spec| argument
- // passed to this function.
- // buffers for holding escaped url segments (these will remain empty unless
- // escaping is required).
- nsAutoCString encUsername, encPassword, encHost, encDirectory,
- encBasename, encExtension, encQuery, encRef;
- bool useEncUsername, useEncPassword, useEncHost = false,
- useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef;
- nsAutoCString portbuf;
- //
- // escape each URL segment, if necessary, and calculate approximate normalized
- // spec length.
- //
- // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
- uint32_t approxLen = 0;
- // the scheme is already ASCII
- if (mScheme.mLen > 0)
- approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
- // encode URL segments; convert UTF-8 to origin charset and possibly escape.
- // results written to encXXX variables only if |spec| is not already in the
- // appropriate encoding.
- {
- GET_SEGMENT_ENCODER(encoder);
- GET_QUERY_ENCODER(queryEncoder);
- // Items using an extraLen of 1 don't add anything unless mLen > 0
- // Username@
- approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1);
- // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
- if (mPassword.mLen >= 0)
- approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword);
- // mHost is handled differently below due to encoding differences
- MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
- if (mPort != -1 && mPort != mDefaultPort)
- {
- // :port
- portbuf.AppendInt(mPort);
- approxLen += portbuf.Length() + 1;
- }
- approxLen += 1; // reserve space for possible leading '/' - may not be needed
- // Should just use mPath? These are pessimistic, and thus waste space
- approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1);
- approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename);
- approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
- // These next ones *always* add their leading character even if length is 0
- // Handles items like "http://#"
- // ?query
- if (mQuery.mLen >= 0)
- approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery);
- // #ref
- if (mRef.mLen >= 0) {
- if (nsContentUtils::EncodeDecodeURLHash()) {
- approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref,
- encRef, useEncRef);
- } else {
- approxLen += 1 + mRef.mLen;
- useEncRef = false;
- }
- }
- }
- // do not escape the hostname, if IPv6 address literal, mHost will
- // already point to a [ ] delimited IPv6 address literal.
- // However, perform Unicode normalization on it, as IDN does.
- mHostEncoding = eEncoding_ASCII;
- // Note that we don't disallow URLs without a host - file:, etc
- if (mHost.mLen > 0) {
- nsAutoCString tempHost;
- NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost);
- if (tempHost.Contains('\0'))
- return NS_ERROR_MALFORMED_URI; // null embedded in hostname
- if (tempHost.Contains(' '))
- return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname
- nsresult rv = NormalizeIDN(tempHost, encHost);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!SegmentIs(spec, mScheme, "resource") &&
- !SegmentIs(spec, mScheme, "chrome")) {
- nsAutoCString ipString;
- if (NS_SUCCEEDED(NormalizeIPv4(encHost, ipString))) {
- encHost = ipString;
- }
- }
- // NormalizeIDN always copies, if the call was successful.
- useEncHost = true;
- approxLen += encHost.Length();
- if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) {
- return NS_ERROR_MALFORMED_URI;
- }
- }
- // We must take a copy of every single segment because they are pointing to
- // the |spec| while we are changing their value, in case we must use
- // encoded strings.
- URLSegment username(mUsername);
- URLSegment password(mPassword);
- URLSegment host(mHost);
- URLSegment path(mPath);
- URLSegment filepath(mFilepath);
- URLSegment directory(mDirectory);
- URLSegment basename(mBasename);
- URLSegment extension(mExtension);
- URLSegment query(mQuery);
- URLSegment ref(mRef);
- //
- // generate the normalized URL string
- //
- // approxLen should be correct or 1 high
- if (!mSpec.SetLength(approxLen+1, fallible)) // buf needs a trailing '\0' below
- return NS_ERROR_OUT_OF_MEMORY;
- char *buf;
- mSpec.BeginWriting(buf);
- uint32_t i = 0;
- int32_t diff = 0;
- if (mScheme.mLen > 0) {
- i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
- net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
- i = AppendToBuf(buf, i, "://", 3);
- }
- // record authority starting position
- mAuthority.mPos = i;
- // append authority
- if (mUsername.mLen > 0) {
- i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
- &encUsername, useEncUsername, &diff);
- ShiftFromPassword(diff);
- if (password.mLen > 0) {
- buf[i++] = ':';
- i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
- &encPassword, useEncPassword, &diff);
- ShiftFromHost(diff);
- } else {
- mPassword.mLen = -1;
- }
- buf[i++] = '@';
- }
- if (host.mLen > 0) {
- i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
- &diff);
- ShiftFromPath(diff);
- net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
- MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
- if (mPort != -1 && mPort != mDefaultPort) {
- buf[i++] = ':';
- // Already formatted while building approxLen
- i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
- }
- }
- // record authority length
- mAuthority.mLen = i - mAuthority.mPos;
- // path must always start with a "/"
- if (mPath.mLen <= 0) {
- LOG(("setting path=/"));
- mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
- mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
- // basename must exist, even if empty (bug 113508)
- mBasename.mPos = i+1;
- mBasename.mLen = 0;
- buf[i++] = '/';
- }
- else {
- uint32_t leadingSlash = 0;
- if (spec[path.mPos] != '/') {
- LOG(("adding leading slash to path\n"));
- leadingSlash = 1;
- buf[i++] = '/';
- // basename must exist, even if empty (bugs 113508, 429347)
- if (mBasename.mLen == -1) {
- mBasename.mPos = basename.mPos = i;
- mBasename.mLen = basename.mLen = 0;
- }
- }
- // record corrected (file)path starting position
- mPath.mPos = mFilepath.mPos = i - leadingSlash;
- i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory,
- &encDirectory, useEncDirectory, &diff);
- ShiftFromBasename(diff);
- // the directory must end with a '/'
- if (buf[i-1] != '/') {
- buf[i++] = '/';
- mDirectory.mLen++;
- }
- i = AppendSegmentToBuf(buf, i, spec, basename, mBasename,
- &encBasename, useEncBasename, &diff);
- ShiftFromExtension(diff);
- // make corrections to directory segment if leadingSlash
- if (leadingSlash) {
- mDirectory.mPos = mPath.mPos;
- if (mDirectory.mLen >= 0)
- mDirectory.mLen += leadingSlash;
- else
- mDirectory.mLen = 1;
- }
- if (mExtension.mLen >= 0) {
- buf[i++] = '.';
- i = AppendSegmentToBuf(buf, i, spec, extension, mExtension,
- &encExtension, useEncExtension, &diff);
- ShiftFromQuery(diff);
- }
- // calculate corrected filepath length
- mFilepath.mLen = i - mFilepath.mPos;
- if (mQuery.mLen >= 0) {
- buf[i++] = '?';
- i = AppendSegmentToBuf(buf, i, spec, query, mQuery,
- &encQuery, useEncQuery,
- &diff);
- ShiftFromRef(diff);
- }
- if (mRef.mLen >= 0) {
- buf[i++] = '#';
- i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
- &diff);
- }
- // calculate corrected path length
- mPath.mLen = i - mPath.mPos;
- }
- buf[i] = '\0';
- if (mDirectory.mLen > 1) {
- netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
- if (SegmentIs(buf,mScheme,"ftp")) {
- coalesceFlag = (netCoalesceFlags) (coalesceFlag
- | NET_COALESCE_ALLOW_RELATIVE_ROOT
- | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
- }
- CoalescePath(coalesceFlag, buf + mDirectory.mPos);
- }
- mSpec.SetLength(strlen(buf));
- NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
- return NS_OK;
- }
- bool
- nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
- {
- // one or both may be null
- if (!val || mSpec.IsEmpty())
- return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
- if (seg.mLen < 0)
- return false;
- // if the first |seg.mLen| chars of |val| match, then |val| must
- // also be null terminated at |seg.mLen|.
- if (ignoreCase)
- return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
- && (val[seg.mLen] == '\0');
- else
- return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
- && (val[seg.mLen] == '\0');
- }
- bool
- nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase)
- {
- // one or both may be null
- if (!val || !spec)
- return (!val && (!spec || seg.mLen < 0));
- if (seg.mLen < 0)
- return false;
- // if the first |seg.mLen| chars of |val| match, then |val| must
- // also be null terminated at |seg.mLen|.
- if (ignoreCase)
- return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
- && (val[seg.mLen] == '\0');
- else
- return !strncmp(spec + seg.mPos, val, seg.mLen)
- && (val[seg.mLen] == '\0');
- }
- bool
- nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase)
- {
- if (seg1.mLen != seg2.mLen)
- return false;
- if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
- return true; // both are empty
- if (!val)
- return false;
- if (ignoreCase)
- return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
- else
- return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
- }
- int32_t
- nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen)
- {
- if (val && valLen) {
- if (len == 0)
- mSpec.Insert(val, pos, valLen);
- else
- mSpec.Replace(pos, len, nsDependentCString(val, valLen));
- return valLen - len;
- }
- // else remove the specified segment
- mSpec.Cut(pos, len);
- return -int32_t(len);
- }
- int32_t
- nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val)
- {
- if (len == 0)
- mSpec.Insert(val, pos);
- else
- mSpec.Replace(pos, len, val);
- return val.Length() - len;
- }
- nsresult
- nsStandardURL::ParseURL(const char *spec, int32_t specLen)
- {
- nsresult rv;
- if (specLen > net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- //
- // parse given URL string
- //
- rv = mParser->ParseURL(spec, specLen,
- &mScheme.mPos, &mScheme.mLen,
- &mAuthority.mPos, &mAuthority.mLen,
- &mPath.mPos, &mPath.mLen);
- if (NS_FAILED(rv)) return rv;
- #ifdef DEBUG
- if (mScheme.mLen <= 0) {
- printf("spec=%s\n", spec);
- NS_WARNING("malformed url: no scheme");
- }
- #endif
-
- if (mAuthority.mLen > 0) {
- rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
- &mUsername.mPos, &mUsername.mLen,
- &mPassword.mPos, &mPassword.mLen,
- &mHost.mPos, &mHost.mLen,
- &mPort);
- if (NS_FAILED(rv)) return rv;
- // Don't allow mPort to be set to this URI's default port
- if (mPort == mDefaultPort)
- mPort = -1;
- mUsername.mPos += mAuthority.mPos;
- mPassword.mPos += mAuthority.mPos;
- mHost.mPos += mAuthority.mPos;
- }
- if (mPath.mLen > 0)
- rv = ParsePath(spec, mPath.mPos, mPath.mLen);
- return rv;
- }
- nsresult
- nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
- {
- LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
- if (pathLen > net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
- &mFilepath.mPos, &mFilepath.mLen,
- &mQuery.mPos, &mQuery.mLen,
- &mRef.mPos, &mRef.mLen);
- if (NS_FAILED(rv)) return rv;
- mFilepath.mPos += pathPos;
- mQuery.mPos += pathPos;
- mRef.mPos += pathPos;
- if (mFilepath.mLen > 0) {
- rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
- &mDirectory.mPos, &mDirectory.mLen,
- &mBasename.mPos, &mBasename.mLen,
- &mExtension.mPos, &mExtension.mLen);
- if (NS_FAILED(rv)) return rv;
- mDirectory.mPos += mFilepath.mPos;
- mBasename.mPos += mFilepath.mPos;
- mExtension.mPos += mFilepath.mPos;
- }
- return NS_OK;
- }
- char *
- nsStandardURL::AppendToSubstring(uint32_t pos,
- int32_t len,
- const char *tail)
- {
- // Verify pos and length are within boundaries
- if (pos > mSpec.Length())
- return nullptr;
- if (len < 0)
- return nullptr;
- if ((uint32_t)len > (mSpec.Length() - pos))
- return nullptr;
- if (!tail)
- return nullptr;
- uint32_t tailLen = strlen(tail);
- // Check for int overflow for proposed length of combined string
- if (UINT32_MAX - ((uint32_t)len + 1) < tailLen)
- return nullptr;
- char *result = (char *) moz_xmalloc(len + tailLen + 1);
- if (result) {
- memcpy(result, mSpec.get() + pos, len);
- memcpy(result + len, tail, tailLen);
- result[len + tailLen] = '\0';
- }
- return result;
- }
- nsresult
- nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
- {
- nsresult rv;
- rv = stream->Read32(&seg.mPos);
- if (NS_FAILED(rv)) return rv;
- rv = stream->Read32((uint32_t *) &seg.mLen);
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- nsresult
- nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
- {
- nsresult rv;
- rv = stream->Write32(seg.mPos);
- if (NS_FAILED(rv)) return rv;
- rv = stream->Write32(uint32_t(seg.mLen));
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- /* static */ void
- nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
- {
- bool val;
- LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref));
- #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p))
- #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
- if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) {
- if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val))
- gEscapeUTF8 = val;
- LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled"));
- }
- if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) {
- if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val))
- gAlwaysEncodeInUTF8 = val;
- LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled"));
- }
- #undef PREF_CHANGED
- #undef GOT_PREF
- }
- #define SHIFT_FROM(name, what) \
- void \
- nsStandardURL::name(int32_t diff) \
- { \
- if (!diff) return; \
- if (what.mLen >= 0) { \
- CheckedInt<int32_t> pos = what.mPos; \
- pos += diff; \
- MOZ_ASSERT(pos.isValid()); \
- what.mPos = pos.value(); \
- }
- #define SHIFT_FROM_NEXT(name, what, next) \
- SHIFT_FROM(name, what) \
- next(diff); \
- }
- #define SHIFT_FROM_LAST(name, what) \
- SHIFT_FROM(name, what) \
- }
- SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername)
- SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword)
- SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost)
- SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath)
- SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath)
- SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory)
- SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename)
- SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension)
- SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery)
- SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef)
- SHIFT_FROM_LAST(ShiftFromRef, mRef)
- //----------------------------------------------------------------------------
- // nsStandardURL::nsISupports
- //----------------------------------------------------------------------------
- NS_IMPL_ADDREF(nsStandardURL)
- NS_IMPL_RELEASE(nsStandardURL)
- NS_INTERFACE_MAP_BEGIN(nsStandardURL)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
- NS_INTERFACE_MAP_ENTRY(nsIURI)
- NS_INTERFACE_MAP_ENTRY(nsIURL)
- NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
- NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
- NS_INTERFACE_MAP_ENTRY(nsISerializable)
- NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
- NS_INTERFACE_MAP_ENTRY(nsIMutable)
- NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
- NS_INTERFACE_MAP_ENTRY(nsISensitiveInfoHiddenURI)
- // see nsStandardURL::Equals
- if (aIID.Equals(kThisImplCID))
- foundInterface = static_cast<nsIURI *>(this);
- else
- NS_INTERFACE_MAP_ENTRY(nsISizeOf)
- NS_INTERFACE_MAP_END
- //----------------------------------------------------------------------------
- // nsStandardURL::nsIURI
- //----------------------------------------------------------------------------
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetSpec(nsACString &result)
- {
- MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
- "The spec should never be this long, we missed a check.");
- result = mSpec;
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
- {
- result = mSpec;
- if (mPassword.mLen >= 0) {
- result.Replace(mPassword.mPos, mPassword.mLen, "****");
- }
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetSpecIgnoringRef(nsACString &result)
- {
- // URI without ref is 0 to one char before ref
- if (mRef.mLen >= 0) {
- URLSegment noRef(0, mRef.mPos - 1);
- result = Segment(noRef);
- } else {
- result = mSpec;
- }
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetPrePath(nsACString &result)
- {
- result = Prepath();
- return NS_OK;
- }
- // result is strictly US-ASCII
- NS_IMETHODIMP
- nsStandardURL::GetScheme(nsACString &result)
- {
- result = Scheme();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetUserPass(nsACString &result)
- {
- result = Userpass();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetUsername(nsACString &result)
- {
- result = Username();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetPassword(nsACString &result)
- {
- result = Password();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetHostPort(nsACString &result)
- {
- result = Hostport();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetHost(nsACString &result)
- {
- result = Host();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetPort(int32_t *result)
- {
- // should never be more than 16 bit
- MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
- *result = mPort;
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetPath(nsACString &result)
- {
- result = Path();
- return NS_OK;
- }
- // result is ASCII
- NS_IMETHODIMP
- nsStandardURL::GetAsciiSpec(nsACString &result)
- {
- if (mSpecEncoding == eEncoding_Unknown) {
- if (IsASCII(mSpec))
- mSpecEncoding = eEncoding_ASCII;
- else
- mSpecEncoding = eEncoding_UTF8;
- }
- if (mSpecEncoding == eEncoding_ASCII) {
- result = mSpec;
- return NS_OK;
- }
- // try to guess the capacity required for result...
- result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10));
- result = Substring(mSpec, 0, mScheme.mLen + 3);
- // This is left fallible as this entire function is expected to be
- // infallible.
- NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result);
- // get the hostport
- nsAutoCString hostport;
- MOZ_ALWAYS_SUCCEEDS(GetAsciiHostPort(hostport));
- result += hostport;
- // This is left fallible as this entire function is expected to be
- // infallible.
- NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
- return NS_OK;
- }
- // result is ASCII
- NS_IMETHODIMP
- nsStandardURL::GetAsciiHostPort(nsACString &result)
- {
- if (mHostEncoding == eEncoding_ASCII) {
- result = Hostport();
- return NS_OK;
- }
- MOZ_ALWAYS_SUCCEEDS(GetAsciiHost(result));
- // As our mHostEncoding is not eEncoding_ASCII, we know that
- // the our host is not ipv6, and we can avoid looking at it.
- MOZ_ASSERT(result.FindChar(':') == -1, "The host must not be ipv6");
- // hostport = "hostA" + ":port"
- uint32_t pos = mHost.mPos + mHost.mLen;
- if (pos < mPath.mPos)
- result += Substring(mSpec, pos, mPath.mPos - pos);
- return NS_OK;
- }
- // result is ASCII
- NS_IMETHODIMP
- nsStandardURL::GetAsciiHost(nsACString &result)
- {
- if (mHostEncoding == eEncoding_ASCII) {
- result = Host();
- return NS_OK;
- }
- // perhaps we have it cached...
- if (mHostA) {
- result = mHostA;
- return NS_OK;
- }
- if (gIDN) {
- nsresult rv;
- rv = gIDN->ConvertUTF8toACE(Host(), result);
- if (NS_SUCCEEDED(rv)) {
- mHostA = ToNewCString(result);
- return NS_OK;
- }
- NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
- }
- // something went wrong... guess all we can do is URL escape :-/
- NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetOriginCharset(nsACString &result)
- {
- if (mOriginCharset.IsEmpty())
- result.AssignLiteral("UTF-8");
- else
- result = mOriginCharset;
- return NS_OK;
- }
- static bool
- IsSpecialProtocol(const nsACString &input)
- {
- nsACString::const_iterator start, end;
- input.BeginReading(start);
- nsACString::const_iterator iterator(start);
- input.EndReading(end);
- while (iterator != end && *iterator != ':') {
- iterator++;
- }
- nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get()));
- return protocol.LowerCaseEqualsLiteral("http") ||
- protocol.LowerCaseEqualsLiteral("https") ||
- protocol.LowerCaseEqualsLiteral("ftp") ||
- protocol.LowerCaseEqualsLiteral("ws") ||
- protocol.LowerCaseEqualsLiteral("wss") ||
- protocol.LowerCaseEqualsLiteral("file") ||
- protocol.LowerCaseEqualsLiteral("gopher");
- }
- NS_IMETHODIMP
- nsStandardURL::SetSpec(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &flat = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetSpec [spec=%s]\n", flat.get()));
- if (input.Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- // filter out unexpected chars "\r\n\t" if necessary
- nsAutoCString filteredURI;
- net_FilterURIString(flat, filteredURI);
- if (filteredURI.Length() == 0) {
- return NS_ERROR_MALFORMED_URI;
- }
- // Make a backup of the curent URL
- nsStandardURL prevURL(false,false);
- prevURL.CopyMembers(this, eHonorRef, EmptyCString());
- Clear();
- if (IsSpecialProtocol(filteredURI)) {
- // Bug 652186: Replace all backslashes with slashes when parsing paths
- // Stop when we reach the query or the hash.
- nsAutoCString::iterator start;
- nsAutoCString::iterator end;
- filteredURI.BeginWriting(start);
- filteredURI.EndWriting(end);
- while (start != end) {
- if (*start == '?' || *start == '#') {
- break;
- }
- if (*start == '\\') {
- *start = '/';
- }
- start++;
- }
- }
- const char *spec = filteredURI.get();
- int32_t specLength = filteredURI.Length();
- // parse the given URL...
- nsresult rv = ParseURL(spec, specLength);
- if (NS_SUCCEEDED(rv)) {
- // finally, use the URLSegment member variables to build a normalized
- // copy of |spec|
- rv = BuildNormalizedSpec(spec);
- }
- // Make sure that a URLTYPE_AUTHORITY has a non-empty hostname.
- if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
- rv = NS_ERROR_MALFORMED_URI;
- }
- if (NS_FAILED(rv)) {
- Clear();
- // If parsing the spec has failed, restore the old URL
- // so we don't end up with an empty URL.
- CopyMembers(&prevURL, eHonorRef, EmptyCString());
- return rv;
- }
- if (LOG_ENABLED()) {
- LOG((" spec = %s\n", mSpec.get()));
- LOG((" port = %d\n", mPort));
- LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen));
- LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
- LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen));
- LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen));
- LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen));
- LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen));
- LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen));
- LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
- LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen));
- LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
- LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen));
- LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen));
- }
- return rv;
- }
- NS_IMETHODIMP
- nsStandardURL::SetScheme(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
- if (scheme.IsEmpty()) {
- NS_WARNING("cannot remove the scheme from an url");
- return NS_ERROR_UNEXPECTED;
- }
- if (mScheme.mLen < 0) {
- NS_WARNING("uninitialized");
- return NS_ERROR_NOT_INITIALIZED;
- }
- if (!net_IsValidScheme(scheme)) {
- NS_WARNING("the given url scheme contains invalid characters");
- return NS_ERROR_UNEXPECTED;
- }
- if (mSpec.Length() + input.Length() - Scheme().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
- if (shift) {
- mScheme.mLen = scheme.Length();
- ShiftFromAuthority(shift);
- }
- // ensure new scheme is lowercase
- //
- // XXX the string code unfortunately doesn't provide a ToLowerCase
- // that operates on a substring.
- net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetUserPass(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
- if (mURLType == URLTYPE_NO_AUTHORITY) {
- if (userpass.IsEmpty())
- return NS_OK;
- NS_WARNING("cannot set user:pass on no-auth url");
- return NS_ERROR_UNEXPECTED;
- }
- if (mAuthority.mLen < 0) {
- NS_WARNING("uninitialized");
- return NS_ERROR_NOT_INITIALIZED;
- }
- if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- if (userpass.IsEmpty()) {
- // remove user:pass
- if (mUsername.mLen > 0) {
- if (mPassword.mLen > 0)
- mUsername.mLen += (mPassword.mLen + 1);
- mUsername.mLen++;
- mSpec.Cut(mUsername.mPos, mUsername.mLen);
- mAuthority.mLen -= mUsername.mLen;
- ShiftFromHost(-mUsername.mLen);
- mUsername.mLen = -1;
- mPassword.mLen = -1;
- }
- return NS_OK;
- }
- NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
- nsresult rv;
- uint32_t usernamePos, passwordPos;
- int32_t usernameLen, passwordLen;
- rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
- &usernamePos, &usernameLen,
- &passwordPos, &passwordLen);
- if (NS_FAILED(rv)) return rv;
- // build new user:pass in |buf|
- nsAutoCString buf;
- if (usernameLen > 0) {
- GET_SEGMENT_ENCODER(encoder);
- bool ignoredOut;
- usernameLen = encoder.EncodeSegmentCount(userpass.get(),
- URLSegment(usernamePos,
- usernameLen),
- esc_Username | esc_AlwaysCopy,
- buf, ignoredOut);
- if (passwordLen > 0) {
- buf.Append(':');
- passwordLen = encoder.EncodeSegmentCount(userpass.get(),
- URLSegment(passwordPos,
- passwordLen),
- esc_Password |
- esc_AlwaysCopy, buf,
- ignoredOut);
- } else {
- passwordLen = -1;
- }
- if (mUsername.mLen < 0)
- buf.Append('@');
- }
- uint32_t shift = 0;
- if (mUsername.mLen < 0) {
- // no existing user:pass
- if (!buf.IsEmpty()) {
- mSpec.Insert(buf, mHost.mPos);
- mUsername.mPos = mHost.mPos;
- shift = buf.Length();
- }
- }
- else {
- // replace existing user:pass
- uint32_t userpassLen = mUsername.mLen;
- if (mPassword.mLen >= 0)
- userpassLen += (mPassword.mLen + 1);
- mSpec.Replace(mUsername.mPos, userpassLen, buf);
- shift = buf.Length() - userpassLen;
- }
- if (shift) {
- ShiftFromHost(shift);
- mAuthority.mLen += shift;
- }
- // update positions and lengths
- mUsername.mLen = usernameLen;
- mPassword.mLen = passwordLen;
- if (passwordLen > 0) {
- mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetUsername(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &username = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
- if (mURLType == URLTYPE_NO_AUTHORITY) {
- if (username.IsEmpty())
- return NS_OK;
- NS_WARNING("cannot set username on no-auth url");
- return NS_ERROR_UNEXPECTED;
- }
- if (username.IsEmpty())
- return SetUserPass(username);
- if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- // escape username if necessary
- nsAutoCString buf;
- GET_SEGMENT_ENCODER(encoder);
- const nsACString &escUsername =
- encoder.EncodeSegment(username, esc_Username, buf);
- int32_t shift;
- if (mUsername.mLen < 0) {
- mUsername.mPos = mAuthority.mPos;
- mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
- shift = escUsername.Length() + 1;
- }
- else
- shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
- if (shift) {
- mUsername.mLen = escUsername.Length();
- mAuthority.mLen += shift;
- ShiftFromPassword(shift);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetPassword(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &password = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
- if (mURLType == URLTYPE_NO_AUTHORITY) {
- if (password.IsEmpty())
- return NS_OK;
- NS_WARNING("cannot set password on no-auth url");
- return NS_ERROR_UNEXPECTED;
- }
- if (mUsername.mLen <= 0) {
- NS_WARNING("cannot set password without existing username");
- return NS_ERROR_FAILURE;
- }
- if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- if (password.IsEmpty()) {
- if (mPassword.mLen >= 0) {
- // cut(":password")
- mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
- ShiftFromHost(-(mPassword.mLen + 1));
- mAuthority.mLen -= (mPassword.mLen + 1);
- mPassword.mLen = -1;
- }
- return NS_OK;
- }
- // escape password if necessary
- nsAutoCString buf;
- GET_SEGMENT_ENCODER(encoder);
- const nsACString &escPassword =
- encoder.EncodeSegment(password, esc_Password, buf);
- int32_t shift;
- if (mPassword.mLen < 0) {
- mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
- mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
- shift = escPassword.Length() + 1;
- }
- else
- shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
- if (shift) {
- mPassword.mLen = escPassword.Length();
- mAuthority.mLen += shift;
- ShiftFromHost(shift);
- }
- return NS_OK;
- }
- void
- nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
- nsACString::const_iterator& aEnd)
- {
- for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
- nsACString::const_iterator c(aStart);
- if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
- aEnd = c;
- }
- }
- }
- // If aValue only has a host part and no port number, the port
- // will not be reset!!!
- NS_IMETHODIMP
- nsStandardURL::SetHostPort(const nsACString &aValue)
- {
- ENSURE_MUTABLE();
- // We cannot simply call nsIURI::SetHost because that would treat the name as
- // an IPv6 address (like http:://[server:443]/). We also cannot call
- // nsIURI::SetHostPort because that isn't implemented. Sadfaces.
- nsACString::const_iterator start, end;
- aValue.BeginReading(start);
- aValue.EndReading(end);
- nsACString::const_iterator iter(start);
- bool isIPv6 = false;
- FindHostLimit(start, end);
- if (*start == '[') { // IPv6 address
- if (!FindCharInReadable(']', iter, end)) {
- // the ] character is missing
- return NS_ERROR_MALFORMED_URI;
- }
- // iter now at the ']' character
- isIPv6 = true;
- } else {
- nsACString::const_iterator iter2(start);
- if (FindCharInReadable(']', iter2, end)) {
- // if the first char isn't [ then there should be no ] character
- return NS_ERROR_MALFORMED_URI;
- }
- }
- FindCharInReadable(':', iter, end);
- if (!isIPv6 && iter != end) {
- nsACString::const_iterator iter2(iter);
- iter2++; // Skip over the first ':' character
- if (FindCharInReadable(':', iter2, end)) {
- // If there is more than one ':' character it suggests an IPv6
- // The format should be [2001::1]:80 where the port is optional
- return NS_ERROR_MALFORMED_URI;
- }
- }
- nsresult rv = SetHost(Substring(start, iter));
- NS_ENSURE_SUCCESS(rv, rv);
- // Also set the port if needed.
- if (iter != end) {
- iter++;
- if (iter != end) {
- nsCString portStr(Substring(iter, end));
- nsresult rv;
- int32_t port = portStr.ToInteger(&rv);
- if (NS_SUCCEEDED(rv)) {
- rv = SetPort(port);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- // Failure parsing port number
- return NS_ERROR_MALFORMED_URI;
- }
- } else {
- // port number is missing
- return NS_ERROR_MALFORMED_URI;
- }
- }
- return NS_OK;
- }
- // This function is different than SetHostPort in that the port number will be
- // reset as well if aValue parameter does not contain a port port number.
- NS_IMETHODIMP
- nsStandardURL::SetHostAndPort(const nsACString &aValue)
- {
- // Reset the port and than call SetHostPort. SetHostPort does not reset
- // the port number.
- nsresult rv = SetPort(-1);
- NS_ENSURE_SUCCESS(rv, rv);
- return SetHostPort(aValue);
- }
- NS_IMETHODIMP
- nsStandardURL::SetHost(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &hostname = PromiseFlatCString(input);
- nsACString::const_iterator start, end;
- hostname.BeginReading(start);
- hostname.EndReading(end);
- FindHostLimit(start, end);
- const nsCString unescapedHost(Substring(start, end));
- // Do percent decoding on the the input.
- nsAutoCString flat;
- NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(),
- esc_AlwaysCopy | esc_Host, flat);
- const char *host = flat.get();
- LOG(("nsStandardURL::SetHost [host=%s]\n", host));
- if (mURLType == URLTYPE_NO_AUTHORITY) {
- if (flat.IsEmpty())
- return NS_OK;
- NS_WARNING("cannot set host on no-auth url");
- return NS_ERROR_UNEXPECTED;
- } else {
- if (flat.IsEmpty()) {
- // Setting an empty hostname is not allowed for
- // URLTYPE_STANDARD and URLTYPE_AUTHORITY.
- return NS_ERROR_UNEXPECTED;
- }
- }
- if (strlen(host) < flat.Length())
- return NS_ERROR_MALFORMED_URI; // found embedded null
- // For consistency with SetSpec/nsURLParsers, don't allow spaces
- // in the hostname.
- if (strchr(host, ' '))
- return NS_ERROR_MALFORMED_URI;
- if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- mHostEncoding = eEncoding_ASCII;
- uint32_t len;
- nsAutoCString hostBuf;
- nsresult rv = NormalizeIDN(flat, hostBuf);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) {
- nsAutoCString ipString;
- if (NS_SUCCEEDED(NormalizeIPv4(hostBuf, ipString))) {
- hostBuf = ipString;
- }
- }
- // NormalizeIDN always copies if the call was successful
- host = hostBuf.get();
- len = hostBuf.Length();
- if (!ValidIPv6orHostname(host, len)) {
- return NS_ERROR_MALFORMED_URI;
- }
- if (mHost.mLen < 0) {
- int port_length = 0;
- if (mPort != -1) {
- nsAutoCString buf;
- buf.Assign(':');
- buf.AppendInt(mPort);
- port_length = buf.Length();
- }
- if (mAuthority.mLen > 0) {
- mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
- mHost.mLen = 0;
- } else if (mScheme.mLen > 0) {
- mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
- mHost.mLen = 0;
- }
- }
- int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
- if (shift) {
- mHost.mLen = len;
- mAuthority.mLen += shift;
- ShiftFromPath(shift);
- }
- // Now canonicalize the host to lowercase
- net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetPort(int32_t port)
- {
- ENSURE_MUTABLE();
- LOG(("nsStandardURL::SetPort [port=%d]\n", port));
- if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
- return NS_OK;
- // ports must be >= 0 and 16 bit
- // -1 == use default
- if (port < -1 || port > std::numeric_limits<uint16_t>::max())
- return NS_ERROR_MALFORMED_URI;
- if (mURLType == URLTYPE_NO_AUTHORITY) {
- NS_WARNING("cannot set port on no-auth url");
- return NS_ERROR_UNEXPECTED;
- }
- InvalidateCache();
- if (port == mDefaultPort) {
- port = -1;
- }
- ReplacePortInSpec(port);
- mPort = port;
- return NS_OK;
- }
- /**
- * Replaces the existing port in mSpec with aNewPort.
- *
- * The caller is responsible for:
- * - Calling InvalidateCache (since our mSpec is changing).
- * - Checking whether aNewPort is mDefaultPort (in which case the
- * caller should pass aNewPort=-1).
- */
- void
- nsStandardURL::ReplacePortInSpec(int32_t aNewPort)
- {
- MOZ_ASSERT(mMutable, "Caller should ensure we're mutable");
- NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1,
- "Caller should check its passed-in value and pass -1 instead of "
- "mDefaultPort, to avoid encoding default port into mSpec");
- // Create the (possibly empty) string that we're planning to replace:
- nsAutoCString buf;
- if (mPort != -1) {
- buf.Assign(':');
- buf.AppendInt(mPort);
- }
- // Find the position & length of that string:
- const uint32_t replacedLen = buf.Length();
- const uint32_t replacedStart =
- mAuthority.mPos + mAuthority.mLen - replacedLen;
- // Create the (possibly empty) replacement string:
- if (aNewPort == -1) {
- buf.Truncate();
- } else {
- buf.Assign(':');
- buf.AppendInt(aNewPort);
- }
- // Perform the replacement:
- mSpec.Replace(replacedStart, replacedLen, buf);
- // Bookkeeping to reflect the new length:
- int32_t shift = buf.Length() - replacedLen;
- mAuthority.mLen += shift;
- ShiftFromPath(shift);
- }
- NS_IMETHODIMP
- nsStandardURL::SetPath(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &path = PromiseFlatCString(input);
- LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
- InvalidateCache();
- if (!path.IsEmpty()) {
- nsAutoCString spec;
- spec.Assign(mSpec.get(), mPath.mPos);
- if (path.First() != '/')
- spec.Append('/');
- spec.Append(path);
- return SetSpec(spec);
- }
- else if (mPath.mLen >= 1) {
- mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
- // these contain only a '/'
- mPath.mLen = 1;
- mDirectory.mLen = 1;
- mFilepath.mLen = 1;
- // these are no longer defined
- mBasename.mLen = -1;
- mExtension.mLen = -1;
- mQuery.mLen = -1;
- mRef.mLen = -1;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::Equals(nsIURI *unknownOther, bool *result)
- {
- return EqualsInternal(unknownOther, eHonorRef, result);
- }
- NS_IMETHODIMP
- nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result)
- {
- return EqualsInternal(unknownOther, eIgnoreRef, result);
- }
- nsresult
- nsStandardURL::EqualsInternal(nsIURI *unknownOther,
- nsStandardURL::RefHandlingEnum refHandlingMode,
- bool *result)
- {
- NS_ENSURE_ARG_POINTER(unknownOther);
- NS_PRECONDITION(result, "null pointer");
- RefPtr<nsStandardURL> other;
- nsresult rv = unknownOther->QueryInterface(kThisImplCID,
- getter_AddRefs(other));
- if (NS_FAILED(rv)) {
- *result = false;
- return NS_OK;
- }
- // First, check whether one URIs is an nsIFileURL while the other
- // is not. If that's the case, they're different.
- if (mSupportsFileURL != other->mSupportsFileURL) {
- *result = false;
- return NS_OK;
- }
- // Next check parts of a URI that, if different, automatically make the
- // URIs different
- if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
- // Check for host manually, since conversion to file will
- // ignore the host!
- !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
- !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
- !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
- !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
- Port() != other->Port()) {
- // No need to compare files or other URI parts -- these are different
- // beasties
- *result = false;
- return NS_OK;
- }
- if (refHandlingMode == eHonorRef &&
- !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
- *result = false;
- return NS_OK;
- }
-
- // Then check for exact identity of URIs. If we have it, they're equal
- if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
- SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
- SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
- *result = true;
- return NS_OK;
- }
- // At this point, the URIs are not identical, but they only differ in the
- // directory/filename/extension. If these are file URLs, then get the
- // corresponding file objects and compare those, since two filenames that
- // differ, eg, only in case could still be equal.
- if (mSupportsFileURL) {
- // Assume not equal for failure cases... but failures in GetFile are
- // really failures, more or less, so propagate them to caller.
- *result = false;
- rv = EnsureFile();
- nsresult rv2 = other->EnsureFile();
- // special case for resource:// urls that don't resolve to files
- if (rv == NS_ERROR_NO_INTERFACE && rv == rv2)
- return NS_OK;
-
- if (NS_FAILED(rv)) {
- LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
- this, mSpec.get()));
- return rv;
- }
- NS_ASSERTION(mFile, "EnsureFile() lied!");
- rv = rv2;
- if (NS_FAILED(rv)) {
- LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
- other.get(), other->mSpec.get()));
- return rv;
- }
- NS_ASSERTION(other->mFile, "EnsureFile() lied!");
- return mFile->Equals(other->mFile, result);
- }
- // The URLs are not identical, and they do not correspond to the
- // same file, so they are different.
- *result = false;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SchemeIs(const char *scheme, bool *result)
- {
- NS_PRECONDITION(result, "null pointer");
- *result = SegmentIs(mScheme, scheme);
- return NS_OK;
- }
- /* virtual */ nsStandardURL*
- nsStandardURL::StartClone()
- {
- nsStandardURL *clone = new nsStandardURL();
- return clone;
- }
- NS_IMETHODIMP
- nsStandardURL::Clone(nsIURI **result)
- {
- return CloneInternal(eHonorRef, EmptyCString(), result);
- }
- NS_IMETHODIMP
- nsStandardURL::CloneIgnoringRef(nsIURI **result)
- {
- return CloneInternal(eIgnoreRef, EmptyCString(), result);
- }
- NS_IMETHODIMP
- nsStandardURL::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
- {
- return CloneInternal(eReplaceRef, newRef, result);
- }
- nsresult
- nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
- const nsACString& newRef,
- nsIURI **result)
- {
- RefPtr<nsStandardURL> clone = StartClone();
- if (!clone)
- return NS_ERROR_OUT_OF_MEMORY;
- // Copy local members into clone.
- // Also copies the cached members mFile, mHostA
- clone->CopyMembers(this, refHandlingMode, newRef, true);
- clone.forget(result);
- return NS_OK;
- }
- nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
- nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
- bool copyCached)
- {
- mSpec = source->mSpec;
- mDefaultPort = source->mDefaultPort;
- mPort = source->mPort;
- mScheme = source->mScheme;
- mAuthority = source->mAuthority;
- mUsername = source->mUsername;
- mPassword = source->mPassword;
- mHost = source->mHost;
- mPath = source->mPath;
- mFilepath = source->mFilepath;
- mDirectory = source->mDirectory;
- mBasename = source->mBasename;
- mExtension = source->mExtension;
- mQuery = source->mQuery;
- mRef = source->mRef;
- mOriginCharset = source->mOriginCharset;
- mURLType = source->mURLType;
- mParser = source->mParser;
- mMutable = true;
- mSupportsFileURL = source->mSupportsFileURL;
- mHostEncoding = source->mHostEncoding;
- if (copyCached) {
- mFile = source->mFile;
- mHostA = source->mHostA ? strdup(source->mHostA) : nullptr;
- mSpecEncoding = source->mSpecEncoding;
- } else {
- // The same state as after calling InvalidateCache()
- mFile = nullptr;
- mHostA = nullptr;
- mSpecEncoding = eEncoding_Unknown;
- }
- if (refHandlingMode == eIgnoreRef) {
- SetRef(EmptyCString());
- } else if (refHandlingMode == eReplaceRef) {
- SetRef(newRef);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::Resolve(const nsACString &in, nsACString &out)
- {
- const nsPromiseFlatCString &flat = PromiseFlatCString(in);
- // filter out unexpected chars "\r\n\t" if necessary
- nsAutoCString buf;
- net_FilterURIString(flat, buf);
- const char *relpath = buf.get();
- int32_t relpathLen = buf.Length();
- char *result = nullptr;
- LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
- this, mSpec.get(), relpath));
- NS_ASSERTION(mParser, "no parser: unitialized");
- // NOTE: there is no need for this function to produce normalized
- // output. normalization will occur when the result is used to
- // initialize a nsStandardURL object.
- if (mScheme.mLen < 0) {
- NS_WARNING("unable to Resolve URL: this URL not initialized");
- return NS_ERROR_NOT_INITIALIZED;
- }
- nsresult rv;
- URLSegment scheme;
- char *resultPath = nullptr;
- bool relative = false;
- uint32_t offset = 0;
- netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
- // relative urls should never contain a host, so we always want to use
- // the noauth url parser.
- // use it to extract a possible scheme
- rv = mParser->ParseURL(relpath,
- relpathLen,
- &scheme.mPos, &scheme.mLen,
- nullptr, nullptr,
- nullptr, nullptr);
- // if the parser fails (for example because there is no valid scheme)
- // reset the scheme and assume a relative url
- if (NS_FAILED(rv)) scheme.Reset();
- nsAutoCString protocol(Segment(scheme));
- nsAutoCString baseProtocol(Scheme());
- // We need to do backslash replacement for the following cases:
- // 1. The input is an absolute path with a http/https/ftp scheme
- // 2. The input is a relative path, and the base URL has a http/https/ftp scheme
- if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) ||
- IsSpecialProtocol(protocol)) {
- nsAutoCString::iterator start;
- nsAutoCString::iterator end;
- buf.BeginWriting(start);
- buf.EndWriting(end);
- while (start != end) {
- if (*start == '?' || *start == '#') {
- break;
- }
- if (*start == '\\') {
- *start = '/';
- }
- start++;
- }
- }
- if (scheme.mLen >= 0) {
- // add some flags to coalesceFlag if it is an ftp-url
- // need this later on when coalescing the resulting URL
- if (SegmentIs(relpath, scheme, "ftp", true)) {
- coalesceFlag = (netCoalesceFlags) (coalesceFlag
- | NET_COALESCE_ALLOW_RELATIVE_ROOT
- | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
- }
- // this URL appears to be absolute
- // but try to find out more
- if (SegmentIs(mScheme, relpath, scheme, true)) {
- // mScheme and Scheme are the same
- // but this can still be relative
- if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen,
- "://",3) == 0) {
- // now this is really absolute
- // because a :// follows the scheme
- result = NS_strdup(relpath);
- } else {
- // This is a deprecated form of relative urls like
- // http:file or http:/path/file
- // we will support it for now ...
- relative = true;
- offset = scheme.mLen + 1;
- }
- } else {
- // the schemes are not the same, we are also done
- // because we have to assume this is absolute
- result = NS_strdup(relpath);
- }
- } else {
- // add some flags to coalesceFlag if it is an ftp-url
- // need this later on when coalescing the resulting URL
- if (SegmentIs(mScheme,"ftp")) {
- coalesceFlag = (netCoalesceFlags) (coalesceFlag
- | NET_COALESCE_ALLOW_RELATIVE_ROOT
- | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
- }
- if (relpath[0] == '/' && relpath[1] == '/') {
- // this URL //host/path is almost absolute
- result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
- } else {
- // then it must be relative
- relative = true;
- }
- }
- if (relative) {
- uint32_t len = 0;
- const char *realrelpath = relpath + offset;
- switch (*realrelpath) {
- case '/':
- // overwrite everything after the authority
- len = mAuthority.mPos + mAuthority.mLen;
- break;
- case '?':
- // overwrite the existing ?query and #ref
- if (mQuery.mLen >= 0)
- len = mQuery.mPos - 1;
- else if (mRef.mLen >= 0)
- len = mRef.mPos - 1;
- else
- len = mPath.mPos + mPath.mLen;
- break;
- case '#':
- case '\0':
- // overwrite the existing #ref
- if (mRef.mLen < 0)
- len = mPath.mPos + mPath.mLen;
- else
- len = mRef.mPos - 1;
- break;
- default:
- if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
- if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
- nsCaseInsensitiveCStringComparator())) {
- // if ftp URL ends with %2F then simply
- // append relative part because %2F also
- // marks the root directory with ftp-urls
- len = mFilepath.mPos + mFilepath.mLen;
- } else {
- // overwrite everything after the directory
- len = mDirectory.mPos + mDirectory.mLen;
- }
- } else {
- // overwrite everything after the directory
- len = mDirectory.mPos + mDirectory.mLen;
- }
- }
- result = AppendToSubstring(0, len, realrelpath);
- // locate result path
- resultPath = result + mPath.mPos;
- }
- if (!result)
- return NS_ERROR_OUT_OF_MEMORY;
- if (resultPath)
- net_CoalesceDirs(coalesceFlag, resultPath);
- else {
- // locate result path
- resultPath = PL_strstr(result, "://");
- if (resultPath) {
- resultPath = PL_strchr(resultPath + 3, '/');
- if (resultPath)
- net_CoalesceDirs(coalesceFlag,resultPath);
- }
- }
- out.Adopt(result);
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
- {
- NS_ENSURE_ARG_POINTER(uri2);
- // if uri's are equal, then return uri as is
- bool isEquals = false;
- if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
- return GetSpec(aResult);
- aResult.Truncate();
- // check pre-path; if they don't match, then return empty string
- nsStandardURL *stdurl2;
- nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
- isEquals = NS_SUCCEEDED(rv)
- && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
- && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
- && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
- && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
- && (Port() == stdurl2->Port());
- if (!isEquals)
- {
- if (NS_SUCCEEDED(rv))
- NS_RELEASE(stdurl2);
- return NS_OK;
- }
- // scan for first mismatched character
- const char *thisIndex, *thatIndex, *startCharPos;
- startCharPos = mSpec.get() + mDirectory.mPos;
- thisIndex = startCharPos;
- thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
- while ((*thisIndex == *thatIndex) && *thisIndex)
- {
- thisIndex++;
- thatIndex++;
- }
- // backup to just after previous slash so we grab an appropriate path
- // segment such as a directory (not partial segments)
- // todo: also check for file matches which include '?' and '#'
- while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/'))
- thisIndex--;
- // grab spec from beginning to thisIndex
- aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
- NS_RELEASE(stdurl2);
- return rv;
- }
- NS_IMETHODIMP
- nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
- {
- NS_ENSURE_ARG_POINTER(uri2);
- aResult.Truncate();
- // if uri's are equal, then return empty string
- bool isEquals = false;
- if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
- return NS_OK;
- nsStandardURL *stdurl2;
- nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
- isEquals = NS_SUCCEEDED(rv)
- && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
- && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
- && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
- && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
- && (Port() == stdurl2->Port());
- if (!isEquals)
- {
- if (NS_SUCCEEDED(rv))
- NS_RELEASE(stdurl2);
- return uri2->GetSpec(aResult);
- }
- // scan for first mismatched character
- const char *thisIndex, *thatIndex, *startCharPos;
- startCharPos = mSpec.get() + mDirectory.mPos;
- thisIndex = startCharPos;
- thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
- #ifdef XP_WIN
- bool isFileScheme = SegmentIs(mScheme, "file");
- if (isFileScheme)
- {
- // on windows, we need to match the first segment of the path
- // if these don't match then we need to return an absolute path
- // skip over any leading '/' in path
- while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
- {
- thisIndex++;
- thatIndex++;
- }
- // look for end of first segment
- while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
- {
- thisIndex++;
- thatIndex++;
- }
- // if we didn't match through the first segment, return absolute path
- if ((*thisIndex != '/') || (*thatIndex != '/'))
- {
- NS_RELEASE(stdurl2);
- return uri2->GetSpec(aResult);
- }
- }
- #endif
- while ((*thisIndex == *thatIndex) && *thisIndex)
- {
- thisIndex++;
- thatIndex++;
- }
- // backup to just after previous slash so we grab an appropriate path
- // segment such as a directory (not partial segments)
- // todo: also check for file matches with '#' and '?'
- while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
- thatIndex--;
- const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen;
- // need to account for slashes and add corresponding "../"
- for (; thisIndex <= limit && *thisIndex; ++thisIndex)
- {
- if (*thisIndex == '/')
- aResult.AppendLiteral("../");
- }
- // grab spec from thisIndex to end
- uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
- aResult.Append(Substring(stdurl2->mSpec, startPos,
- stdurl2->mSpec.Length() - startPos));
- NS_RELEASE(stdurl2);
- return rv;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsIURL
- //----------------------------------------------------------------------------
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetFilePath(nsACString &result)
- {
- result = Filepath();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetQuery(nsACString &result)
- {
- result = Query();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetRef(nsACString &result)
- {
- result = Ref();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetHasRef(bool *result)
- {
- *result = (mRef.mLen >= 0);
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetDirectory(nsACString &result)
- {
- result = Directory();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetFileName(nsACString &result)
- {
- result = Filename();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetFileBaseName(nsACString &result)
- {
- result = Basename();
- return NS_OK;
- }
- // result may contain unescaped UTF-8 characters
- NS_IMETHODIMP
- nsStandardURL::GetFileExtension(nsACString &result)
- {
- result = Extension();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetFilePath(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &flat = PromiseFlatCString(input);
- const char *filepath = flat.get();
- LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
- // if there isn't a filepath, then there can't be anything
- // after the path either. this url is likely uninitialized.
- if (mFilepath.mLen < 0)
- return SetPath(flat);
- if (filepath && *filepath) {
- nsAutoCString spec;
- uint32_t dirPos, basePos, extPos;
- int32_t dirLen, baseLen, extLen;
- nsresult rv;
- rv = mParser->ParseFilePath(filepath, flat.Length(),
- &dirPos, &dirLen,
- &basePos, &baseLen,
- &extPos, &extLen);
- if (NS_FAILED(rv)) return rv;
- // build up new candidate spec
- spec.Assign(mSpec.get(), mPath.mPos);
- // ensure leading '/'
- if (filepath[dirPos] != '/')
- spec.Append('/');
- GET_SEGMENT_ENCODER(encoder);
- // append encoded filepath components
- if (dirLen > 0)
- encoder.EncodeSegment(Substring(filepath + dirPos,
- filepath + dirPos + dirLen),
- esc_Directory | esc_AlwaysCopy, spec);
- if (baseLen > 0)
- encoder.EncodeSegment(Substring(filepath + basePos,
- filepath + basePos + baseLen),
- esc_FileBaseName | esc_AlwaysCopy, spec);
- if (extLen >= 0) {
- spec.Append('.');
- if (extLen > 0)
- encoder.EncodeSegment(Substring(filepath + extPos,
- filepath + extPos + extLen),
- esc_FileExtension | esc_AlwaysCopy,
- spec);
- }
- // compute the ending position of the current filepath
- if (mFilepath.mLen >= 0) {
- uint32_t end = mFilepath.mPos + mFilepath.mLen;
- if (mSpec.Length() > end)
- spec.Append(mSpec.get() + end, mSpec.Length() - end);
- }
- return SetSpec(spec);
- }
- if (mPath.mLen > 1) {
- mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
- // left shift query, and ref
- ShiftFromQuery(1 - mFilepath.mLen);
- // One character for '/', and if we have a query or ref we add their
- // length and one extra for each '?' or '#' characters
- mPath.mLen = 1 + (mQuery.mLen >= 0 ? (mQuery.mLen + 1) : 0) +
- (mRef.mLen >= 0 ? (mRef.mLen + 1) : 0);
- // these contain only a '/'
- mDirectory.mLen = 1;
- mFilepath.mLen = 1;
- // these are no longer defined
- mBasename.mLen = -1;
- mExtension.mLen = -1;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetQuery(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &flat = PromiseFlatCString(input);
- const char *query = flat.get();
- LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
- if (mPath.mLen < 0)
- return SetPath(flat);
- if (mSpec.Length() + input.Length() - Query().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- if (!query || !*query) {
- // remove existing query
- if (mQuery.mLen >= 0) {
- // remove query and leading '?'
- mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
- ShiftFromRef(-(mQuery.mLen + 1));
- mPath.mLen -= (mQuery.mLen + 1);
- mQuery.mPos = 0;
- mQuery.mLen = -1;
- }
- return NS_OK;
- }
- int32_t queryLen = flat.Length();
- if (query[0] == '?') {
- query++;
- queryLen--;
- }
- if (mQuery.mLen < 0) {
- if (mRef.mLen < 0)
- mQuery.mPos = mSpec.Length();
- else
- mQuery.mPos = mRef.mPos - 1;
- mSpec.Insert('?', mQuery.mPos);
- mQuery.mPos++;
- mQuery.mLen = 0;
- // the insertion pushes these out by 1
- mPath.mLen++;
- mRef.mPos++;
- }
- // encode query if necessary
- nsAutoCString buf;
- bool encoded;
- GET_QUERY_ENCODER(encoder);
- encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
- buf, encoded);
- if (encoded) {
- query = buf.get();
- queryLen = buf.Length();
- }
- int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
- if (shift) {
- mQuery.mLen = queryLen;
- mPath.mLen += shift;
- ShiftFromRef(shift);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetRef(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &flat = PromiseFlatCString(input);
- const char *ref = flat.get();
- LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
- if (mPath.mLen < 0)
- return SetPath(flat);
- if (mSpec.Length() + input.Length() - Ref().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- if (!ref || !*ref) {
- // remove existing ref
- if (mRef.mLen >= 0) {
- // remove ref and leading '#'
- mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
- mPath.mLen -= (mRef.mLen + 1);
- mRef.mPos = 0;
- mRef.mLen = -1;
- }
- return NS_OK;
- }
- int32_t refLen = flat.Length();
- if (ref[0] == '#') {
- ref++;
- refLen--;
- }
- if (mRef.mLen < 0) {
- mSpec.Append('#');
- ++mPath.mLen; // Include the # in the path.
- mRef.mPos = mSpec.Length();
- mRef.mLen = 0;
- }
- // If precent encoding is necessary, `ref` will point to `buf`'s content.
- // `buf` needs to outlive any use of the `ref` pointer.
- nsAutoCString buf;
- if (nsContentUtils::EncodeDecodeURLHash()) {
- // encode ref if necessary
- bool encoded;
- GET_SEGMENT_ENCODER(encoder);
- encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
- buf, encoded);
- if (encoded) {
- ref = buf.get();
- refLen = buf.Length();
- }
- }
- int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
- mPath.mLen += shift;
- mRef.mLen = refLen;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetDirectory(const nsACString &input)
- {
- NS_NOTYETIMPLEMENTED("");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- nsStandardURL::SetFileName(const nsACString &input)
- {
- ENSURE_MUTABLE();
- const nsPromiseFlatCString &flat = PromiseFlatCString(input);
- const char *filename = flat.get();
- LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
- if (mPath.mLen < 0)
- return SetPath(flat);
- if (mSpec.Length() + input.Length() - Filename().Length() > (uint32_t) net_GetURLMaxLength()) {
- return NS_ERROR_MALFORMED_URI;
- }
- int32_t shift = 0;
- if (!(filename && *filename)) {
- // remove the filename
- if (mBasename.mLen > 0) {
- if (mExtension.mLen >= 0)
- mBasename.mLen += (mExtension.mLen + 1);
- mSpec.Cut(mBasename.mPos, mBasename.mLen);
- shift = -mBasename.mLen;
- mBasename.mLen = 0;
- mExtension.mLen = -1;
- }
- }
- else {
- nsresult rv;
- URLSegment basename, extension;
- // let the parser locate the basename and extension
- rv = mParser->ParseFileName(filename, flat.Length(),
- &basename.mPos, &basename.mLen,
- &extension.mPos, &extension.mLen);
- if (NS_FAILED(rv)) return rv;
- if (basename.mLen < 0) {
- // remove existing filename
- if (mBasename.mLen >= 0) {
- uint32_t len = mBasename.mLen;
- if (mExtension.mLen >= 0)
- len += (mExtension.mLen + 1);
- mSpec.Cut(mBasename.mPos, len);
- shift = -int32_t(len);
- mBasename.mLen = 0;
- mExtension.mLen = -1;
- }
- }
- else {
- nsAutoCString newFilename;
- bool ignoredOut;
- GET_SEGMENT_ENCODER(encoder);
- basename.mLen = encoder.EncodeSegmentCount(filename, basename,
- esc_FileBaseName |
- esc_AlwaysCopy,
- newFilename,
- ignoredOut);
- if (extension.mLen >= 0) {
- newFilename.Append('.');
- extension.mLen = encoder.EncodeSegmentCount(filename, extension,
- esc_FileExtension |
- esc_AlwaysCopy,
- newFilename,
- ignoredOut);
- }
- if (mBasename.mLen < 0) {
- // insert new filename
- mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
- mSpec.Insert(newFilename, mBasename.mPos);
- shift = newFilename.Length();
- }
- else {
- // replace existing filename
- uint32_t oldLen = uint32_t(mBasename.mLen);
- if (mExtension.mLen >= 0)
- oldLen += (mExtension.mLen + 1);
- mSpec.Replace(mBasename.mPos, oldLen, newFilename);
- shift = newFilename.Length() - oldLen;
- }
-
- mBasename.mLen = basename.mLen;
- mExtension.mLen = extension.mLen;
- if (mExtension.mLen >= 0)
- mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
- }
- }
- if (shift) {
- ShiftFromQuery(shift);
- mFilepath.mLen += shift;
- mPath.mLen += shift;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetFileBaseName(const nsACString &input)
- {
- nsAutoCString extension;
- nsresult rv = GetFileExtension(extension);
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString newFileName(input);
- if (!extension.IsEmpty()) {
- newFileName.Append('.');
- newFileName.Append(extension);
- }
- return SetFileName(newFileName);
- }
- NS_IMETHODIMP
- nsStandardURL::SetFileExtension(const nsACString &input)
- {
- nsAutoCString newFileName;
- nsresult rv = GetFileBaseName(newFileName);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!input.IsEmpty()) {
- newFileName.Append('.');
- newFileName.Append(input);
- }
- return SetFileName(newFileName);
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsIFileURL
- //----------------------------------------------------------------------------
- nsresult
- nsStandardURL::EnsureFile()
- {
- NS_PRECONDITION(mSupportsFileURL,
- "EnsureFile() called on a URL that doesn't support files!");
- if (mFile) {
- // Nothing to do
- return NS_OK;
- }
- // Parse the spec if we don't have a cached result
- if (mSpec.IsEmpty()) {
- NS_WARNING("url not initialized");
- return NS_ERROR_NOT_INITIALIZED;
- }
- if (!SegmentIs(mScheme, "file")) {
- NS_WARNING("not a file URL");
- return NS_ERROR_FAILURE;
- }
- return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
- }
- NS_IMETHODIMP
- nsStandardURL::GetFile(nsIFile **result)
- {
- NS_PRECONDITION(mSupportsFileURL,
- "GetFile() called on a URL that doesn't support files!");
- nsresult rv = EnsureFile();
- if (NS_FAILED(rv))
- return rv;
- if (LOG_ENABLED()) {
- nsAutoCString path;
- mFile->GetNativePath(path);
- LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
- this, mSpec.get(), path.get()));
- }
- // clone the file, so the caller can modify it.
- // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
- // nsIFile returned from this method; but it seems that some folks do
- // (see bug 161921). until we can be sure that all the consumers are
- // behaving themselves, we'll stay on the safe side and clone the file.
- // see bug 212724 about fixing the consumers.
- return mFile->Clone(result);
- }
- NS_IMETHODIMP
- nsStandardURL::SetFile(nsIFile *file)
- {
- ENSURE_MUTABLE();
- NS_ENSURE_ARG_POINTER(file);
- nsresult rv;
- nsAutoCString url;
- rv = net_GetURLSpecFromFile(file, url);
- if (NS_FAILED(rv)) return rv;
- uint32_t oldURLType = mURLType;
- uint32_t oldDefaultPort = mDefaultPort;
- rv = Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, url, nullptr, nullptr);
- if (NS_FAILED(rv)) {
- // Restore the old url type and default port if the call to Init fails.
- mURLType = oldURLType;
- mDefaultPort = oldDefaultPort;
- return rv;
- }
- // must clone |file| since its value is not guaranteed to remain constant
- InvalidateCache();
- if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
- NS_WARNING("nsIFile::Clone failed");
- // failure to clone is not fatal (GetFile will generate mFile)
- mFile = nullptr;
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsIStandardURL
- //----------------------------------------------------------------------------
- inline bool
- IsUTFCharset(const char *aCharset)
- {
- return ((aCharset[0] == 'U' || aCharset[0] == 'u') &&
- (aCharset[1] == 'T' || aCharset[1] == 't') &&
- (aCharset[2] == 'F' || aCharset[2] == 'f'));
- }
- NS_IMETHODIMP
- nsStandardURL::Init(uint32_t urlType,
- int32_t defaultPort,
- const nsACString &spec,
- const char *charset,
- nsIURI *baseURI)
- {
- ENSURE_MUTABLE();
- if (spec.Length() > (uint32_t) net_GetURLMaxLength() ||
- defaultPort > std::numeric_limits<uint16_t>::max()) {
- return NS_ERROR_MALFORMED_URI;
- }
- InvalidateCache();
- switch (urlType) {
- case URLTYPE_STANDARD:
- mParser = net_GetStdURLParser();
- break;
- case URLTYPE_AUTHORITY:
- mParser = net_GetAuthURLParser();
- break;
- case URLTYPE_NO_AUTHORITY:
- mParser = net_GetNoAuthURLParser();
- break;
- default:
- NS_NOTREACHED("bad urlType");
- return NS_ERROR_INVALID_ARG;
- }
- mDefaultPort = defaultPort;
- mURLType = urlType;
- mOriginCharset.Truncate();
- //if charset override is absent, use UTF8 as url encoding
- if (charset != nullptr && *charset != '\0' && !IsUTFCharset(charset)) {
- mOriginCharset = charset;
- }
- if (baseURI && net_IsAbsoluteURL(spec)) {
- baseURI = nullptr;
- }
- if (!baseURI)
- return SetSpec(spec);
- nsAutoCString buf;
- nsresult rv = baseURI->Resolve(spec, buf);
- if (NS_FAILED(rv)) return rv;
- return SetSpec(buf);
- }
- NS_IMETHODIMP
- nsStandardURL::SetDefaultPort(int32_t aNewDefaultPort)
- {
- ENSURE_MUTABLE();
- InvalidateCache();
- // should never be more than 16 bit
- if (aNewDefaultPort >= std::numeric_limits<uint16_t>::max()) {
- return NS_ERROR_MALFORMED_URI;
- }
- // If we're already using the new default-port as a custom port, then clear
- // it off of our mSpec & set mPort to -1, to indicate that we'll be using
- // the default from now on (which happens to match what we already had).
- if (mPort == aNewDefaultPort) {
- ReplacePortInSpec(-1);
- mPort = -1;
- }
- mDefaultPort = aNewDefaultPort;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetMutable(bool *value)
- {
- *value = mMutable;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::SetMutable(bool value)
- {
- NS_ENSURE_ARG(mMutable || !value);
- mMutable = value;
- return NS_OK;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsISerializable
- //----------------------------------------------------------------------------
- NS_IMETHODIMP
- nsStandardURL::Read(nsIObjectInputStream *stream)
- {
- NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
- NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
- "Shouldn't have spec encoding here");
-
- nsresult rv;
-
- uint32_t urlType;
- rv = stream->Read32(&urlType);
- if (NS_FAILED(rv)) return rv;
- mURLType = urlType;
- switch (mURLType) {
- case URLTYPE_STANDARD:
- mParser = net_GetStdURLParser();
- break;
- case URLTYPE_AUTHORITY:
- mParser = net_GetAuthURLParser();
- break;
- case URLTYPE_NO_AUTHORITY:
- mParser = net_GetNoAuthURLParser();
- break;
- default:
- NS_NOTREACHED("bad urlType");
- return NS_ERROR_FAILURE;
- }
- rv = stream->Read32((uint32_t *) &mPort);
- if (NS_FAILED(rv)) return rv;
- rv = stream->Read32((uint32_t *) &mDefaultPort);
- if (NS_FAILED(rv)) return rv;
- rv = NS_ReadOptionalCString(stream, mSpec);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mScheme);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mAuthority);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mUsername);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mPassword);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mHost);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mPath);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mFilepath);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mDirectory);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mBasename);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mExtension);
- if (NS_FAILED(rv)) return rv;
- // handle forward compatibility from older serializations that included mParam
- URLSegment old_param;
- rv = ReadSegment(stream, old_param);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mQuery);
- if (NS_FAILED(rv)) return rv;
- rv = ReadSegment(stream, mRef);
- if (NS_FAILED(rv)) return rv;
- rv = NS_ReadOptionalCString(stream, mOriginCharset);
- if (NS_FAILED(rv)) return rv;
- bool isMutable;
- rv = stream->ReadBoolean(&isMutable);
- if (NS_FAILED(rv)) return rv;
- mMutable = isMutable;
- bool supportsFileURL;
- rv = stream->ReadBoolean(&supportsFileURL);
- if (NS_FAILED(rv)) return rv;
- mSupportsFileURL = supportsFileURL;
- uint32_t hostEncoding;
- rv = stream->Read32(&hostEncoding);
- if (NS_FAILED(rv)) return rv;
- if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) {
- NS_WARNING("Unexpected host encoding");
- return NS_ERROR_UNEXPECTED;
- }
- mHostEncoding = hostEncoding;
- // wait until object is set up, then modify path to include the param
- if (old_param.mLen >= 0) { // note that mLen=0 is ";"
- // If this wasn't empty, it marks characters between the end of the
- // file and start of the query - mPath should include the param,
- // query and ref already. Bump the mFilePath and
- // directory/basename/extension components to include this.
- mFilepath.Merge(mSpec, ';', old_param);
- mDirectory.Merge(mSpec, ';', old_param);
- mBasename.Merge(mSpec, ';', old_param);
- mExtension.Merge(mSpec, ';', old_param);
- }
-
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::Write(nsIObjectOutputStream *stream)
- {
- MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
- "The spec should never be this long, we missed a check.");
- nsresult rv;
- rv = stream->Write32(mURLType);
- if (NS_FAILED(rv)) return rv;
- rv = stream->Write32(uint32_t(mPort));
- if (NS_FAILED(rv)) return rv;
- rv = stream->Write32(uint32_t(mDefaultPort));
- if (NS_FAILED(rv)) return rv;
- rv = NS_WriteOptionalStringZ(stream, mSpec.get());
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mScheme);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mAuthority);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mUsername);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mPassword);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mHost);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mPath);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mFilepath);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mDirectory);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mBasename);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mExtension);
- if (NS_FAILED(rv)) return rv;
- // for backwards compatibility since we removed mParam. Note that this will mean that
- // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they
- // after the removal of special handling). It only matters if you downgrade a browser to before
- // the patch.
- URLSegment empty;
- rv = WriteSegment(stream, empty);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mQuery);
- if (NS_FAILED(rv)) return rv;
- rv = WriteSegment(stream, mRef);
- if (NS_FAILED(rv)) return rv;
- rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get());
- if (NS_FAILED(rv)) return rv;
- rv = stream->WriteBoolean(mMutable);
- if (NS_FAILED(rv)) return rv;
- rv = stream->WriteBoolean(mSupportsFileURL);
- if (NS_FAILED(rv)) return rv;
- rv = stream->Write32(mHostEncoding);
- if (NS_FAILED(rv)) return rv;
- // mSpecEncoding and mHostA are just caches that can be recovered as needed.
- return NS_OK;
- }
- //---------------------------------------------------------------------------
- // nsStandardURL::nsIIPCSerializableURI
- //---------------------------------------------------------------------------
- inline
- ipc::StandardURLSegment
- ToIPCSegment(const nsStandardURL::URLSegment& aSegment)
- {
- return ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen);
- }
- inline
- MOZ_MUST_USE bool
- FromIPCSegment(const nsACString& aSpec, const ipc::StandardURLSegment& aSegment, nsStandardURL::URLSegment& aTarget)
- {
- // This seems to be just an empty segment.
- if (aSegment.length() == -1) {
- aTarget = nsStandardURL::URLSegment();
- return true;
- }
- // A value of -1 means an empty segment, but < -1 is undefined.
- if (NS_WARN_IF(aSegment.length() < -1)) {
- return false;
- }
- CheckedInt<uint32_t> segmentLen = aSegment.position();
- segmentLen += aSegment.length();
- // Make sure the segment does not extend beyond the spec.
- if (NS_WARN_IF(!segmentLen.isValid() || segmentLen.value() > aSpec.Length())) {
- return false;
- }
- aTarget.mPos = aSegment.position();
- aTarget.mLen = aSegment.length();
- return true;
- }
- void
- nsStandardURL::Serialize(URIParams& aParams)
- {
- MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
- "The spec should never be this long, we missed a check.");
- StandardURLParams params;
- params.urlType() = mURLType;
- params.port() = mPort;
- params.defaultPort() = mDefaultPort;
- params.spec() = mSpec;
- params.scheme() = ToIPCSegment(mScheme);
- params.authority() = ToIPCSegment(mAuthority);
- params.username() = ToIPCSegment(mUsername);
- params.password() = ToIPCSegment(mPassword);
- params.host() = ToIPCSegment(mHost);
- params.path() = ToIPCSegment(mPath);
- params.filePath() = ToIPCSegment(mFilepath);
- params.directory() = ToIPCSegment(mDirectory);
- params.baseName() = ToIPCSegment(mBasename);
- params.extension() = ToIPCSegment(mExtension);
- params.query() = ToIPCSegment(mQuery);
- params.ref() = ToIPCSegment(mRef);
- params.originCharset() = mOriginCharset;
- params.isMutable() = !!mMutable;
- params.supportsFileURL() = !!mSupportsFileURL;
- params.hostEncoding() = mHostEncoding;
- // mSpecEncoding and mHostA are just caches that can be recovered as needed.
- aParams = params;
- }
- bool
- nsStandardURL::Deserialize(const URIParams& aParams)
- {
- NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
- NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
- "Shouldn't have spec encoding here");
- NS_PRECONDITION(!mFile, "Shouldn't have cached file");
- if (aParams.type() != URIParams::TStandardURLParams) {
- NS_ERROR("Received unknown parameters from the other process!");
- return false;
- }
- // If we exit early, make sure to clear the URL so we don't fail the sanity
- // check in the destructor
- auto clearOnExit = MakeScopeExit([&] { Clear(); });
- const StandardURLParams& params = aParams.get_StandardURLParams();
- mURLType = params.urlType();
- switch (mURLType) {
- case URLTYPE_STANDARD:
- mParser = net_GetStdURLParser();
- break;
- case URLTYPE_AUTHORITY:
- mParser = net_GetAuthURLParser();
- break;
- case URLTYPE_NO_AUTHORITY:
- mParser = net_GetNoAuthURLParser();
- break;
- default:
- NS_NOTREACHED("bad urlType");
- return false;
- }
- if (params.hostEncoding() != eEncoding_ASCII &&
- params.hostEncoding() != eEncoding_UTF8) {
- NS_WARNING("Unexpected host encoding");
- return false;
- }
- mPort = params.port();
- mDefaultPort = params.defaultPort();
- mSpec = params.spec();
- NS_ENSURE_TRUE(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.scheme(), mScheme), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.authority(), mAuthority), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.username(), mUsername), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.password(), mPassword), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.host(), mHost), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.path(), mPath), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.filePath(), mFilepath), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.directory(), mDirectory), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.baseName(), mBasename), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.extension(), mExtension), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.query(), mQuery), false);
- NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.ref(), mRef), false);
- mOriginCharset = params.originCharset();
- mMutable = params.isMutable();
- mSupportsFileURL = params.supportsFileURL();
- mHostEncoding = params.hostEncoding();
- // Some segment sanity checks
- NS_ENSURE_TRUE(mScheme.mPos == 0, false);
- NS_ENSURE_TRUE(mScheme.mLen > 0, false);
- // Make sure scheme is followed by :// (3 characters)
- NS_ENSURE_TRUE(mScheme.mLen < INT32_MAX - 3, false); // avoid overflow
- NS_ENSURE_TRUE(mSpec.Length() >= (uint32_t) mScheme.mLen + 3, false);
- NS_ENSURE_TRUE(nsDependentCSubstring(mSpec, mScheme.mLen, 3).EqualsLiteral("://"), false);
- NS_ENSURE_TRUE(mPath.mLen != -1 && mSpec.CharAt(mPath.mPos) == '/', false);
- NS_ENSURE_TRUE(mPath.mPos == mFilepath.mPos, false);
- NS_ENSURE_TRUE(mQuery.mLen == -1 || mSpec.CharAt(mQuery.mPos - 1) == '?', false);
- NS_ENSURE_TRUE(mRef.mLen == -1 || mSpec.CharAt(mRef.mPos - 1) == '#', false);
- // Sanity-check the result
- if (!IsValid()) {
- return false;
- }
- clearOnExit.release();
- // mSpecEncoding and mHostA are just caches that can be recovered as needed.
- return true;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsIClassInfo
- //----------------------------------------------------------------------------
- NS_IMETHODIMP
- nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array)
- {
- *count = 0;
- *array = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval)
- {
- *_retval = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetContractID(char * *aContractID)
- {
- *aContractID = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetClassDescription(char * *aClassDescription)
- {
- *aClassDescription = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetClassID(nsCID * *aClassID)
- {
- *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID));
- if (!*aClassID)
- return NS_ERROR_OUT_OF_MEMORY;
- return GetClassIDNoAlloc(*aClassID);
- }
- NS_IMETHODIMP
- nsStandardURL::GetFlags(uint32_t *aFlags)
- {
- *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
- {
- *aClassIDNoAlloc = kStandardURLCID;
- return NS_OK;
- }
- //----------------------------------------------------------------------------
- // nsStandardURL::nsISizeOf
- //----------------------------------------------------------------------------
- size_t
- nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
- {
- return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
- mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
- aMallocSizeOf(mHostA);
- // Measurement of the following members may be added later if DMD finds it is
- // worthwhile:
- // - mParser
- // - mFile
- }
- size_t
- nsStandardURL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
- return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
- }
- } // namespace net
- } // namespace mozilla
|