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