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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Darin Fisher <darin@netscape.com> (original author)
25 * Andreas Otte <andreas.otte@debitel.net>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsStandardURL.h"
42 #include "nsDependentSubstring.h"
43 #include "nsReadableUtils.h"
46 #include "nsILocalFile.h"
47 #include "nsIObjectInputStream.h"
48 #include "nsIObjectOutputStream.h"
49 #include "nsICharsetConverterManager.h"
50 #include "nsIPrefService.h"
51 #include "nsIPrefBranch.h"
52 #include "nsIPrefBranch2.h"
53 #include "nsIIDNService.h"
54 #include "nsNetUtil.h"
56 #include "nsAutoPtr.h"
57 #include "nsIProgrammingLanguage.h"
59 static NS_DEFINE_CID(kThisImplCID
, NS_THIS_STANDARDURL_IMPL_CID
);
60 static NS_DEFINE_CID(kStandardURLCID
, NS_STANDARDURL_CID
);
62 nsIIDNService
*nsStandardURL::gIDN
= nsnull
;
63 nsICharsetConverterManager
*nsStandardURL::gCharsetMgr
= nsnull
;
64 PRBool
nsStandardURL::gInitialized
= PR_FALSE
;
65 PRBool
nsStandardURL::gEscapeUTF8
= PR_TRUE
;
66 PRBool
nsStandardURL::gAlwaysEncodeInUTF8
= PR_TRUE
;
67 PRBool
nsStandardURL::gEncodeQueryInUTF8
= PR_TRUE
;
69 #if defined(PR_LOGGING)
71 // setenv NSPR_LOG_MODULES nsStandardURL:5
73 static PRLogModuleInfo
*gStandardURLLog
;
75 #define LOG(args) PR_LOG(gStandardURLLog, PR_LOG_DEBUG, args)
76 #define LOG_ENABLED() PR_LOG_TEST(gStandardURLLog, PR_LOG_DEBUG)
78 //----------------------------------------------------------------------------
80 #define ENSURE_MUTABLE() \
83 NS_ERROR("attempt to modify an immutable nsStandardURL"); \
84 return NS_ERROR_ABORT; \
88 //----------------------------------------------------------------------------
91 EncodeString(nsIUnicodeEncoder
*encoder
, const nsAFlatString
&str
, nsACString
&result
)
94 PRInt32 len
= str
.Length();
97 rv
= encoder
->GetMaxLength(str
.get(), len
, &maxlen
);
101 char buf
[256], *p
= buf
;
102 if (PRUint32(maxlen
) > sizeof(buf
) - 1) {
103 p
= (char *) malloc(maxlen
+ 1);
105 return NS_ERROR_OUT_OF_MEMORY
;
108 rv
= encoder
->Convert(str
.get(), &len
, p
, &maxlen
);
111 if (rv
== NS_ERROR_UENC_NOMAPPING
) {
112 NS_WARNING("unicode conversion failed");
113 rv
= NS_ERROR_UNEXPECTED
;
119 len
= sizeof(buf
) - 1;
120 rv
= encoder
->Finish(buf
, &len
);
134 //----------------------------------------------------------------------------
135 // nsStandardURL::nsPrefObserver
136 //----------------------------------------------------------------------------
138 #define NS_NET_PREF_ESCAPEUTF8 "network.standard-url.escape-utf8"
139 #define NS_NET_PREF_ENABLEIDN "network.enableIDN"
140 #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
141 #define NS_NET_PREF_ENCODEQUERYINUTF8 "network.standard-url.encode-query-utf8"
143 NS_IMPL_ISUPPORTS1(nsStandardURL::nsPrefObserver
, nsIObserver
)
145 NS_IMETHODIMP
nsStandardURL::
146 nsPrefObserver::Observe(nsISupports
*subject
,
148 const PRUnichar
*data
)
150 if (!strcmp(topic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
)) {
151 nsCOMPtr
<nsIPrefBranch
> prefBranch( do_QueryInterface(subject
) );
153 PrefsChanged(prefBranch
, NS_ConvertUTF16toUTF8(data
).get());
159 //----------------------------------------------------------------------------
160 // nsStandardURL::nsSegmentEncoder
161 //----------------------------------------------------------------------------
164 nsSegmentEncoder::nsSegmentEncoder(const char *charset
)
169 PRInt32
nsStandardURL::
170 nsSegmentEncoder::EncodeSegmentCount(const char *str
,
171 const URLSegment
&seg
,
173 nsAFlatCString
&result
,
181 PRUint32 pos
= seg
.mPos
;
184 // first honor the origin charset if appropriate. as an optimization,
185 // only do this if the segment is non-ASCII. Further, if mCharset is
186 // null or the empty string then the origin charset is UTF-8 and there
188 nsCAutoString encBuf
;
189 if (mCharset
&& *mCharset
&& !nsCRT::IsAscii(str
+ pos
, len
)) {
190 // we have to encode this segment
191 if (mEncoder
|| InitUnicodeEncoder()) {
192 NS_ConvertUTF8toUTF16
ucsBuf(Substring(str
+ pos
, str
+ pos
+ len
));
193 if (NS_SUCCEEDED(EncodeString(mEncoder
, ucsBuf
, encBuf
))) {
196 len
= encBuf
.Length();
198 // else some failure occured... assume UTF-8 is ok.
202 // escape per RFC2396 unless UTF-8 and allowed by preferences
203 PRInt16 escapeFlags
= (gEscapeUTF8
|| mEncoder
) ? 0 : esc_OnlyASCII
;
205 PRUint32 initLen
= result
.Length();
207 // now perform any required escaping
208 if (NS_EscapeURL(str
+ pos
, len
, mask
| escapeFlags
, result
)) {
209 len
= result
.Length() - initLen
;
212 else if (str
== encBuf
.get()) {
213 result
+= encBuf
; // append only!!
214 len
= encBuf
.Length();
221 const nsACString
&nsStandardURL::
222 nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString
&str
,
224 nsAFlatCString
&result
)
228 EncodeSegmentCount(str
.BeginReading(text
), URLSegment(0, str
.Length()), mask
, result
, encoded
);
234 PRBool
nsStandardURL::
235 nsSegmentEncoder::InitUnicodeEncoder()
237 NS_ASSERTION(!mEncoder
, "Don't call this if we have an encoder already!");
240 rv
= CallGetService("@mozilla.org/charset-converter-manager;1",
243 NS_ERROR("failed to get charset-converter-manager");
248 rv
= gCharsetMgr
->GetUnicodeEncoder(mCharset
, getter_AddRefs(mEncoder
));
250 NS_ERROR("failed to get unicode encoder");
251 mEncoder
= 0; // just in case
258 #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
259 nsSegmentEncoder name(useUTF8 ? nsnull : mOriginCharset.get())
261 #define GET_SEGMENT_ENCODER(name) \
262 GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8)
264 #define GET_QUERY_ENCODER(name) \
265 GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8 && \
268 //----------------------------------------------------------------------------
269 // nsStandardURL <public>
270 //----------------------------------------------------------------------------
272 nsStandardURL::nsStandardURL(PRBool aSupportsFileURL
)
276 , mHostEncoding(eEncoding_ASCII
)
277 , mSpecEncoding(eEncoding_Unknown
)
278 , mURLType(URLTYPE_STANDARD
)
280 , mSupportsFileURL(aSupportsFileURL
)
282 #if defined(PR_LOGGING)
283 if (!gStandardURLLog
)
284 gStandardURLLog
= PR_NewLogModule("nsStandardURL");
287 LOG(("Creating nsStandardURL @%p\n", this));
290 gInitialized
= PR_TRUE
;
294 // default parser in case nsIStandardURL::Init is never called
295 mParser
= net_GetStdURLParser();
298 nsStandardURL::~nsStandardURL()
300 LOG(("Destroying nsStandardURL @%p\n", this));
306 nsStandardURL::InitGlobalObjects()
308 nsCOMPtr
<nsIPrefBranch2
> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID
) );
310 nsCOMPtr
<nsIObserver
> obs( new nsPrefObserver() );
311 prefBranch
->AddObserver(NS_NET_PREF_ESCAPEUTF8
, obs
.get(), PR_FALSE
);
312 prefBranch
->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8
, obs
.get(), PR_FALSE
);
313 prefBranch
->AddObserver(NS_NET_PREF_ENCODEQUERYINUTF8
, obs
.get(), PR_FALSE
);
314 prefBranch
->AddObserver(NS_NET_PREF_ENABLEIDN
, obs
.get(), PR_FALSE
);
316 PrefsChanged(prefBranch
, nsnull
);
321 nsStandardURL::ShutdownGlobalObjects()
324 NS_IF_RELEASE(gCharsetMgr
);
327 //----------------------------------------------------------------------------
328 // nsStandardURL <private>
329 //----------------------------------------------------------------------------
332 nsStandardURL::Clear()
342 mHostEncoding
= eEncoding_ASCII
;
358 nsStandardURL::InvalidateCache(PRBool invalidateCachedFile
)
360 if (invalidateCachedFile
)
363 mSpecEncoding
= eEncoding_Unknown
;
367 nsStandardURL::EscapeIPv6(const char *host
, nsCString
&result
)
369 // Escape IPv6 address literal by surrounding it with []'s
370 if (host
&& (host
[0] != '[') && PL_strchr(host
, ':')) {
380 nsStandardURL::NormalizeIDN(const nsCSubstring
&host
, nsCString
&result
)
382 // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8,
383 // then make sure it is normalized per IDN.
385 // this function returns PR_TRUE if normalization succeeds.
387 // NOTE: As a side-effect this function sets mHostEncoding. While it would
388 // be nice to avoid side-effects in this function, the implementation of
389 // this function is already somewhat bound to the behavior of the
390 // callsites. Anyways, this function exists to avoid code duplication, so
391 // side-effects abound :-/
393 NS_ASSERTION(mHostEncoding
== eEncoding_ASCII
, "unexpected default encoding");
397 NS_SUCCEEDED(gIDN
->ConvertToDisplayIDN(host
, &isASCII
, result
))) {
399 mHostEncoding
= eEncoding_UTF8
;
409 nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag
, char *path
)
411 net_CoalesceDirs(coalesceFlag
, path
);
412 PRInt32 newLen
= strlen(path
);
413 if (newLen
< mPath
.mLen
) {
414 PRInt32 diff
= newLen
- mPath
.mLen
;
416 mDirectory
.mLen
+= diff
;
417 mFilepath
.mLen
+= diff
;
418 ShiftFromBasename(diff
);
423 nsStandardURL::AppendSegmentToBuf(char *buf
, PRUint32 i
, const char *str
, URLSegment
&seg
, const nsCString
*escapedStr
, PRBool useEscaped
)
427 seg
.mLen
= escapedStr
->Length();
428 memcpy(buf
+ i
, escapedStr
->get(), seg
.mLen
);
431 memcpy(buf
+ i
, str
+ seg
.mPos
, seg
.mLen
);
439 nsStandardURL::AppendToBuf(char *buf
, PRUint32 i
, const char *str
, PRUint32 len
)
441 memcpy(buf
+ i
, str
, len
);
446 // 1- escape url segments (for improved GetSpec efficiency)
447 // 2- allocate spec buffer
448 // 3- write url segments
449 // 4- update url segment positions and lengths
451 nsStandardURL::BuildNormalizedSpec(const char *spec
)
453 // Assumptions: all member URLSegments must be relative the |spec| argument
454 // passed to this function.
456 // buffers for holding escaped url segments (these will remain empty unless
457 // escaping is required).
458 nsCAutoString encUsername
, encPassword
, encHost
, encDirectory
,
459 encBasename
, encExtension
, encParam
, encQuery
, encRef
;
460 PRBool useEncUsername
, useEncPassword
, useEncHost
, useEncDirectory
,
461 useEncBasename
, useEncExtension
, useEncParam
, useEncQuery
, useEncRef
;
464 // escape each URL segment, if necessary, and calculate approximate normalized
467 PRUint32 approxLen
= 3; // includes room for "://"
469 // the scheme is already ASCII
470 if (mScheme
.mLen
> 0)
471 approxLen
+= mScheme
.mLen
;
473 // encode URL segments; convert UTF-8 to origin charset and possibly escape.
474 // results written to encXXX variables only if |spec| is not already in the
475 // appropriate encoding.
477 GET_SEGMENT_ENCODER(encoder
);
478 GET_QUERY_ENCODER(queryEncoder
);
479 approxLen
+= encoder
.EncodeSegmentCount(spec
, mUsername
, esc_Username
, encUsername
, useEncUsername
);
480 approxLen
+= encoder
.EncodeSegmentCount(spec
, mPassword
, esc_Password
, encPassword
, useEncPassword
);
481 approxLen
+= encoder
.EncodeSegmentCount(spec
, mDirectory
, esc_Directory
, encDirectory
, useEncDirectory
);
482 approxLen
+= encoder
.EncodeSegmentCount(spec
, mBasename
, esc_FileBaseName
, encBasename
, useEncBasename
);
483 approxLen
+= encoder
.EncodeSegmentCount(spec
, mExtension
, esc_FileExtension
, encExtension
, useEncExtension
);
484 approxLen
+= encoder
.EncodeSegmentCount(spec
, mParam
, esc_Param
, encParam
, useEncParam
);
485 approxLen
+= queryEncoder
.EncodeSegmentCount(spec
, mQuery
, esc_Query
, encQuery
, useEncQuery
);
486 approxLen
+= encoder
.EncodeSegmentCount(spec
, mRef
, esc_Ref
, encRef
, useEncRef
);
489 // do not escape the hostname, if IPv6 address literal, mHost will
490 // already point to a [ ] delimited IPv6 address literal.
491 // However, perform Unicode normalization on it, as IDN does.
492 mHostEncoding
= eEncoding_ASCII
;
493 if (mHost
.mLen
> 0) {
494 const nsCSubstring
& tempHost
=
495 Substring(spec
+ mHost
.mPos
, spec
+ mHost
.mPos
+ mHost
.mLen
);
496 if (tempHost
.FindChar('\0') != kNotFound
)
497 return NS_ERROR_MALFORMED_URI
; // null embedded in hostname
498 if (tempHost
.FindChar(' ') != kNotFound
)
499 return NS_ERROR_MALFORMED_URI
; // don't allow spaces in the hostname
500 if ((useEncHost
= NormalizeIDN(tempHost
, encHost
)))
501 approxLen
+= encHost
.Length();
503 approxLen
+= mHost
.mLen
;
507 // generate the normalized URL string
509 if (!EnsureStringLength(mSpec
, approxLen
+ 32))
510 return NS_ERROR_OUT_OF_MEMORY
;
512 mSpec
.BeginWriting(buf
);
515 if (mScheme
.mLen
> 0) {
516 i
= AppendSegmentToBuf(buf
, i
, spec
, mScheme
);
517 net_ToLowerCase(buf
+ mScheme
.mPos
, mScheme
.mLen
);
518 i
= AppendToBuf(buf
, i
, "://", 3);
521 // record authority starting position
525 if (mUsername
.mLen
> 0) {
526 i
= AppendSegmentToBuf(buf
, i
, spec
, mUsername
, &encUsername
, useEncUsername
);
527 if (mPassword
.mLen
>= 0) {
529 i
= AppendSegmentToBuf(buf
, i
, spec
, mPassword
, &encPassword
, useEncPassword
);
533 if (mHost
.mLen
> 0) {
534 i
= AppendSegmentToBuf(buf
, i
, spec
, mHost
, &encHost
, useEncHost
);
535 net_ToLowerCase(buf
+ mHost
.mPos
, mHost
.mLen
);
536 if (mPort
!= -1 && mPort
!= mDefaultPort
) {
537 nsCAutoString portbuf
;
538 portbuf
.AppendInt(mPort
);
540 i
= AppendToBuf(buf
, i
, portbuf
.get(), portbuf
.Length());
544 // record authority length
545 mAuthority
.mLen
= i
- mAuthority
.mPos
;
547 // path must always start with a "/"
548 if (mPath
.mLen
<= 0) {
549 LOG(("setting path=/"));
550 mDirectory
.mPos
= mFilepath
.mPos
= mPath
.mPos
= i
;
551 mDirectory
.mLen
= mFilepath
.mLen
= mPath
.mLen
= 1;
552 // basename must exist, even if empty (bug 113508)
553 mBasename
.mPos
= i
+1;
558 PRUint32 leadingSlash
= 0;
559 if (spec
[mPath
.mPos
] != '/') {
560 LOG(("adding leading slash to path\n"));
563 // basename must exist, even if empty (bugs 113508, 429347)
564 if (mBasename
.mLen
== -1) {
570 // record corrected (file)path starting position
571 mPath
.mPos
= mFilepath
.mPos
= i
- leadingSlash
;
573 i
= AppendSegmentToBuf(buf
, i
, spec
, mDirectory
, &encDirectory
, useEncDirectory
);
575 // the directory must end with a '/'
576 if (buf
[i
-1] != '/') {
581 i
= AppendSegmentToBuf(buf
, i
, spec
, mBasename
, &encBasename
, useEncBasename
);
583 // make corrections to directory segment if leadingSlash
585 mDirectory
.mPos
= mPath
.mPos
;
586 if (mDirectory
.mLen
>= 0)
587 mDirectory
.mLen
+= leadingSlash
;
592 if (mExtension
.mLen
>= 0) {
594 i
= AppendSegmentToBuf(buf
, i
, spec
, mExtension
, &encExtension
, useEncExtension
);
596 // calculate corrected filepath length
597 mFilepath
.mLen
= i
- mFilepath
.mPos
;
599 if (mParam
.mLen
>= 0) {
601 i
= AppendSegmentToBuf(buf
, i
, spec
, mParam
, &encParam
, useEncParam
);
603 if (mQuery
.mLen
>= 0) {
605 i
= AppendSegmentToBuf(buf
, i
, spec
, mQuery
, &encQuery
, useEncQuery
);
607 if (mRef
.mLen
>= 0) {
609 i
= AppendSegmentToBuf(buf
, i
, spec
, mRef
, &encRef
, useEncRef
);
611 // calculate corrected path length
612 mPath
.mLen
= i
- mPath
.mPos
;
617 if (mDirectory
.mLen
> 1) {
618 netCoalesceFlags coalesceFlag
= NET_COALESCE_NORMAL
;
619 if (SegmentIs(buf
,mScheme
,"ftp")) {
620 coalesceFlag
= (netCoalesceFlags
) (coalesceFlag
621 | NET_COALESCE_ALLOW_RELATIVE_ROOT
622 | NET_COALESCE_DOUBLE_SLASH_IS_ROOT
);
624 CoalescePath(coalesceFlag
, buf
+ mDirectory
.mPos
);
626 mSpec
.SetLength(strlen(buf
));
627 NS_ASSERTION(mSpec
.Length() <= approxLen
+32, "We've overflowed the mSpec buffer!");
632 nsStandardURL::SegmentIs(const URLSegment
&seg
, const char *val
, PRBool ignoreCase
)
634 // one or both may be null
635 if (!val
|| mSpec
.IsEmpty())
636 return (!val
&& (mSpec
.IsEmpty() || seg
.mLen
< 0));
639 // if the first |seg.mLen| chars of |val| match, then |val| must
640 // also be null terminated at |seg.mLen|.
642 return !PL_strncasecmp(mSpec
.get() + seg
.mPos
, val
, seg
.mLen
)
643 && (val
[seg
.mLen
] == '\0');
645 return !strncmp(mSpec
.get() + seg
.mPos
, val
, seg
.mLen
)
646 && (val
[seg
.mLen
] == '\0');
650 nsStandardURL::SegmentIs(const char* spec
, const URLSegment
&seg
, const char *val
, PRBool ignoreCase
)
652 // one or both may be null
654 return (!val
&& (!spec
|| seg
.mLen
< 0));
657 // if the first |seg.mLen| chars of |val| match, then |val| must
658 // also be null terminated at |seg.mLen|.
660 return !PL_strncasecmp(spec
+ seg
.mPos
, val
, seg
.mLen
)
661 && (val
[seg
.mLen
] == '\0');
663 return !strncmp(spec
+ seg
.mPos
, val
, seg
.mLen
)
664 && (val
[seg
.mLen
] == '\0');
668 nsStandardURL::SegmentIs(const URLSegment
&seg1
, const char *val
, const URLSegment
&seg2
, PRBool ignoreCase
)
670 if (seg1
.mLen
!= seg2
.mLen
)
672 if (seg1
.mLen
== -1 || (!val
&& mSpec
.IsEmpty()))
673 return PR_TRUE
; // both are empty
675 return !PL_strncasecmp(mSpec
.get() + seg1
.mPos
, val
+ seg2
.mPos
, seg1
.mLen
);
677 return !strncmp(mSpec
.get() + seg1
.mPos
, val
+ seg2
.mPos
, seg1
.mLen
);
681 nsStandardURL::ReplaceSegment(PRUint32 pos
, PRUint32 len
, const char *val
, PRUint32 valLen
)
685 mSpec
.Insert(val
, pos
, valLen
);
687 mSpec
.Replace(pos
, len
, nsDependentCString(val
, valLen
));
691 // else remove the specified segment
693 return -PRInt32(len
);
697 nsStandardURL::ReplaceSegment(PRUint32 pos
, PRUint32 len
, const nsACString
&val
)
700 mSpec
.Insert(val
, pos
);
702 mSpec
.Replace(pos
, len
, val
);
703 return val
.Length() - len
;
707 nsStandardURL::ParseURL(const char *spec
, PRInt32 specLen
)
712 // parse given URL string
714 rv
= mParser
->ParseURL(spec
, specLen
,
715 &mScheme
.mPos
, &mScheme
.mLen
,
716 &mAuthority
.mPos
, &mAuthority
.mLen
,
717 &mPath
.mPos
, &mPath
.mLen
);
718 if (NS_FAILED(rv
)) return rv
;
721 if (mScheme
.mLen
<= 0) {
722 printf("spec=%s\n", spec
);
723 NS_WARNING("malformed url: no scheme");
727 if (mAuthority
.mLen
> 0) {
728 rv
= mParser
->ParseAuthority(spec
+ mAuthority
.mPos
, mAuthority
.mLen
,
729 &mUsername
.mPos
, &mUsername
.mLen
,
730 &mPassword
.mPos
, &mPassword
.mLen
,
731 &mHost
.mPos
, &mHost
.mLen
,
733 if (NS_FAILED(rv
)) return rv
;
735 // Don't allow mPort to be set to this URI's default port
736 if (mPort
== mDefaultPort
)
739 mUsername
.mPos
+= mAuthority
.mPos
;
740 mPassword
.mPos
+= mAuthority
.mPos
;
741 mHost
.mPos
+= mAuthority
.mPos
;
745 rv
= ParsePath(spec
, mPath
.mPos
, mPath
.mLen
);
751 nsStandardURL::ParsePath(const char *spec
, PRUint32 pathPos
, PRInt32 pathLen
)
753 nsresult rv
= mParser
->ParsePath(spec
+ pathPos
, pathLen
,
754 &mFilepath
.mPos
, &mFilepath
.mLen
,
755 &mParam
.mPos
, &mParam
.mLen
,
756 &mQuery
.mPos
, &mQuery
.mLen
,
757 &mRef
.mPos
, &mRef
.mLen
);
758 if (NS_FAILED(rv
)) return rv
;
760 mFilepath
.mPos
+= pathPos
;
761 mParam
.mPos
+= pathPos
;
762 mQuery
.mPos
+= pathPos
;
763 mRef
.mPos
+= pathPos
;
765 if (mFilepath
.mLen
> 0) {
766 rv
= mParser
->ParseFilePath(spec
+ mFilepath
.mPos
, mFilepath
.mLen
,
767 &mDirectory
.mPos
, &mDirectory
.mLen
,
768 &mBasename
.mPos
, &mBasename
.mLen
,
769 &mExtension
.mPos
, &mExtension
.mLen
);
770 if (NS_FAILED(rv
)) return rv
;
772 mDirectory
.mPos
+= mFilepath
.mPos
;
773 mBasename
.mPos
+= mFilepath
.mPos
;
774 mExtension
.mPos
+= mFilepath
.mPos
;
780 nsStandardURL::AppendToSubstring(PRUint32 pos
,
786 tailLen
= strlen(tail
);
788 char *result
= (char *) malloc(len
+ tailLen
+ 1);
790 memcpy(result
, mSpec
.get() + pos
, len
);
791 memcpy(result
+ len
, tail
, tailLen
);
792 result
[len
+ tailLen
] = '\0';
798 nsStandardURL::ReadSegment(nsIBinaryInputStream
*stream
, URLSegment
&seg
)
802 rv
= stream
->Read32(&seg
.mPos
);
803 if (NS_FAILED(rv
)) return rv
;
805 rv
= stream
->Read32((PRUint32
*) &seg
.mLen
);
806 if (NS_FAILED(rv
)) return rv
;
812 nsStandardURL::WriteSegment(nsIBinaryOutputStream
*stream
, const URLSegment
&seg
)
816 rv
= stream
->Write32(seg
.mPos
);
817 if (NS_FAILED(rv
)) return rv
;
819 rv
= stream
->Write32(PRUint32(seg
.mLen
));
820 if (NS_FAILED(rv
)) return rv
;
826 nsStandardURL::PrefsChanged(nsIPrefBranch
*prefs
, const char *pref
)
830 LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref
));
832 #define PREF_CHANGED(p) ((pref == nsnull) || !strcmp(pref, p))
833 #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
835 if (PREF_CHANGED(NS_NET_PREF_ENABLEIDN
)) {
837 if (GOT_PREF(NS_NET_PREF_ENABLEIDN
, val
) && val
) {
839 nsCOMPtr
<nsIIDNService
> serv(do_GetService(NS_IDNSERVICE_CONTRACTID
));
841 NS_ADDREF(gIDN
= serv
.get());
843 LOG(("IDN support %s\n", gIDN
? "enabled" : "disabled"));
846 if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8
)) {
847 if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8
, val
))
849 LOG(("escape UTF-8 %s\n", gEscapeUTF8
? "enabled" : "disabled"));
852 if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8
)) {
853 if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8
, val
))
854 gAlwaysEncodeInUTF8
= val
;
855 LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8
? "enabled" : "disabled"));
858 if (PREF_CHANGED(NS_NET_PREF_ENCODEQUERYINUTF8
)) {
859 if (GOT_PREF(NS_NET_PREF_ENCODEQUERYINUTF8
, val
))
860 gEncodeQueryInUTF8
= val
;
861 LOG(("encode query in UTF-8 %s\n", gEncodeQueryInUTF8
? "enabled" : "disabled"));
867 //----------------------------------------------------------------------------
868 // nsStandardURL::nsISupports
869 //----------------------------------------------------------------------------
871 NS_IMPL_ADDREF(nsStandardURL
)
872 NS_IMPL_RELEASE(nsStandardURL
)
874 NS_INTERFACE_MAP_BEGIN(nsStandardURL
)
875 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStandardURL
)
876 NS_INTERFACE_MAP_ENTRY(nsIURI
)
877 NS_INTERFACE_MAP_ENTRY(nsIURL
)
878 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL
, mSupportsFileURL
)
879 NS_INTERFACE_MAP_ENTRY(nsIStandardURL
)
880 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
881 NS_INTERFACE_MAP_ENTRY(nsIClassInfo
)
882 NS_INTERFACE_MAP_ENTRY(nsIMutable
)
883 // see nsStandardURL::Equals
884 if (aIID
.Equals(kThisImplCID
))
885 foundInterface
= static_cast<nsIURI
*>(this);
889 //----------------------------------------------------------------------------
890 // nsStandardURL::nsIURI
891 //----------------------------------------------------------------------------
893 // result may contain unescaped UTF-8 characters
895 nsStandardURL::GetSpec(nsACString
&result
)
901 // result may contain unescaped UTF-8 characters
903 nsStandardURL::GetPrePath(nsACString
&result
)
909 // result is strictly US-ASCII
911 nsStandardURL::GetScheme(nsACString
&result
)
917 // result may contain unescaped UTF-8 characters
919 nsStandardURL::GetUserPass(nsACString
&result
)
925 // result may contain unescaped UTF-8 characters
927 nsStandardURL::GetUsername(nsACString
&result
)
933 // result may contain unescaped UTF-8 characters
935 nsStandardURL::GetPassword(nsACString
&result
)
942 nsStandardURL::GetHostPort(nsACString
&result
)
949 nsStandardURL::GetHost(nsACString
&result
)
956 nsStandardURL::GetPort(PRInt32
*result
)
962 // result may contain unescaped UTF-8 characters
964 nsStandardURL::GetPath(nsACString
&result
)
972 nsStandardURL::GetAsciiSpec(nsACString
&result
)
974 if (mSpecEncoding
== eEncoding_Unknown
) {
976 mSpecEncoding
= eEncoding_ASCII
;
978 mSpecEncoding
= eEncoding_UTF8
;
981 if (mSpecEncoding
== eEncoding_ASCII
) {
986 // try to guess the capacity required for result...
987 result
.SetCapacity(mSpec
.Length() + PR_MIN(32, mSpec
.Length()/10));
989 result
= Substring(mSpec
, 0, mScheme
.mLen
+ 3);
991 NS_EscapeURL(Userpass(PR_TRUE
), esc_OnlyNonASCII
| esc_AlwaysCopy
, result
);
994 nsCAutoString escHostport
;
995 if (mHost
.mLen
> 0) {
997 (void) GetAsciiHost(escHostport
);
999 // escHostport = "hostA" + ":port"
1000 PRUint32 pos
= mHost
.mPos
+ mHost
.mLen
;
1001 if (pos
< mPath
.mPos
)
1002 escHostport
+= Substring(mSpec
, pos
, mPath
.mPos
- pos
);
1004 result
+= escHostport
;
1006 NS_EscapeURL(Path(), esc_OnlyNonASCII
| esc_AlwaysCopy
, result
);
1012 nsStandardURL::GetAsciiHost(nsACString
&result
)
1014 if (mHostEncoding
== eEncoding_ASCII
) {
1019 // perhaps we have it cached...
1027 rv
= gIDN
->ConvertUTF8toACE(Host(), result
);
1028 if (NS_SUCCEEDED(rv
)) {
1029 mHostA
= ToNewCString(result
);
1032 NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
1035 // something went wrong... guess all we can do is URL escape :-/
1036 NS_EscapeURL(Host(), esc_OnlyNonASCII
| esc_AlwaysCopy
, result
);
1041 nsStandardURL::GetOriginCharset(nsACString
&result
)
1043 if (mOriginCharset
.IsEmpty())
1044 result
.AssignLiteral("UTF-8");
1046 result
= mOriginCharset
;
1051 nsStandardURL::SetSpec(const nsACString
&input
)
1055 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
1056 const char *spec
= flat
.get();
1057 PRInt32 specLength
= flat
.Length();
1059 LOG(("nsStandardURL::SetSpec [spec=%s]\n", spec
));
1063 if (!spec
|| !*spec
)
1066 // filter out unexpected chars "\r\n\t" if necessary
1068 if (net_FilterURIString(spec
, buf1
)) {
1070 specLength
= buf1
.Length();
1073 // parse the given URL...
1074 nsresult rv
= ParseURL(spec
, specLength
);
1075 if (NS_FAILED(rv
)) return rv
;
1077 // finally, use the URLSegment member variables to build a normalized
1079 rv
= BuildNormalizedSpec(spec
);
1081 #if defined(PR_LOGGING)
1082 if (LOG_ENABLED()) {
1083 LOG((" spec = %s\n", mSpec
.get()));
1084 LOG((" port = %d\n", mPort
));
1085 LOG((" scheme = (%u,%d)\n", mScheme
.mPos
, mScheme
.mLen
));
1086 LOG((" authority = (%u,%d)\n", mAuthority
.mPos
, mAuthority
.mLen
));
1087 LOG((" username = (%u,%d)\n", mUsername
.mPos
, mUsername
.mLen
));
1088 LOG((" password = (%u,%d)\n", mPassword
.mPos
, mPassword
.mLen
));
1089 LOG((" hostname = (%u,%d)\n", mHost
.mPos
, mHost
.mLen
));
1090 LOG((" path = (%u,%d)\n", mPath
.mPos
, mPath
.mLen
));
1091 LOG((" filepath = (%u,%d)\n", mFilepath
.mPos
, mFilepath
.mLen
));
1092 LOG((" directory = (%u,%d)\n", mDirectory
.mPos
, mDirectory
.mLen
));
1093 LOG((" basename = (%u,%d)\n", mBasename
.mPos
, mBasename
.mLen
));
1094 LOG((" extension = (%u,%d)\n", mExtension
.mPos
, mExtension
.mLen
));
1095 LOG((" param = (%u,%d)\n", mParam
.mPos
, mParam
.mLen
));
1096 LOG((" query = (%u,%d)\n", mQuery
.mPos
, mQuery
.mLen
));
1097 LOG((" ref = (%u,%d)\n", mRef
.mPos
, mRef
.mLen
));
1104 nsStandardURL::SetScheme(const nsACString
&input
)
1108 const nsPromiseFlatCString
&scheme
= PromiseFlatCString(input
);
1110 LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme
.get()));
1112 if (scheme
.IsEmpty()) {
1113 NS_ERROR("cannot remove the scheme from an url");
1114 return NS_ERROR_UNEXPECTED
;
1116 if (mScheme
.mLen
< 0) {
1117 NS_ERROR("uninitialized");
1118 return NS_ERROR_NOT_INITIALIZED
;
1121 if (!net_IsValidScheme(scheme
)) {
1122 NS_ERROR("the given url scheme contains invalid characters");
1123 return NS_ERROR_UNEXPECTED
;
1128 PRInt32 shift
= ReplaceSegment(mScheme
.mPos
, mScheme
.mLen
, scheme
);
1131 mScheme
.mLen
= scheme
.Length();
1132 ShiftFromAuthority(shift
);
1135 // ensure new scheme is lowercase
1137 // XXX the string code unfortunately doesn't provide a ToLowerCase
1138 // that operates on a substring.
1139 net_ToLowerCase((char *) mSpec
.get(), mScheme
.mLen
);
1144 nsStandardURL::SetUserPass(const nsACString
&input
)
1148 const nsPromiseFlatCString
&userpass
= PromiseFlatCString(input
);
1150 LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass
.get()));
1152 if (mURLType
== URLTYPE_NO_AUTHORITY
) {
1153 if (userpass
.IsEmpty())
1155 NS_ERROR("cannot set user:pass on no-auth url");
1156 return NS_ERROR_UNEXPECTED
;
1158 if (mAuthority
.mLen
< 0) {
1159 NS_ERROR("uninitialized");
1160 return NS_ERROR_NOT_INITIALIZED
;
1165 if (userpass
.IsEmpty()) {
1167 if (mUsername
.mLen
> 0) {
1168 if (mPassword
.mLen
> 0)
1169 mUsername
.mLen
+= (mPassword
.mLen
+ 1);
1171 mSpec
.Cut(mUsername
.mPos
, mUsername
.mLen
);
1172 mAuthority
.mLen
-= mUsername
.mLen
;
1173 ShiftFromHost(-mUsername
.mLen
);
1174 mUsername
.mLen
= -1;
1175 mPassword
.mLen
= -1;
1180 NS_ASSERTION(mHost
.mLen
>= 0, "uninitialized");
1183 PRUint32 usernamePos
, passwordPos
;
1184 PRInt32 usernameLen
, passwordLen
;
1186 rv
= mParser
->ParseUserInfo(userpass
.get(), userpass
.Length(),
1187 &usernamePos
, &usernameLen
,
1188 &passwordPos
, &passwordLen
);
1189 if (NS_FAILED(rv
)) return rv
;
1191 // build new user:pass in |buf|
1193 if (usernameLen
> 0) {
1194 GET_SEGMENT_ENCODER(encoder
);
1196 usernameLen
= encoder
.EncodeSegmentCount(userpass
.get(),
1197 URLSegment(usernamePos
,
1199 esc_Username
| esc_AlwaysCopy
,
1201 if (passwordLen
>= 0) {
1203 passwordLen
= encoder
.EncodeSegmentCount(userpass
.get(),
1204 URLSegment(passwordPos
,
1207 esc_AlwaysCopy
, buf
,
1210 if (mUsername
.mLen
< 0)
1216 if (mUsername
.mLen
< 0) {
1217 // no existing user:pass
1218 if (!buf
.IsEmpty()) {
1219 mSpec
.Insert(buf
, mHost
.mPos
);
1220 mUsername
.mPos
= mHost
.mPos
;
1221 shift
= buf
.Length();
1225 // replace existing user:pass
1226 PRUint32 userpassLen
= mUsername
.mLen
;
1227 if (mPassword
.mLen
>= 0)
1228 userpassLen
+= (mPassword
.mLen
+ 1);
1229 mSpec
.Replace(mUsername
.mPos
, userpassLen
, buf
);
1230 shift
= buf
.Length() - userpassLen
;
1233 ShiftFromHost(shift
);
1234 mAuthority
.mLen
+= shift
;
1236 // update positions and lengths
1237 mUsername
.mLen
= usernameLen
;
1238 mPassword
.mLen
= passwordLen
;
1240 mPassword
.mPos
= mUsername
.mPos
+ mUsername
.mLen
+ 1;
1245 nsStandardURL::SetUsername(const nsACString
&input
)
1249 const nsPromiseFlatCString
&username
= PromiseFlatCString(input
);
1251 LOG(("nsStandardURL::SetUsername [username=%s]\n", username
.get()));
1253 if (mURLType
== URLTYPE_NO_AUTHORITY
) {
1254 if (username
.IsEmpty())
1256 NS_ERROR("cannot set username on no-auth url");
1257 return NS_ERROR_UNEXPECTED
;
1260 if (username
.IsEmpty())
1261 return SetUserPass(username
);
1265 // escape username if necessary
1267 GET_SEGMENT_ENCODER(encoder
);
1268 const nsACString
&escUsername
=
1269 encoder
.EncodeSegment(username
, esc_Username
, buf
);
1273 if (mUsername
.mLen
< 0) {
1274 mUsername
.mPos
= mAuthority
.mPos
;
1275 mSpec
.Insert(escUsername
+ NS_LITERAL_CSTRING("@"), mUsername
.mPos
);
1276 shift
= escUsername
.Length() + 1;
1279 shift
= ReplaceSegment(mUsername
.mPos
, mUsername
.mLen
, escUsername
);
1282 mUsername
.mLen
= escUsername
.Length();
1283 mAuthority
.mLen
+= shift
;
1284 ShiftFromPassword(shift
);
1290 nsStandardURL::SetPassword(const nsACString
&input
)
1294 const nsPromiseFlatCString
&password
= PromiseFlatCString(input
);
1296 LOG(("nsStandardURL::SetPassword [password=%s]\n", password
.get()));
1298 if (mURLType
== URLTYPE_NO_AUTHORITY
) {
1299 if (password
.IsEmpty())
1301 NS_ERROR("cannot set password on no-auth url");
1302 return NS_ERROR_UNEXPECTED
;
1304 if (mUsername
.mLen
<= 0) {
1305 NS_ERROR("cannot set password without existing username");
1306 return NS_ERROR_FAILURE
;
1311 if (password
.IsEmpty()) {
1312 if (mPassword
.mLen
>= 0) {
1314 mSpec
.Cut(mPassword
.mPos
- 1, mPassword
.mLen
+ 1);
1315 ShiftFromHost(-(mPassword
.mLen
+ 1));
1316 mAuthority
.mLen
-= (mPassword
.mLen
+ 1);
1317 mPassword
.mLen
= -1;
1322 // escape password if necessary
1324 GET_SEGMENT_ENCODER(encoder
);
1325 const nsACString
&escPassword
=
1326 encoder
.EncodeSegment(password
, esc_Password
, buf
);
1330 if (mPassword
.mLen
< 0) {
1331 mPassword
.mPos
= mUsername
.mPos
+ mUsername
.mLen
+ 1;
1332 mSpec
.Insert(NS_LITERAL_CSTRING(":") + escPassword
, mPassword
.mPos
- 1);
1333 shift
= escPassword
.Length() + 1;
1336 shift
= ReplaceSegment(mPassword
.mPos
, mPassword
.mLen
, escPassword
);
1339 mPassword
.mLen
= escPassword
.Length();
1340 mAuthority
.mLen
+= shift
;
1341 ShiftFromHost(shift
);
1347 nsStandardURL::SetHostPort(const nsACString
&value
)
1351 // XXX needs implementation!!
1352 NS_NOTREACHED("not implemented");
1353 return NS_ERROR_NOT_IMPLEMENTED
;
1357 nsStandardURL::SetHost(const nsACString
&input
)
1361 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
1362 const char *host
= flat
.get();
1364 LOG(("nsStandardURL::SetHost [host=%s]\n", host
));
1366 if (mURLType
== URLTYPE_NO_AUTHORITY
) {
1369 NS_WARNING("cannot set host on no-auth url");
1370 return NS_ERROR_UNEXPECTED
;
1373 if (host
&& strlen(host
) < flat
.Length())
1374 return NS_ERROR_MALFORMED_URI
; // found embedded null
1376 // For consistency with SetSpec/nsURLParsers, don't allow spaces
1378 if (strchr(host
, ' '))
1379 return NS_ERROR_MALFORMED_URI
;
1382 mHostEncoding
= eEncoding_ASCII
;
1384 if (!(host
&& *host
)) {
1385 // remove existing hostname
1386 if (mHost
.mLen
> 0) {
1387 // remove entire authority
1388 mSpec
.Cut(mAuthority
.mPos
, mAuthority
.mLen
);
1389 ShiftFromPath(-mAuthority
.mLen
);
1390 mAuthority
.mLen
= 0;
1391 mUsername
.mLen
= -1;
1392 mPassword
.mLen
= -1;
1399 // handle IPv6 unescaped address literal
1401 nsCAutoString hostBuf
;
1402 if (EscapeIPv6(host
, hostBuf
)) {
1403 host
= hostBuf
.get();
1404 len
= hostBuf
.Length();
1406 else if (NormalizeIDN(flat
, hostBuf
)) {
1407 host
= hostBuf
.get();
1408 len
= hostBuf
.Length();
1411 len
= flat
.Length();
1413 if (mHost
.mLen
< 0) {
1414 mHost
.mPos
= mAuthority
.mPos
;
1418 PRInt32 shift
= ReplaceSegment(mHost
.mPos
, mHost
.mLen
, host
, len
);
1422 mAuthority
.mLen
+= shift
;
1423 ShiftFromPath(shift
);
1426 // Now canonicalize the host to lowercase
1427 net_ToLowerCase(mSpec
.BeginWriting() + mHost
.mPos
, mHost
.mLen
);
1433 nsStandardURL::SetPort(PRInt32 port
)
1437 LOG(("nsStandardURL::SetPort [port=%d]\n", port
));
1439 if ((port
== mPort
) || (mPort
== -1 && port
== mDefaultPort
))
1442 if (mURLType
== URLTYPE_NO_AUTHORITY
) {
1443 NS_WARNING("cannot set port on no-auth url");
1444 return NS_ERROR_UNEXPECTED
;
1450 // need to insert the port number in the URL spec
1453 buf
.AppendInt(port
);
1454 mSpec
.Insert(buf
, mHost
.mPos
+ mHost
.mLen
);
1455 mAuthority
.mLen
+= buf
.Length();
1456 ShiftFromPath(buf
.Length());
1458 else if (port
== -1 || port
== mDefaultPort
) {
1459 // Don't allow mPort == mDefaultPort
1462 // need to remove the port number from the URL spec
1463 PRUint32 start
= mHost
.mPos
+ mHost
.mLen
;
1464 PRUint32 lengthToCut
= mPath
.mPos
- start
;
1465 mSpec
.Cut(start
, lengthToCut
);
1466 mAuthority
.mLen
-= lengthToCut
;
1467 ShiftFromPath(-lengthToCut
);
1470 // need to replace the existing port
1472 buf
.AppendInt(port
);
1473 PRUint32 start
= mHost
.mPos
+ mHost
.mLen
+ 1;
1474 PRUint32 length
= mPath
.mPos
- start
;
1475 mSpec
.Replace(start
, length
, buf
);
1476 if (buf
.Length() != length
) {
1477 mAuthority
.mLen
+= buf
.Length() - length
;
1478 ShiftFromPath(buf
.Length() - length
);
1487 nsStandardURL::SetPath(const nsACString
&input
)
1491 const nsPromiseFlatCString
&path
= PromiseFlatCString(input
);
1493 LOG(("nsStandardURL::SetPath [path=%s]\n", path
.get()));
1497 if (!path
.IsEmpty()) {
1500 spec
.Assign(mSpec
.get(), mPath
.mPos
);
1501 if (path
.First() != '/')
1505 return SetSpec(spec
);
1507 else if (mPath
.mLen
>= 1) {
1508 mSpec
.Cut(mPath
.mPos
+ 1, mPath
.mLen
- 1);
1509 // these contain only a '/'
1511 mDirectory
.mLen
= 1;
1513 // these are no longer defined
1514 mBasename
.mLen
= -1;
1515 mExtension
.mLen
= -1;
1524 nsStandardURL::Equals(nsIURI
*unknownOther
, PRBool
*result
)
1526 NS_ENSURE_ARG_POINTER(unknownOther
);
1527 NS_PRECONDITION(result
, "null pointer");
1529 nsRefPtr
<nsStandardURL
> other
;
1530 nsresult rv
= unknownOther
->QueryInterface(kThisImplCID
,
1531 getter_AddRefs(other
));
1532 if (NS_FAILED(rv
)) {
1537 // First, check whether one URIs is an nsIFileURL while the other
1538 // is not. If that's the case, they're different.
1539 if (mSupportsFileURL
!= other
->mSupportsFileURL
) {
1544 // Next check parts of a URI that, if different, automatically make the
1546 if (!SegmentIs(mScheme
, other
->mSpec
.get(), other
->mScheme
) ||
1547 // Check for host manually, since conversion to file will
1549 !SegmentIs(mHost
, other
->mSpec
.get(), other
->mHost
) ||
1550 !SegmentIs(mQuery
, other
->mSpec
.get(), other
->mQuery
) ||
1551 !SegmentIs(mRef
, other
->mSpec
.get(), other
->mRef
) ||
1552 !SegmentIs(mUsername
, other
->mSpec
.get(), other
->mUsername
) ||
1553 !SegmentIs(mPassword
, other
->mSpec
.get(), other
->mPassword
) ||
1554 Port() != other
->Port() ||
1555 !SegmentIs(mParam
, other
->mSpec
.get(), other
->mParam
)) {
1556 // No need to compare files or other URI parts -- these are different
1562 // Then check for exact identity of URIs. If we have it, they're equal
1563 if (SegmentIs(mDirectory
, other
->mSpec
.get(), other
->mDirectory
) &&
1564 SegmentIs(mBasename
, other
->mSpec
.get(), other
->mBasename
) &&
1565 SegmentIs(mExtension
, other
->mSpec
.get(), other
->mExtension
)) {
1570 // At this point, the URIs are not identical, but they only differ in the
1571 // directory/filename/extension. If these are file URLs, then get the
1572 // corresponding file objects and compare those, since two filenames that
1573 // differ, eg, only in case could still be equal.
1574 if (mSupportsFileURL
) {
1575 // Assume not equal for failure cases... but failures in GetFile are
1576 // really failures, more or less, so propagate them to caller.
1580 if (NS_FAILED(rv
)) {
1581 LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
1582 this, mSpec
.get()));
1585 NS_ASSERTION(mFile
, "EnsureFile() lied!");
1586 rv
= other
->EnsureFile();
1587 if (NS_FAILED(rv
)) {
1588 LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
1589 other
.get(), other
->mSpec
.get()));
1592 NS_ASSERTION(other
->mFile
, "EnsureFile() lied!");
1593 return mFile
->Equals(other
->mFile
, result
);
1596 // The URLs are not identical, and they do not correspond to the
1597 // same file, so they are different.
1604 nsStandardURL::SchemeIs(const char *scheme
, PRBool
*result
)
1606 NS_PRECONDITION(result
, "null pointer");
1608 *result
= SegmentIs(mScheme
, scheme
);
1612 /* virtual */ nsStandardURL
*
1613 nsStandardURL::StartClone()
1615 nsStandardURL
*clone
;
1616 NS_NEWXPCOM(clone
, nsStandardURL
);
1621 nsStandardURL::Clone(nsIURI
**result
)
1623 nsStandardURL
*clone
= StartClone();
1625 return NS_ERROR_OUT_OF_MEMORY
;
1627 clone
->mSpec
= mSpec
;
1628 clone
->mDefaultPort
= mDefaultPort
;
1629 clone
->mPort
= mPort
;
1630 clone
->mScheme
= mScheme
;
1631 clone
->mAuthority
= mAuthority
;
1632 clone
->mUsername
= mUsername
;
1633 clone
->mPassword
= mPassword
;
1634 clone
->mHost
= mHost
;
1635 clone
->mPath
= mPath
;
1636 clone
->mFilepath
= mFilepath
;
1637 clone
->mDirectory
= mDirectory
;
1638 clone
->mBasename
= mBasename
;
1639 clone
->mExtension
= mExtension
;
1640 clone
->mParam
= mParam
;
1641 clone
->mQuery
= mQuery
;
1643 clone
->mOriginCharset
= mOriginCharset
;
1644 clone
->mURLType
= mURLType
;
1645 clone
->mParser
= mParser
;
1646 clone
->mFile
= mFile
;
1647 clone
->mHostA
= mHostA
? nsCRT::strdup(mHostA
) : nsnull
;
1648 clone
->mMutable
= PR_TRUE
;
1649 clone
->mSupportsFileURL
= mSupportsFileURL
;
1650 clone
->mHostEncoding
= mHostEncoding
;
1651 clone
->mSpecEncoding
= mSpecEncoding
;
1653 NS_ADDREF(*result
= clone
);
1658 nsStandardURL::Resolve(const nsACString
&in
, nsACString
&out
)
1660 const nsPromiseFlatCString
&flat
= PromiseFlatCString(in
);
1661 const char *relpath
= flat
.get();
1663 // filter out unexpected chars "\r\n\t" if necessary
1666 if (net_FilterURIString(relpath
, buf
)) {
1667 relpath
= buf
.get();
1668 relpathLen
= buf
.Length();
1670 relpathLen
= flat
.Length();
1672 // XXX hack hack hack
1676 LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
1677 this, mSpec
.get(), relpath
));
1679 NS_ASSERTION(mParser
, "no parser: unitialized");
1681 // NOTE: there is no need for this function to produce normalized
1682 // output. normalization will occur when the result is used to
1683 // initialize a nsStandardURL object.
1685 if (mScheme
.mLen
< 0) {
1686 NS_ERROR("unable to Resolve URL: this URL not initialized");
1687 return NS_ERROR_NOT_INITIALIZED
;
1692 char *resultPath
= nsnull
;
1693 PRBool relative
= PR_FALSE
;
1694 PRUint32 offset
= 0;
1695 netCoalesceFlags coalesceFlag
= NET_COALESCE_NORMAL
;
1697 // relative urls should never contain a host, so we always want to use
1698 // the noauth url parser.
1699 // use it to extract a possible scheme
1700 rv
= mParser
->ParseURL(relpath
,
1702 &scheme
.mPos
, &scheme
.mLen
,
1706 // if the parser fails (for example because there is no valid scheme)
1707 // reset the scheme and assume a relative url
1708 if (NS_FAILED(rv
)) scheme
.Reset();
1710 if (scheme
.mLen
>= 0) {
1711 // add some flags to coalesceFlag if it is an ftp-url
1712 // need this later on when coalescing the resulting URL
1713 if (SegmentIs(relpath
, scheme
, "ftp", PR_TRUE
)) {
1714 coalesceFlag
= (netCoalesceFlags
) (coalesceFlag
1715 | NET_COALESCE_ALLOW_RELATIVE_ROOT
1716 | NET_COALESCE_DOUBLE_SLASH_IS_ROOT
);
1719 // this URL appears to be absolute
1720 // but try to find out more
1721 if (SegmentIs(mScheme
, relpath
, scheme
, PR_TRUE
)) {
1722 // mScheme and Scheme are the same
1723 // but this can still be relative
1724 if (nsCRT::strncmp(relpath
+ scheme
.mPos
+ scheme
.mLen
,
1726 // now this is really absolute
1727 // because a :// follows the scheme
1728 *result
= nsCRT::strdup(relpath
);
1730 // This is a deprecated form of relative urls like
1731 // http:file or http:/path/file
1732 // we will support it for now ...
1734 offset
= scheme
.mLen
+ 1;
1737 // the schemes are not the same, we are also done
1738 // because we have to assume this is absolute
1739 *result
= nsCRT::strdup(relpath
);
1742 // add some flags to coalesceFlag if it is an ftp-url
1743 // need this later on when coalescing the resulting URL
1744 if (SegmentIs(mScheme
,"ftp")) {
1745 coalesceFlag
= (netCoalesceFlags
) (coalesceFlag
1746 | NET_COALESCE_ALLOW_RELATIVE_ROOT
1747 | NET_COALESCE_DOUBLE_SLASH_IS_ROOT
);
1749 if (relpath
[0] == '/' && relpath
[1] == '/') {
1750 // this URL //host/path is almost absolute
1751 *result
= AppendToSubstring(mScheme
.mPos
, mScheme
.mLen
+ 1, relpath
);
1753 // then it must be relative
1759 const char *realrelpath
= relpath
+ offset
;
1760 switch (*realrelpath
) {
1762 // overwrite everything after the authority
1763 len
= mAuthority
.mPos
+ mAuthority
.mLen
;
1766 // overwrite the existing ?query and #ref
1767 if (mQuery
.mLen
>= 0)
1768 len
= mQuery
.mPos
- 1;
1769 else if (mRef
.mLen
>= 0)
1770 len
= mRef
.mPos
- 1;
1772 len
= mPath
.mPos
+ mPath
.mLen
;
1776 // overwrite the existing #ref
1778 len
= mPath
.mPos
+ mPath
.mLen
;
1780 len
= mRef
.mPos
- 1;
1783 if (coalesceFlag
& NET_COALESCE_DOUBLE_SLASH_IS_ROOT
) {
1784 if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
1785 nsCaseInsensitiveCStringComparator())) {
1786 // if ftp URL ends with %2F then simply
1787 // append relative part because %2F also
1788 // marks the root directory with ftp-urls
1789 len
= mFilepath
.mPos
+ mFilepath
.mLen
;
1791 // overwrite everything after the directory
1792 len
= mDirectory
.mPos
+ mDirectory
.mLen
;
1795 // overwrite everything after the directory
1796 len
= mDirectory
.mPos
+ mDirectory
.mLen
;
1799 *result
= AppendToSubstring(0, len
, realrelpath
);
1800 // locate result path
1801 resultPath
= *result
+ mPath
.mPos
;
1804 return NS_ERROR_OUT_OF_MEMORY
;
1807 net_CoalesceDirs(coalesceFlag
, resultPath
);
1809 // locate result path
1810 resultPath
= PL_strstr(*result
, "://");
1812 resultPath
= PL_strchr(resultPath
+ 3, '/');
1814 net_CoalesceDirs(coalesceFlag
,resultPath
);
1817 // XXX avoid extra copy
1823 // result may contain unescaped UTF-8 characters
1825 nsStandardURL::GetCommonBaseSpec(nsIURI
*uri2
, nsACString
&aResult
)
1827 NS_ENSURE_ARG_POINTER(uri2
);
1829 // if uri's are equal, then return uri as is
1830 PRBool isEquals
= PR_FALSE
;
1831 if (NS_SUCCEEDED(Equals(uri2
, &isEquals
)) && isEquals
)
1832 return GetSpec(aResult
);
1836 // check pre-path; if they don't match, then return empty string
1837 nsStandardURL
*stdurl2
;
1838 nsresult rv
= uri2
->QueryInterface(kThisImplCID
, (void **) &stdurl2
);
1839 isEquals
= NS_SUCCEEDED(rv
)
1840 && SegmentIs(mScheme
, stdurl2
->mSpec
.get(), stdurl2
->mScheme
)
1841 && SegmentIs(mHost
, stdurl2
->mSpec
.get(), stdurl2
->mHost
)
1842 && SegmentIs(mUsername
, stdurl2
->mSpec
.get(), stdurl2
->mUsername
)
1843 && SegmentIs(mPassword
, stdurl2
->mSpec
.get(), stdurl2
->mPassword
)
1844 && (Port() == stdurl2
->Port());
1847 if (NS_SUCCEEDED(rv
))
1848 NS_RELEASE(stdurl2
);
1852 // scan for first mismatched character
1853 const char *thisIndex
, *thatIndex
, *startCharPos
;
1854 startCharPos
= mSpec
.get() + mDirectory
.mPos
;
1855 thisIndex
= startCharPos
;
1856 thatIndex
= stdurl2
->mSpec
.get() + mDirectory
.mPos
;
1857 while ((*thisIndex
== *thatIndex
) && *thisIndex
)
1863 // backup to just after previous slash so we grab an appropriate path
1864 // segment such as a directory (not partial segments)
1865 // todo: also check for file matches which include '?', '#', and ';'
1866 while ((*(thisIndex
-1) != '/') && (thisIndex
!= startCharPos
))
1869 // grab spec from beginning to thisIndex
1870 aResult
= Substring(mSpec
, mScheme
.mPos
, thisIndex
- mSpec
.get());
1872 NS_RELEASE(stdurl2
);
1877 nsStandardURL::GetRelativeSpec(nsIURI
*uri2
, nsACString
&aResult
)
1879 NS_ENSURE_ARG_POINTER(uri2
);
1883 // if uri's are equal, then return empty string
1884 PRBool isEquals
= PR_FALSE
;
1885 if (NS_SUCCEEDED(Equals(uri2
, &isEquals
)) && isEquals
)
1888 nsStandardURL
*stdurl2
;
1889 nsresult rv
= uri2
->QueryInterface(kThisImplCID
, (void **) &stdurl2
);
1890 isEquals
= NS_SUCCEEDED(rv
)
1891 && SegmentIs(mScheme
, stdurl2
->mSpec
.get(), stdurl2
->mScheme
)
1892 && SegmentIs(mHost
, stdurl2
->mSpec
.get(), stdurl2
->mHost
)
1893 && SegmentIs(mUsername
, stdurl2
->mSpec
.get(), stdurl2
->mUsername
)
1894 && SegmentIs(mPassword
, stdurl2
->mSpec
.get(), stdurl2
->mPassword
)
1895 && (Port() == stdurl2
->Port());
1898 if (NS_SUCCEEDED(rv
))
1899 NS_RELEASE(stdurl2
);
1901 return uri2
->GetSpec(aResult
);
1904 // scan for first mismatched character
1905 const char *thisIndex
, *thatIndex
, *startCharPos
;
1906 startCharPos
= mSpec
.get() + mDirectory
.mPos
;
1907 thisIndex
= startCharPos
;
1908 thatIndex
= stdurl2
->mSpec
.get() + mDirectory
.mPos
;
1911 PRBool isFileScheme
= SegmentIs(mScheme
, "file");
1914 // on windows, we need to match the first segment of the path
1915 // if these don't match then we need to return an absolute path
1916 // skip over any leading '/' in path
1917 while ((*thisIndex
== *thatIndex
) && (*thisIndex
== '/'))
1922 // look for end of first segment
1923 while ((*thisIndex
== *thatIndex
) && *thisIndex
&& (*thisIndex
!= '/'))
1929 // if we didn't match through the first segment, return absolute path
1930 if ((*thisIndex
!= '/') || (*thatIndex
!= '/'))
1932 NS_RELEASE(stdurl2
);
1933 return uri2
->GetSpec(aResult
);
1938 while ((*thisIndex
== *thatIndex
) && *thisIndex
)
1944 // backup to just after previous slash so we grab an appropriate path
1945 // segment such as a directory (not partial segments)
1946 // todo: also check for file matches with '#', '?' and ';'
1947 while ((*(thatIndex
-1) != '/') && (thatIndex
!= startCharPos
))
1950 // need to account for slashes and add corresponding "../"
1953 if (*thisIndex
== '/')
1954 aResult
.AppendLiteral("../");
1959 // grab spec from thisIndex to end
1960 PRUint32 startPos
= stdurl2
->mScheme
.mPos
+ thatIndex
- stdurl2
->mSpec
.get();
1961 aResult
.Append(Substring(stdurl2
->mSpec
, startPos
,
1962 stdurl2
->mSpec
.Length() - startPos
));
1964 NS_RELEASE(stdurl2
);
1968 //----------------------------------------------------------------------------
1969 // nsStandardURL::nsIURL
1970 //----------------------------------------------------------------------------
1972 // result may contain unescaped UTF-8 characters
1974 nsStandardURL::GetFilePath(nsACString
&result
)
1976 result
= Filepath();
1980 // result may contain unescaped UTF-8 characters
1982 nsStandardURL::GetParam(nsACString
&result
)
1988 // result may contain unescaped UTF-8 characters
1990 nsStandardURL::GetQuery(nsACString
&result
)
1996 // result may contain unescaped UTF-8 characters
1998 nsStandardURL::GetRef(nsACString
&result
)
2004 // result may contain unescaped UTF-8 characters
2006 nsStandardURL::GetDirectory(nsACString
&result
)
2008 result
= Directory();
2012 // result may contain unescaped UTF-8 characters
2014 nsStandardURL::GetFileName(nsACString
&result
)
2016 result
= Filename();
2020 // result may contain unescaped UTF-8 characters
2022 nsStandardURL::GetFileBaseName(nsACString
&result
)
2024 result
= Basename();
2028 // result may contain unescaped UTF-8 characters
2030 nsStandardURL::GetFileExtension(nsACString
&result
)
2032 result
= Extension();
2037 nsStandardURL::SetFilePath(const nsACString
&input
)
2041 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
2042 const char *filepath
= flat
.get();
2044 LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath
));
2046 // if there isn't a filepath, then there can't be anything
2047 // after the path either. this url is likely uninitialized.
2048 if (mFilepath
.mLen
< 0)
2049 return SetPath(flat
);
2051 if (filepath
&& *filepath
) {
2053 PRUint32 dirPos
, basePos
, extPos
;
2054 PRInt32 dirLen
, baseLen
, extLen
;
2057 rv
= mParser
->ParseFilePath(filepath
, -1,
2061 if (NS_FAILED(rv
)) return rv
;
2063 // build up new candidate spec
2064 spec
.Assign(mSpec
.get(), mPath
.mPos
);
2066 // ensure leading '/'
2067 if (filepath
[dirPos
] != '/')
2070 GET_SEGMENT_ENCODER(encoder
);
2072 // append encoded filepath components
2074 encoder
.EncodeSegment(Substring(filepath
+ dirPos
,
2075 filepath
+ dirPos
+ dirLen
),
2076 esc_Directory
| esc_AlwaysCopy
, spec
);
2078 encoder
.EncodeSegment(Substring(filepath
+ basePos
,
2079 filepath
+ basePos
+ baseLen
),
2080 esc_FileBaseName
| esc_AlwaysCopy
, spec
);
2084 encoder
.EncodeSegment(Substring(filepath
+ extPos
,
2085 filepath
+ extPos
+ extLen
),
2086 esc_FileExtension
| esc_AlwaysCopy
,
2090 // compute the ending position of the current filepath
2091 if (mFilepath
.mLen
>= 0) {
2092 PRUint32 end
= mFilepath
.mPos
+ mFilepath
.mLen
;
2093 if (mSpec
.Length() > end
)
2094 spec
.Append(mSpec
.get() + end
, mSpec
.Length() - end
);
2097 return SetSpec(spec
);
2099 else if (mPath
.mLen
> 1) {
2100 mSpec
.Cut(mPath
.mPos
+ 1, mFilepath
.mLen
- 1);
2101 // left shift param, query, and ref
2102 ShiftFromParam(1 - mFilepath
.mLen
);
2103 // these contain only a '/'
2105 mDirectory
.mLen
= 1;
2107 // these are no longer defined
2108 mBasename
.mLen
= -1;
2109 mExtension
.mLen
= -1;
2115 nsStandardURL::SetParam(const nsACString
&input
)
2117 NS_NOTYETIMPLEMENTED("");
2118 return NS_ERROR_NOT_IMPLEMENTED
;
2122 nsStandardURL::SetQuery(const nsACString
&input
)
2126 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
2127 const char *query
= flat
.get();
2129 LOG(("nsStandardURL::SetQuery [query=%s]\n", query
));
2132 return SetPath(flat
);
2136 if (!query
|| !*query
) {
2137 // remove existing query
2138 if (mQuery
.mLen
>= 0) {
2139 // remove query and leading '?'
2140 mSpec
.Cut(mQuery
.mPos
- 1, mQuery
.mLen
+ 1);
2141 ShiftFromRef(-(mQuery
.mLen
+ 1));
2142 mPath
.mLen
-= (mQuery
.mLen
+ 1);
2149 PRInt32 queryLen
= strlen(query
);
2150 if (query
[0] == '?') {
2155 if (mQuery
.mLen
< 0) {
2157 mQuery
.mPos
= mSpec
.Length();
2159 mQuery
.mPos
= mRef
.mPos
- 1;
2160 mSpec
.Insert('?', mQuery
.mPos
);
2163 // the insertion pushes these out by 1
2168 // encode query if necessary
2171 GET_QUERY_ENCODER(encoder
);
2172 encoder
.EncodeSegmentCount(query
, URLSegment(0, queryLen
), esc_Query
,
2176 queryLen
= buf
.Length();
2179 PRInt32 shift
= ReplaceSegment(mQuery
.mPos
, mQuery
.mLen
, query
, queryLen
);
2182 mQuery
.mLen
= queryLen
;
2183 mPath
.mLen
+= shift
;
2184 ShiftFromRef(shift
);
2190 nsStandardURL::SetRef(const nsACString
&input
)
2194 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
2195 const char *ref
= flat
.get();
2197 LOG(("nsStandardURL::SetRef [ref=%s]\n", ref
));
2200 return SetPath(flat
);
2204 if (!ref
|| !*ref
) {
2205 // remove existing ref
2206 if (mRef
.mLen
>= 0) {
2207 // remove ref and leading '#'
2208 mSpec
.Cut(mRef
.mPos
- 1, mRef
.mLen
+ 1);
2209 mPath
.mLen
-= (mRef
.mLen
+ 1);
2216 PRInt32 refLen
= strlen(ref
);
2217 if (ref
[0] == '#') {
2222 if (mRef
.mLen
< 0) {
2224 ++mPath
.mLen
; // Include the # in the path.
2225 mRef
.mPos
= mSpec
.Length();
2229 // encode ref if necessary
2232 GET_SEGMENT_ENCODER(encoder
);
2233 encoder
.EncodeSegmentCount(ref
, URLSegment(0, refLen
), esc_Ref
,
2237 refLen
= buf
.Length();
2240 ReplaceSegment(mRef
.mPos
, mRef
.mLen
, ref
, refLen
);
2241 mPath
.mLen
+= (refLen
- mRef
.mLen
);
2247 nsStandardURL::SetDirectory(const nsACString
&input
)
2249 NS_NOTYETIMPLEMENTED("");
2250 return NS_ERROR_NOT_IMPLEMENTED
;
2254 nsStandardURL::SetFileName(const nsACString
&input
)
2258 const nsPromiseFlatCString
&flat
= PromiseFlatCString(input
);
2259 const char *filename
= flat
.get();
2261 LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename
));
2264 return SetPath(flat
);
2268 if (!(filename
&& *filename
)) {
2269 // remove the filename
2270 if (mBasename
.mLen
> 0) {
2271 if (mExtension
.mLen
>= 0)
2272 mBasename
.mLen
+= (mExtension
.mLen
+ 1);
2273 mSpec
.Cut(mBasename
.mPos
, mBasename
.mLen
);
2274 shift
= -mBasename
.mLen
;
2276 mExtension
.mLen
= -1;
2281 URLSegment basename
, extension
;
2283 // let the parser locate the basename and extension
2284 rv
= mParser
->ParseFileName(filename
, -1,
2285 &basename
.mPos
, &basename
.mLen
,
2286 &extension
.mPos
, &extension
.mLen
);
2287 if (NS_FAILED(rv
)) return rv
;
2289 if (basename
.mLen
< 0) {
2290 // remove existing filename
2291 if (mBasename
.mLen
>= 0) {
2292 PRUint32 len
= mBasename
.mLen
;
2293 if (mExtension
.mLen
>= 0)
2294 len
+= (mExtension
.mLen
+ 1);
2295 mSpec
.Cut(mBasename
.mPos
, len
);
2296 shift
= -PRInt32(len
);
2298 mExtension
.mLen
= -1;
2302 nsCAutoString newFilename
;
2304 GET_SEGMENT_ENCODER(encoder
);
2305 basename
.mLen
= encoder
.EncodeSegmentCount(filename
, basename
,
2310 if (extension
.mLen
>= 0) {
2311 newFilename
.Append('.');
2312 extension
.mLen
= encoder
.EncodeSegmentCount(filename
, extension
,
2319 if (mBasename
.mLen
< 0) {
2320 // insert new filename
2321 mBasename
.mPos
= mDirectory
.mPos
+ mDirectory
.mLen
;
2322 mSpec
.Insert(newFilename
, mBasename
.mPos
);
2323 shift
= newFilename
.Length();
2326 // replace existing filename
2327 PRUint32 oldLen
= PRUint32(mBasename
.mLen
);
2328 if (mExtension
.mLen
>= 0)
2329 oldLen
+= (mExtension
.mLen
+ 1);
2330 mSpec
.Replace(mBasename
.mPos
, oldLen
, newFilename
);
2331 shift
= newFilename
.Length() - oldLen
;
2334 mBasename
.mLen
= basename
.mLen
;
2335 mExtension
.mLen
= extension
.mLen
;
2336 if (mExtension
.mLen
>= 0)
2337 mExtension
.mPos
= mBasename
.mPos
+ mBasename
.mLen
+ 1;
2341 ShiftFromParam(shift
);
2342 mFilepath
.mLen
+= shift
;
2343 mPath
.mLen
+= shift
;
2349 nsStandardURL::SetFileBaseName(const nsACString
&input
)
2351 nsCAutoString extension
;
2352 nsresult rv
= GetFileExtension(extension
);
2353 NS_ENSURE_SUCCESS(rv
, rv
);
2355 nsCAutoString
newFileName(input
);
2357 if (!extension
.IsEmpty()) {
2358 newFileName
.Append('.');
2359 newFileName
.Append(extension
);
2362 return SetFileName(newFileName
);
2366 nsStandardURL::SetFileExtension(const nsACString
&input
)
2368 nsCAutoString newFileName
;
2369 nsresult rv
= GetFileBaseName(newFileName
);
2370 NS_ENSURE_SUCCESS(rv
, rv
);
2372 if (!input
.IsEmpty()) {
2373 newFileName
.Append('.');
2374 newFileName
.Append(input
);
2377 return SetFileName(newFileName
);
2380 //----------------------------------------------------------------------------
2381 // nsStandardURL::nsIFileURL
2382 //----------------------------------------------------------------------------
2385 nsStandardURL::EnsureFile()
2387 NS_PRECONDITION(mSupportsFileURL
,
2388 "EnsureFile() called on a URL that doesn't support files!");
2394 // Parse the spec if we don't have a cached result
2395 if (mSpec
.IsEmpty()) {
2396 NS_ERROR("url not initialized");
2397 return NS_ERROR_NOT_INITIALIZED
;
2400 if (!SegmentIs(mScheme
, "file")) {
2401 NS_ERROR("not a file URL");
2402 return NS_ERROR_FAILURE
;
2405 return net_GetFileFromURLSpec(mSpec
, getter_AddRefs(mFile
));
2409 nsStandardURL::GetFile(nsIFile
**result
)
2411 NS_PRECONDITION(mSupportsFileURL
,
2412 "GetFile() called on a URL that doesn't support files!");
2413 nsresult rv
= EnsureFile();
2417 #if defined(PR_LOGGING)
2418 if (LOG_ENABLED()) {
2420 mFile
->GetNativePath(path
);
2421 LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
2422 this, mSpec
.get(), path
.get()));
2426 // clone the file, so the caller can modify it.
2427 // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
2428 // nsIFile returned from this method; but it seems that some folks do
2429 // (see bug 161921). until we can be sure that all the consumers are
2430 // behaving themselves, we'll stay on the safe side and clone the file.
2431 // see bug 212724 about fixing the consumers.
2432 return mFile
->Clone(result
);
2436 nsStandardURL::SetFile(nsIFile
*file
)
2440 NS_ENSURE_ARG_POINTER(file
);
2445 rv
= net_GetURLSpecFromFile(file
, url
);
2446 if (NS_FAILED(rv
)) return rv
;
2450 rv
= Init(mURLType
, mDefaultPort
, url
, nsnull
, nsnull
);
2452 // must clone |file| since its value is not guaranteed to remain constant
2453 if (NS_SUCCEEDED(rv
)) {
2455 if (NS_FAILED(file
->Clone(getter_AddRefs(mFile
)))) {
2456 NS_WARNING("nsIFile::Clone failed");
2457 // failure to clone is not fatal (GetFile will generate mFile)
2464 //----------------------------------------------------------------------------
2465 // nsStandardURL::nsIStandardURL
2466 //----------------------------------------------------------------------------
2469 IsUTFCharset(const char *aCharset
)
2471 return ((aCharset
[0] == 'U' || aCharset
[0] == 'u') &&
2472 (aCharset
[1] == 'T' || aCharset
[1] == 't') &&
2473 (aCharset
[2] == 'F' || aCharset
[2] == 'f'));
2477 nsStandardURL::Init(PRUint32 urlType
,
2478 PRInt32 defaultPort
,
2479 const nsACString
&spec
,
2480 const char *charset
,
2488 case URLTYPE_STANDARD
:
2489 mParser
= net_GetStdURLParser();
2491 case URLTYPE_AUTHORITY
:
2492 mParser
= net_GetAuthURLParser();
2494 case URLTYPE_NO_AUTHORITY
:
2495 mParser
= net_GetNoAuthURLParser();
2498 NS_NOTREACHED("bad urlType");
2499 return NS_ERROR_INVALID_ARG
;
2501 mDefaultPort
= defaultPort
;
2504 mOriginCharset
.Truncate();
2506 if (charset
== nsnull
|| *charset
== '\0') {
2507 // check if baseURI provides an origin charset and use that.
2509 baseURI
->GetOriginCharset(mOriginCharset
);
2511 // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32,
2512 // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if
2513 // it starts with "utf" (since an empty mOriginCharset implies
2514 // UTF-8, this is safe even if mOriginCharset is UTF-8).
2516 if (mOriginCharset
.Length() > 3 &&
2517 IsUTFCharset(mOriginCharset
.get())) {
2518 mOriginCharset
.Truncate();
2521 else if (!IsUTFCharset(charset
)) {
2522 mOriginCharset
= charset
;
2526 PRUint32 start
, end
;
2527 // pull out the scheme and where it ends
2528 nsresult rv
= net_ExtractURLScheme(spec
, &start
, &end
, nsnull
);
2529 if (NS_SUCCEEDED(rv
) && spec
.Length() > end
+2) {
2530 nsACString::const_iterator slash
;
2531 spec
.BeginReading(slash
);
2532 slash
.advance(end
+1);
2533 // then check if // follows
2534 // if it follows, aSpec is really absolute ...
2535 // ignore aBaseURI in this case
2536 if (*slash
== '/' && *(++slash
) == '/')
2542 return SetSpec(spec
);
2545 nsresult rv
= baseURI
->Resolve(spec
, buf
);
2546 if (NS_FAILED(rv
)) return rv
;
2548 return SetSpec(buf
);
2552 nsStandardURL::GetMutable(PRBool
*value
)
2559 nsStandardURL::SetMutable(PRBool value
)
2561 NS_ENSURE_ARG(mMutable
|| !value
);
2567 //----------------------------------------------------------------------------
2568 // nsStandardURL::nsISerializable
2569 //----------------------------------------------------------------------------
2572 nsStandardURL::Read(nsIObjectInputStream
*stream
)
2574 NS_PRECONDITION(!mHostA
, "Shouldn't have cached ASCII host");
2575 NS_PRECONDITION(mSpecEncoding
== eEncoding_Unknown
,
2576 "Shouldn't have spec encoding here");
2581 rv
= stream
->Read32(&urlType
);
2582 if (NS_FAILED(rv
)) return rv
;
2585 case URLTYPE_STANDARD
:
2586 mParser
= net_GetStdURLParser();
2588 case URLTYPE_AUTHORITY
:
2589 mParser
= net_GetAuthURLParser();
2591 case URLTYPE_NO_AUTHORITY
:
2592 mParser
= net_GetNoAuthURLParser();
2595 NS_NOTREACHED("bad urlType");
2596 return NS_ERROR_FAILURE
;
2599 rv
= stream
->Read32((PRUint32
*) &mPort
);
2600 if (NS_FAILED(rv
)) return rv
;
2602 rv
= stream
->Read32((PRUint32
*) &mDefaultPort
);
2603 if (NS_FAILED(rv
)) return rv
;
2605 rv
= NS_ReadOptionalCString(stream
, mSpec
);
2606 if (NS_FAILED(rv
)) return rv
;
2608 rv
= ReadSegment(stream
, mScheme
);
2609 if (NS_FAILED(rv
)) return rv
;
2611 rv
= ReadSegment(stream
, mAuthority
);
2612 if (NS_FAILED(rv
)) return rv
;
2614 rv
= ReadSegment(stream
, mUsername
);
2615 if (NS_FAILED(rv
)) return rv
;
2617 rv
= ReadSegment(stream
, mPassword
);
2618 if (NS_FAILED(rv
)) return rv
;
2620 rv
= ReadSegment(stream
, mHost
);
2621 if (NS_FAILED(rv
)) return rv
;
2623 rv
= ReadSegment(stream
, mPath
);
2624 if (NS_FAILED(rv
)) return rv
;
2626 rv
= ReadSegment(stream
, mFilepath
);
2627 if (NS_FAILED(rv
)) return rv
;
2629 rv
= ReadSegment(stream
, mDirectory
);
2630 if (NS_FAILED(rv
)) return rv
;
2632 rv
= ReadSegment(stream
, mBasename
);
2633 if (NS_FAILED(rv
)) return rv
;
2635 rv
= ReadSegment(stream
, mExtension
);
2636 if (NS_FAILED(rv
)) return rv
;
2638 rv
= ReadSegment(stream
, mParam
);
2639 if (NS_FAILED(rv
)) return rv
;
2641 rv
= ReadSegment(stream
, mQuery
);
2642 if (NS_FAILED(rv
)) return rv
;
2644 rv
= ReadSegment(stream
, mRef
);
2645 if (NS_FAILED(rv
)) return rv
;
2647 rv
= NS_ReadOptionalCString(stream
, mOriginCharset
);
2648 if (NS_FAILED(rv
)) return rv
;
2651 rv
= stream
->ReadBoolean(&isMutable
);
2652 if (NS_FAILED(rv
)) return rv
;
2653 if (isMutable
!= PR_TRUE
&& isMutable
!= PR_FALSE
) {
2654 NS_WARNING("Unexpected boolean value");
2655 return NS_ERROR_UNEXPECTED
;
2657 mMutable
= isMutable
;
2659 PRBool supportsFileURL
;
2660 rv
= stream
->ReadBoolean(&supportsFileURL
);
2661 if (NS_FAILED(rv
)) return rv
;
2662 if (supportsFileURL
!= PR_TRUE
&& supportsFileURL
!= PR_FALSE
) {
2663 NS_WARNING("Unexpected boolean value");
2664 return NS_ERROR_UNEXPECTED
;
2666 mSupportsFileURL
= supportsFileURL
;
2668 PRUint32 hostEncoding
;
2669 rv
= stream
->Read32(&hostEncoding
);
2670 if (NS_FAILED(rv
)) return rv
;
2671 if (hostEncoding
!= eEncoding_ASCII
&& hostEncoding
!= eEncoding_UTF8
) {
2672 NS_WARNING("Unexpected host encoding");
2673 return NS_ERROR_UNEXPECTED
;
2675 mHostEncoding
= hostEncoding
;
2681 nsStandardURL::Write(nsIObjectOutputStream
*stream
)
2685 rv
= stream
->Write32(mURLType
);
2686 if (NS_FAILED(rv
)) return rv
;
2688 rv
= stream
->Write32(PRUint32(mPort
));
2689 if (NS_FAILED(rv
)) return rv
;
2691 rv
= stream
->Write32(PRUint32(mDefaultPort
));
2692 if (NS_FAILED(rv
)) return rv
;
2694 rv
= NS_WriteOptionalStringZ(stream
, mSpec
.get());
2695 if (NS_FAILED(rv
)) return rv
;
2697 rv
= WriteSegment(stream
, mScheme
);
2698 if (NS_FAILED(rv
)) return rv
;
2700 rv
= WriteSegment(stream
, mAuthority
);
2701 if (NS_FAILED(rv
)) return rv
;
2703 rv
= WriteSegment(stream
, mUsername
);
2704 if (NS_FAILED(rv
)) return rv
;
2706 rv
= WriteSegment(stream
, mPassword
);
2707 if (NS_FAILED(rv
)) return rv
;
2709 rv
= WriteSegment(stream
, mHost
);
2710 if (NS_FAILED(rv
)) return rv
;
2712 rv
= WriteSegment(stream
, mPath
);
2713 if (NS_FAILED(rv
)) return rv
;
2715 rv
= WriteSegment(stream
, mFilepath
);
2716 if (NS_FAILED(rv
)) return rv
;
2718 rv
= WriteSegment(stream
, mDirectory
);
2719 if (NS_FAILED(rv
)) return rv
;
2721 rv
= WriteSegment(stream
, mBasename
);
2722 if (NS_FAILED(rv
)) return rv
;
2724 rv
= WriteSegment(stream
, mExtension
);
2725 if (NS_FAILED(rv
)) return rv
;
2727 rv
= WriteSegment(stream
, mParam
);
2728 if (NS_FAILED(rv
)) return rv
;
2730 rv
= WriteSegment(stream
, mQuery
);
2731 if (NS_FAILED(rv
)) return rv
;
2733 rv
= WriteSegment(stream
, mRef
);
2734 if (NS_FAILED(rv
)) return rv
;
2736 rv
= NS_WriteOptionalStringZ(stream
, mOriginCharset
.get());
2737 if (NS_FAILED(rv
)) return rv
;
2739 rv
= stream
->WriteBoolean(mMutable
);
2740 if (NS_FAILED(rv
)) return rv
;
2742 rv
= stream
->WriteBoolean(mSupportsFileURL
);
2743 if (NS_FAILED(rv
)) return rv
;
2745 rv
= stream
->Write32(mHostEncoding
);
2746 if (NS_FAILED(rv
)) return rv
;
2748 // mSpecEncoding and mHostA are just caches that can be recovered as needed.
2753 //----------------------------------------------------------------------------
2754 // nsStandardURL::nsIClassInfo
2755 //----------------------------------------------------------------------------
2758 nsStandardURL::GetInterfaces(PRUint32
*count
, nsIID
* **array
)
2766 nsStandardURL::GetHelperForLanguage(PRUint32 language
, nsISupports
**_retval
)
2773 nsStandardURL::GetContractID(char * *aContractID
)
2775 *aContractID
= nsnull
;
2780 nsStandardURL::GetClassDescription(char * *aClassDescription
)
2782 *aClassDescription
= nsnull
;
2787 nsStandardURL::GetClassID(nsCID
* *aClassID
)
2789 *aClassID
= (nsCID
*) nsMemory::Alloc(sizeof(nsCID
));
2791 return NS_ERROR_OUT_OF_MEMORY
;
2792 return GetClassIDNoAlloc(*aClassID
);
2796 nsStandardURL::GetImplementationLanguage(PRUint32
*aImplementationLanguage
)
2798 *aImplementationLanguage
= nsIProgrammingLanguage::CPLUSPLUS
;
2803 nsStandardURL::GetFlags(PRUint32
*aFlags
)
2805 *aFlags
= nsIClassInfo::MAIN_THREAD_ONLY
;
2810 nsStandardURL::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
2812 *aClassIDNoAlloc
= kStandardURLCID
;