Bug 458256. Use LoadLibraryW instead of LoadLibrary (patch by DougT). r+sr=vlad
[wine-gecko.git] / netwerk / base / src / nsStandardURL.cpp
blobb7c12f4c336402f001397110daabadd59ba394f6
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
14 * License.
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.
23 * Contributor(s):
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"
44 #include "nsCRT.h"
45 #include "nsEscape.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"
55 #include "prlog.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;
74 #endif
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() \
81 PR_BEGIN_MACRO \
82 if (!mMutable) { \
83 NS_ERROR("attempt to modify an immutable nsStandardURL"); \
84 return NS_ERROR_ABORT; \
85 } \
86 PR_END_MACRO
88 //----------------------------------------------------------------------------
90 static nsresult
91 EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
93 nsresult rv;
94 PRInt32 len = str.Length();
95 PRInt32 maxlen;
97 rv = encoder->GetMaxLength(str.get(), len, &maxlen);
98 if (NS_FAILED(rv))
99 return rv;
101 char buf[256], *p = buf;
102 if (PRUint32(maxlen) > sizeof(buf) - 1) {
103 p = (char *) malloc(maxlen + 1);
104 if (!p)
105 return NS_ERROR_OUT_OF_MEMORY;
108 rv = encoder->Convert(str.get(), &len, p, &maxlen);
109 if (NS_FAILED(rv))
110 goto end;
111 if (rv == NS_ERROR_UENC_NOMAPPING) {
112 NS_WARNING("unicode conversion failed");
113 rv = NS_ERROR_UNEXPECTED;
114 goto end;
116 p[maxlen] = 0;
117 result.Assign(p);
119 len = sizeof(buf) - 1;
120 rv = encoder->Finish(buf, &len);
121 if (NS_FAILED(rv))
122 goto end;
123 buf[len] = 0;
124 result.Append(buf);
126 end:
127 encoder->Reset();
129 if (p != buf)
130 free(p);
131 return rv;
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,
147 const char *topic,
148 const PRUnichar *data)
150 if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
151 nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
152 if (prefBranch) {
153 PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
156 return NS_OK;
159 //----------------------------------------------------------------------------
160 // nsStandardURL::nsSegmentEncoder
161 //----------------------------------------------------------------------------
163 nsStandardURL::
164 nsSegmentEncoder::nsSegmentEncoder(const char *charset)
165 : mCharset(charset)
169 PRInt32 nsStandardURL::
170 nsSegmentEncoder::EncodeSegmentCount(const char *str,
171 const URLSegment &seg,
172 PRInt16 mask,
173 nsAFlatCString &result,
174 PRBool &appended)
176 appended = PR_FALSE;
177 if (!str)
178 return 0;
179 PRInt32 len = 0;
180 if (seg.mLen > 0) {
181 PRUint32 pos = seg.mPos;
182 len = seg.mLen;
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
187 // is nothing to do.
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))) {
194 str = encBuf.get();
195 pos = 0;
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;
210 appended = PR_TRUE;
212 else if (str == encBuf.get()) {
213 result += encBuf; // append only!!
214 len = encBuf.Length();
215 appended = PR_TRUE;
218 return len;
221 const nsACString &nsStandardURL::
222 nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
223 PRInt16 mask,
224 nsAFlatCString &result)
226 const char *text;
227 PRBool encoded;
228 EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
229 if (encoded)
230 return result;
231 return str;
234 PRBool nsStandardURL::
235 nsSegmentEncoder::InitUnicodeEncoder()
237 NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!");
238 nsresult rv;
239 if (!gCharsetMgr) {
240 rv = CallGetService("@mozilla.org/charset-converter-manager;1",
241 &gCharsetMgr);
242 if (NS_FAILED(rv)) {
243 NS_ERROR("failed to get charset-converter-manager");
244 return PR_FALSE;
248 rv = gCharsetMgr->GetUnicodeEncoder(mCharset, getter_AddRefs(mEncoder));
249 if (NS_FAILED(rv)) {
250 NS_ERROR("failed to get unicode encoder");
251 mEncoder = 0; // just in case
252 return PR_FALSE;
255 return PR_TRUE;
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 && \
266 gEncodeQueryInUTF8)
268 //----------------------------------------------------------------------------
269 // nsStandardURL <public>
270 //----------------------------------------------------------------------------
272 nsStandardURL::nsStandardURL(PRBool aSupportsFileURL)
273 : mDefaultPort(-1)
274 , mPort(-1)
275 , mHostA(nsnull)
276 , mHostEncoding(eEncoding_ASCII)
277 , mSpecEncoding(eEncoding_Unknown)
278 , mURLType(URLTYPE_STANDARD)
279 , mMutable(PR_TRUE)
280 , mSupportsFileURL(aSupportsFileURL)
282 #if defined(PR_LOGGING)
283 if (!gStandardURLLog)
284 gStandardURLLog = PR_NewLogModule("nsStandardURL");
285 #endif
287 LOG(("Creating nsStandardURL @%p\n", this));
289 if (!gInitialized) {
290 gInitialized = PR_TRUE;
291 InitGlobalObjects();
294 // default parser in case nsIStandardURL::Init is never called
295 mParser = net_GetStdURLParser();
298 nsStandardURL::~nsStandardURL()
300 LOG(("Destroying nsStandardURL @%p\n", this));
302 CRTFREEIF(mHostA);
305 void
306 nsStandardURL::InitGlobalObjects()
308 nsCOMPtr<nsIPrefBranch2> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
309 if (prefBranch) {
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);
320 void
321 nsStandardURL::ShutdownGlobalObjects()
323 NS_IF_RELEASE(gIDN);
324 NS_IF_RELEASE(gCharsetMgr);
327 //----------------------------------------------------------------------------
328 // nsStandardURL <private>
329 //----------------------------------------------------------------------------
331 void
332 nsStandardURL::Clear()
334 mSpec.Truncate();
336 mPort = -1;
338 mAuthority.Reset();
339 mUsername.Reset();
340 mPassword.Reset();
341 mHost.Reset();
342 mHostEncoding = eEncoding_ASCII;
344 mPath.Reset();
345 mFilepath.Reset();
346 mDirectory.Reset();
347 mBasename.Reset();
349 mExtension.Reset();
350 mParam.Reset();
351 mQuery.Reset();
352 mRef.Reset();
354 InvalidateCache();
357 void
358 nsStandardURL::InvalidateCache(PRBool invalidateCachedFile)
360 if (invalidateCachedFile)
361 mFile = 0;
362 CRTFREEIF(mHostA);
363 mSpecEncoding = eEncoding_Unknown;
366 PRBool
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, ':')) {
371 result.Assign('[');
372 result.Append(host);
373 result.Append(']');
374 return PR_TRUE;
376 return PR_FALSE;
379 PRBool
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");
395 PRBool isASCII;
396 if (gIDN &&
397 NS_SUCCEEDED(gIDN->ConvertToDisplayIDN(host, &isASCII, result))) {
398 if (!isASCII)
399 mHostEncoding = eEncoding_UTF8;
401 return PR_TRUE;
404 result.Truncate();
405 return PR_FALSE;
408 void
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;
415 mPath.mLen = newLen;
416 mDirectory.mLen += diff;
417 mFilepath.mLen += diff;
418 ShiftFromBasename(diff);
422 PRUint32
423 nsStandardURL::AppendSegmentToBuf(char *buf, PRUint32 i, const char *str, URLSegment &seg, const nsCString *escapedStr, PRBool useEscaped)
425 if (seg.mLen > 0) {
426 if (useEscaped) {
427 seg.mLen = escapedStr->Length();
428 memcpy(buf + i, escapedStr->get(), seg.mLen);
430 else
431 memcpy(buf + i, str + seg.mPos, seg.mLen);
432 seg.mPos = i;
433 i += seg.mLen;
435 return i;
438 PRUint32
439 nsStandardURL::AppendToBuf(char *buf, PRUint32 i, const char *str, PRUint32 len)
441 memcpy(buf + i, str, len);
442 return i + len;
445 // basic algorithm:
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
450 nsresult
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
465 // spec length.
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();
502 else
503 approxLen += mHost.mLen;
507 // generate the normalized URL string
509 if (!EnsureStringLength(mSpec, approxLen + 32))
510 return NS_ERROR_OUT_OF_MEMORY;
511 char *buf;
512 mSpec.BeginWriting(buf);
513 PRUint32 i = 0;
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
522 mAuthority.mPos = i;
524 // append authority
525 if (mUsername.mLen > 0) {
526 i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername);
527 if (mPassword.mLen >= 0) {
528 buf[i++] = ':';
529 i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword);
531 buf[i++] = '@';
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);
539 buf[i++] = ':';
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;
554 mBasename.mLen = 0;
555 buf[i++] = '/';
557 else {
558 PRUint32 leadingSlash = 0;
559 if (spec[mPath.mPos] != '/') {
560 LOG(("adding leading slash to path\n"));
561 leadingSlash = 1;
562 buf[i++] = '/';
563 // basename must exist, even if empty (bugs 113508, 429347)
564 if (mBasename.mLen == -1) {
565 mBasename.mPos = i;
566 mBasename.mLen = 0;
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] != '/') {
577 buf[i++] = '/';
578 mDirectory.mLen++;
581 i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename);
583 // make corrections to directory segment if leadingSlash
584 if (leadingSlash) {
585 mDirectory.mPos = mPath.mPos;
586 if (mDirectory.mLen >= 0)
587 mDirectory.mLen += leadingSlash;
588 else
589 mDirectory.mLen = 1;
592 if (mExtension.mLen >= 0) {
593 buf[i++] = '.';
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) {
600 buf[i++] = ';';
601 i = AppendSegmentToBuf(buf, i, spec, mParam, &encParam, useEncParam);
603 if (mQuery.mLen >= 0) {
604 buf[i++] = '?';
605 i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery);
607 if (mRef.mLen >= 0) {
608 buf[i++] = '#';
609 i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef);
611 // calculate corrected path length
612 mPath.mLen = i - mPath.mPos;
615 buf[i] = '\0';
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!");
628 return NS_OK;
631 PRBool
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));
637 if (seg.mLen < 0)
638 return PR_FALSE;
639 // if the first |seg.mLen| chars of |val| match, then |val| must
640 // also be null terminated at |seg.mLen|.
641 if (ignoreCase)
642 return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
643 && (val[seg.mLen] == '\0');
644 else
645 return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
646 && (val[seg.mLen] == '\0');
649 PRBool
650 nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, PRBool ignoreCase)
652 // one or both may be null
653 if (!val || !spec)
654 return (!val && (!spec || seg.mLen < 0));
655 if (seg.mLen < 0)
656 return PR_FALSE;
657 // if the first |seg.mLen| chars of |val| match, then |val| must
658 // also be null terminated at |seg.mLen|.
659 if (ignoreCase)
660 return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
661 && (val[seg.mLen] == '\0');
662 else
663 return !strncmp(spec + seg.mPos, val, seg.mLen)
664 && (val[seg.mLen] == '\0');
667 PRBool
668 nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, PRBool ignoreCase)
670 if (seg1.mLen != seg2.mLen)
671 return PR_FALSE;
672 if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
673 return PR_TRUE; // both are empty
674 if (ignoreCase)
675 return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
676 else
677 return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
680 PRInt32
681 nsStandardURL::ReplaceSegment(PRUint32 pos, PRUint32 len, const char *val, PRUint32 valLen)
683 if (val && valLen) {
684 if (len == 0)
685 mSpec.Insert(val, pos, valLen);
686 else
687 mSpec.Replace(pos, len, nsDependentCString(val, valLen));
688 return valLen - len;
691 // else remove the specified segment
692 mSpec.Cut(pos, len);
693 return -PRInt32(len);
696 PRInt32
697 nsStandardURL::ReplaceSegment(PRUint32 pos, PRUint32 len, const nsACString &val)
699 if (len == 0)
700 mSpec.Insert(val, pos);
701 else
702 mSpec.Replace(pos, len, val);
703 return val.Length() - len;
706 nsresult
707 nsStandardURL::ParseURL(const char *spec, PRInt32 specLen)
709 nsresult rv;
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;
720 #ifdef DEBUG
721 if (mScheme.mLen <= 0) {
722 printf("spec=%s\n", spec);
723 NS_WARNING("malformed url: no scheme");
725 #endif
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,
732 &mPort);
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)
737 mPort = -1;
739 mUsername.mPos += mAuthority.mPos;
740 mPassword.mPos += mAuthority.mPos;
741 mHost.mPos += mAuthority.mPos;
744 if (mPath.mLen > 0)
745 rv = ParsePath(spec, mPath.mPos, mPath.mLen);
747 return rv;
750 nsresult
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;
776 return NS_OK;
779 char *
780 nsStandardURL::AppendToSubstring(PRUint32 pos,
781 PRInt32 len,
782 const char *tail,
783 PRInt32 tailLen)
785 if (tailLen < 0)
786 tailLen = strlen(tail);
788 char *result = (char *) malloc(len + tailLen + 1);
789 if (result) {
790 memcpy(result, mSpec.get() + pos, len);
791 memcpy(result + len, tail, tailLen);
792 result[len + tailLen] = '\0';
794 return result;
797 nsresult
798 nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
800 nsresult rv;
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;
808 return NS_OK;
811 nsresult
812 nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
814 nsresult rv;
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;
822 return NS_OK;
825 /* static */ void
826 nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
828 PRBool val;
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)) {
836 NS_IF_RELEASE(gIDN);
837 if (GOT_PREF(NS_NET_PREF_ENABLEIDN, val) && val) {
838 // initialize IDN
839 nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
840 if (serv)
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))
848 gEscapeUTF8 = 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"));
863 #undef PREF_CHANGED
864 #undef GOT_PREF
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);
886 else
887 NS_INTERFACE_MAP_END
889 //----------------------------------------------------------------------------
890 // nsStandardURL::nsIURI
891 //----------------------------------------------------------------------------
893 // result may contain unescaped UTF-8 characters
894 NS_IMETHODIMP
895 nsStandardURL::GetSpec(nsACString &result)
897 result = mSpec;
898 return NS_OK;
901 // result may contain unescaped UTF-8 characters
902 NS_IMETHODIMP
903 nsStandardURL::GetPrePath(nsACString &result)
905 result = Prepath();
906 return NS_OK;
909 // result is strictly US-ASCII
910 NS_IMETHODIMP
911 nsStandardURL::GetScheme(nsACString &result)
913 result = Scheme();
914 return NS_OK;
917 // result may contain unescaped UTF-8 characters
918 NS_IMETHODIMP
919 nsStandardURL::GetUserPass(nsACString &result)
921 result = Userpass();
922 return NS_OK;
925 // result may contain unescaped UTF-8 characters
926 NS_IMETHODIMP
927 nsStandardURL::GetUsername(nsACString &result)
929 result = Username();
930 return NS_OK;
933 // result may contain unescaped UTF-8 characters
934 NS_IMETHODIMP
935 nsStandardURL::GetPassword(nsACString &result)
937 result = Password();
938 return NS_OK;
941 NS_IMETHODIMP
942 nsStandardURL::GetHostPort(nsACString &result)
944 result = Hostport();
945 return NS_OK;
948 NS_IMETHODIMP
949 nsStandardURL::GetHost(nsACString &result)
951 result = Host();
952 return NS_OK;
955 NS_IMETHODIMP
956 nsStandardURL::GetPort(PRInt32 *result)
958 *result = mPort;
959 return NS_OK;
962 // result may contain unescaped UTF-8 characters
963 NS_IMETHODIMP
964 nsStandardURL::GetPath(nsACString &result)
966 result = Path();
967 return NS_OK;
970 // result is ASCII
971 NS_IMETHODIMP
972 nsStandardURL::GetAsciiSpec(nsACString &result)
974 if (mSpecEncoding == eEncoding_Unknown) {
975 if (IsASCII(mSpec))
976 mSpecEncoding = eEncoding_ASCII;
977 else
978 mSpecEncoding = eEncoding_UTF8;
981 if (mSpecEncoding == eEncoding_ASCII) {
982 result = mSpec;
983 return NS_OK;
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);
993 // get escaped host
994 nsCAutoString escHostport;
995 if (mHost.mLen > 0) {
996 // this doesn't fail
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);
1007 return NS_OK;
1010 // result is ASCII
1011 NS_IMETHODIMP
1012 nsStandardURL::GetAsciiHost(nsACString &result)
1014 if (mHostEncoding == eEncoding_ASCII) {
1015 result = Host();
1016 return NS_OK;
1019 // perhaps we have it cached...
1020 if (mHostA) {
1021 result = mHostA;
1022 return NS_OK;
1025 if (gIDN) {
1026 nsresult rv;
1027 rv = gIDN->ConvertUTF8toACE(Host(), result);
1028 if (NS_SUCCEEDED(rv)) {
1029 mHostA = ToNewCString(result);
1030 return NS_OK;
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);
1037 return NS_OK;
1040 NS_IMETHODIMP
1041 nsStandardURL::GetOriginCharset(nsACString &result)
1043 if (mOriginCharset.IsEmpty())
1044 result.AssignLiteral("UTF-8");
1045 else
1046 result = mOriginCharset;
1047 return NS_OK;
1050 NS_IMETHODIMP
1051 nsStandardURL::SetSpec(const nsACString &input)
1053 ENSURE_MUTABLE();
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));
1061 Clear();
1063 if (!spec || !*spec)
1064 return NS_OK;
1066 // filter out unexpected chars "\r\n\t" if necessary
1067 nsCAutoString buf1;
1068 if (net_FilterURIString(spec, buf1)) {
1069 spec = buf1.get();
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
1078 // copy of |spec|
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));
1099 #endif
1100 return rv;
1103 NS_IMETHODIMP
1104 nsStandardURL::SetScheme(const nsACString &input)
1106 ENSURE_MUTABLE();
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;
1126 InvalidateCache();
1128 PRInt32 shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
1130 if (shift) {
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);
1140 return NS_OK;
1143 NS_IMETHODIMP
1144 nsStandardURL::SetUserPass(const nsACString &input)
1146 ENSURE_MUTABLE();
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())
1154 return NS_OK;
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;
1163 InvalidateCache();
1165 if (userpass.IsEmpty()) {
1166 // remove user:pass
1167 if (mUsername.mLen > 0) {
1168 if (mPassword.mLen > 0)
1169 mUsername.mLen += (mPassword.mLen + 1);
1170 mUsername.mLen++;
1171 mSpec.Cut(mUsername.mPos, mUsername.mLen);
1172 mAuthority.mLen -= mUsername.mLen;
1173 ShiftFromHost(-mUsername.mLen);
1174 mUsername.mLen = -1;
1175 mPassword.mLen = -1;
1177 return NS_OK;
1180 NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
1182 nsresult rv;
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|
1192 nsCAutoString buf;
1193 if (usernameLen > 0) {
1194 GET_SEGMENT_ENCODER(encoder);
1195 PRBool ignoredOut;
1196 usernameLen = encoder.EncodeSegmentCount(userpass.get(),
1197 URLSegment(usernamePos,
1198 usernameLen),
1199 esc_Username | esc_AlwaysCopy,
1200 buf, ignoredOut);
1201 if (passwordLen >= 0) {
1202 buf.Append(':');
1203 passwordLen = encoder.EncodeSegmentCount(userpass.get(),
1204 URLSegment(passwordPos,
1205 passwordLen),
1206 esc_Password |
1207 esc_AlwaysCopy, buf,
1208 ignoredOut);
1210 if (mUsername.mLen < 0)
1211 buf.Append('@');
1214 PRUint32 shift = 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();
1224 else {
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;
1232 if (shift) {
1233 ShiftFromHost(shift);
1234 mAuthority.mLen += shift;
1236 // update positions and lengths
1237 mUsername.mLen = usernameLen;
1238 mPassword.mLen = passwordLen;
1239 if (passwordLen)
1240 mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
1241 return NS_OK;
1244 NS_IMETHODIMP
1245 nsStandardURL::SetUsername(const nsACString &input)
1247 ENSURE_MUTABLE();
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())
1255 return NS_OK;
1256 NS_ERROR("cannot set username on no-auth url");
1257 return NS_ERROR_UNEXPECTED;
1260 if (username.IsEmpty())
1261 return SetUserPass(username);
1263 InvalidateCache();
1265 // escape username if necessary
1266 nsCAutoString buf;
1267 GET_SEGMENT_ENCODER(encoder);
1268 const nsACString &escUsername =
1269 encoder.EncodeSegment(username, esc_Username, buf);
1271 PRInt32 shift;
1273 if (mUsername.mLen < 0) {
1274 mUsername.mPos = mAuthority.mPos;
1275 mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
1276 shift = escUsername.Length() + 1;
1278 else
1279 shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
1281 if (shift) {
1282 mUsername.mLen = escUsername.Length();
1283 mAuthority.mLen += shift;
1284 ShiftFromPassword(shift);
1286 return NS_OK;
1289 NS_IMETHODIMP
1290 nsStandardURL::SetPassword(const nsACString &input)
1292 ENSURE_MUTABLE();
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())
1300 return NS_OK;
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;
1309 InvalidateCache();
1311 if (password.IsEmpty()) {
1312 if (mPassword.mLen >= 0) {
1313 // cut(":password")
1314 mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
1315 ShiftFromHost(-(mPassword.mLen + 1));
1316 mAuthority.mLen -= (mPassword.mLen + 1);
1317 mPassword.mLen = -1;
1319 return NS_OK;
1322 // escape password if necessary
1323 nsCAutoString buf;
1324 GET_SEGMENT_ENCODER(encoder);
1325 const nsACString &escPassword =
1326 encoder.EncodeSegment(password, esc_Password, buf);
1328 PRInt32 shift;
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;
1335 else
1336 shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
1338 if (shift) {
1339 mPassword.mLen = escPassword.Length();
1340 mAuthority.mLen += shift;
1341 ShiftFromHost(shift);
1343 return NS_OK;
1346 NS_IMETHODIMP
1347 nsStandardURL::SetHostPort(const nsACString &value)
1349 ENSURE_MUTABLE();
1351 // XXX needs implementation!!
1352 NS_NOTREACHED("not implemented");
1353 return NS_ERROR_NOT_IMPLEMENTED;
1356 NS_IMETHODIMP
1357 nsStandardURL::SetHost(const nsACString &input)
1359 ENSURE_MUTABLE();
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) {
1367 if (flat.IsEmpty())
1368 return NS_OK;
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
1377 // in the hostname.
1378 if (strchr(host, ' '))
1379 return NS_ERROR_MALFORMED_URI;
1381 InvalidateCache();
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;
1393 mHost.mLen = -1;
1394 mPort = -1;
1396 return NS_OK;
1399 // handle IPv6 unescaped address literal
1400 PRInt32 len;
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();
1410 else
1411 len = flat.Length();
1413 if (mHost.mLen < 0) {
1414 mHost.mPos = mAuthority.mPos;
1415 mHost.mLen = 0;
1418 PRInt32 shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
1420 if (shift) {
1421 mHost.mLen = len;
1422 mAuthority.mLen += shift;
1423 ShiftFromPath(shift);
1426 // Now canonicalize the host to lowercase
1427 net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
1429 return NS_OK;
1432 NS_IMETHODIMP
1433 nsStandardURL::SetPort(PRInt32 port)
1435 ENSURE_MUTABLE();
1437 LOG(("nsStandardURL::SetPort [port=%d]\n", port));
1439 if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
1440 return NS_OK;
1442 if (mURLType == URLTYPE_NO_AUTHORITY) {
1443 NS_WARNING("cannot set port on no-auth url");
1444 return NS_ERROR_UNEXPECTED;
1447 InvalidateCache();
1449 if (mPort == -1) {
1450 // need to insert the port number in the URL spec
1451 nsCAutoString buf;
1452 buf.Assign(':');
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
1460 port = -1;
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);
1469 else {
1470 // need to replace the existing port
1471 nsCAutoString buf;
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);
1482 mPort = port;
1483 return NS_OK;
1486 NS_IMETHODIMP
1487 nsStandardURL::SetPath(const nsACString &input)
1489 ENSURE_MUTABLE();
1491 const nsPromiseFlatCString &path = PromiseFlatCString(input);
1493 LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
1495 InvalidateCache();
1497 if (!path.IsEmpty()) {
1498 nsCAutoString spec;
1500 spec.Assign(mSpec.get(), mPath.mPos);
1501 if (path.First() != '/')
1502 spec.Append('/');
1503 spec.Append(path);
1505 return SetSpec(spec);
1507 else if (mPath.mLen >= 1) {
1508 mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
1509 // these contain only a '/'
1510 mPath.mLen = 1;
1511 mDirectory.mLen = 1;
1512 mFilepath.mLen = 1;
1513 // these are no longer defined
1514 mBasename.mLen = -1;
1515 mExtension.mLen = -1;
1516 mParam.mLen = -1;
1517 mQuery.mLen = -1;
1518 mRef.mLen = -1;
1520 return NS_OK;
1523 NS_IMETHODIMP
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)) {
1533 *result = PR_FALSE;
1534 return NS_OK;
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) {
1540 *result = PR_FALSE;
1541 return NS_OK;
1544 // Next check parts of a URI that, if different, automatically make the
1545 // URIs different
1546 if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
1547 // Check for host manually, since conversion to file will
1548 // ignore the host!
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
1557 // beasties
1558 *result = PR_FALSE;
1559 return NS_OK;
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)) {
1566 *result = PR_TRUE;
1567 return NS_OK;
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.
1577 *result = PR_FALSE;
1579 rv = EnsureFile();
1580 if (NS_FAILED(rv)) {
1581 LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
1582 this, mSpec.get()));
1583 return rv;
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()));
1590 return rv;
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.
1598 *result = PR_FALSE;
1600 return NS_OK;
1603 NS_IMETHODIMP
1604 nsStandardURL::SchemeIs(const char *scheme, PRBool *result)
1606 NS_PRECONDITION(result, "null pointer");
1608 *result = SegmentIs(mScheme, scheme);
1609 return NS_OK;
1612 /* virtual */ nsStandardURL*
1613 nsStandardURL::StartClone()
1615 nsStandardURL *clone;
1616 NS_NEWXPCOM(clone, nsStandardURL);
1617 return clone;
1620 NS_IMETHODIMP
1621 nsStandardURL::Clone(nsIURI **result)
1623 nsStandardURL *clone = StartClone();
1624 if (!clone)
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;
1642 clone->mRef = mRef;
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);
1654 return NS_OK;
1657 NS_IMETHODIMP
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
1664 nsCAutoString buf;
1665 PRInt32 relpathLen;
1666 if (net_FilterURIString(relpath, buf)) {
1667 relpath = buf.get();
1668 relpathLen = buf.Length();
1669 } else
1670 relpathLen = flat.Length();
1672 // XXX hack hack hack
1673 char *p = nsnull;
1674 char **result = &p;
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;
1690 nsresult rv;
1691 URLSegment scheme;
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,
1701 relpathLen,
1702 &scheme.mPos, &scheme.mLen,
1703 nsnull, nsnull,
1704 nsnull, nsnull);
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,
1725 "://",3) == 0) {
1726 // now this is really absolute
1727 // because a :// follows the scheme
1728 *result = nsCRT::strdup(relpath);
1729 } else {
1730 // This is a deprecated form of relative urls like
1731 // http:file or http:/path/file
1732 // we will support it for now ...
1733 relative = PR_TRUE;
1734 offset = scheme.mLen + 1;
1736 } else {
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);
1741 } else {
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);
1752 } else {
1753 // then it must be relative
1754 relative = PR_TRUE;
1757 if (relative) {
1758 PRUint32 len = 0;
1759 const char *realrelpath = relpath + offset;
1760 switch (*realrelpath) {
1761 case '/':
1762 // overwrite everything after the authority
1763 len = mAuthority.mPos + mAuthority.mLen;
1764 break;
1765 case '?':
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;
1771 else
1772 len = mPath.mPos + mPath.mLen;
1773 break;
1774 case '#':
1775 case '\0':
1776 // overwrite the existing #ref
1777 if (mRef.mLen < 0)
1778 len = mPath.mPos + mPath.mLen;
1779 else
1780 len = mRef.mPos - 1;
1781 break;
1782 default:
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;
1790 } else {
1791 // overwrite everything after the directory
1792 len = mDirectory.mPos + mDirectory.mLen;
1794 } else {
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;
1803 if (!*result)
1804 return NS_ERROR_OUT_OF_MEMORY;
1806 if (resultPath)
1807 net_CoalesceDirs(coalesceFlag, resultPath);
1808 else {
1809 // locate result path
1810 resultPath = PL_strstr(*result, "://");
1811 if (resultPath) {
1812 resultPath = PL_strchr(resultPath + 3, '/');
1813 if (resultPath)
1814 net_CoalesceDirs(coalesceFlag,resultPath);
1817 // XXX avoid extra copy
1818 out = *result;
1819 free(*result);
1820 return NS_OK;
1823 // result may contain unescaped UTF-8 characters
1824 NS_IMETHODIMP
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);
1834 aResult.Truncate();
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());
1845 if (!isEquals)
1847 if (NS_SUCCEEDED(rv))
1848 NS_RELEASE(stdurl2);
1849 return NS_OK;
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)
1859 thisIndex++;
1860 thatIndex++;
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))
1867 thisIndex--;
1869 // grab spec from beginning to thisIndex
1870 aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
1872 NS_RELEASE(stdurl2);
1873 return rv;
1876 NS_IMETHODIMP
1877 nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
1879 NS_ENSURE_ARG_POINTER(uri2);
1881 aResult.Truncate();
1883 // if uri's are equal, then return empty string
1884 PRBool isEquals = PR_FALSE;
1885 if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
1886 return NS_OK;
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());
1896 if (!isEquals)
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;
1910 #ifdef XP_WIN
1911 PRBool isFileScheme = SegmentIs(mScheme, "file");
1912 if (isFileScheme)
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 == '/'))
1919 thisIndex++;
1920 thatIndex++;
1922 // look for end of first segment
1923 while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
1925 thisIndex++;
1926 thatIndex++;
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);
1936 #endif
1938 while ((*thisIndex == *thatIndex) && *thisIndex)
1940 thisIndex++;
1941 thatIndex++;
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))
1948 thatIndex--;
1950 // need to account for slashes and add corresponding "../"
1951 while (*thisIndex)
1953 if (*thisIndex == '/')
1954 aResult.AppendLiteral("../");
1956 thisIndex++;
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);
1965 return rv;
1968 //----------------------------------------------------------------------------
1969 // nsStandardURL::nsIURL
1970 //----------------------------------------------------------------------------
1972 // result may contain unescaped UTF-8 characters
1973 NS_IMETHODIMP
1974 nsStandardURL::GetFilePath(nsACString &result)
1976 result = Filepath();
1977 return NS_OK;
1980 // result may contain unescaped UTF-8 characters
1981 NS_IMETHODIMP
1982 nsStandardURL::GetParam(nsACString &result)
1984 result = Param();
1985 return NS_OK;
1988 // result may contain unescaped UTF-8 characters
1989 NS_IMETHODIMP
1990 nsStandardURL::GetQuery(nsACString &result)
1992 result = Query();
1993 return NS_OK;
1996 // result may contain unescaped UTF-8 characters
1997 NS_IMETHODIMP
1998 nsStandardURL::GetRef(nsACString &result)
2000 result = Ref();
2001 return NS_OK;
2004 // result may contain unescaped UTF-8 characters
2005 NS_IMETHODIMP
2006 nsStandardURL::GetDirectory(nsACString &result)
2008 result = Directory();
2009 return NS_OK;
2012 // result may contain unescaped UTF-8 characters
2013 NS_IMETHODIMP
2014 nsStandardURL::GetFileName(nsACString &result)
2016 result = Filename();
2017 return NS_OK;
2020 // result may contain unescaped UTF-8 characters
2021 NS_IMETHODIMP
2022 nsStandardURL::GetFileBaseName(nsACString &result)
2024 result = Basename();
2025 return NS_OK;
2028 // result may contain unescaped UTF-8 characters
2029 NS_IMETHODIMP
2030 nsStandardURL::GetFileExtension(nsACString &result)
2032 result = Extension();
2033 return NS_OK;
2036 NS_IMETHODIMP
2037 nsStandardURL::SetFilePath(const nsACString &input)
2039 ENSURE_MUTABLE();
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) {
2052 nsCAutoString spec;
2053 PRUint32 dirPos, basePos, extPos;
2054 PRInt32 dirLen, baseLen, extLen;
2055 nsresult rv;
2057 rv = mParser->ParseFilePath(filepath, -1,
2058 &dirPos, &dirLen,
2059 &basePos, &baseLen,
2060 &extPos, &extLen);
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] != '/')
2068 spec.Append('/');
2070 GET_SEGMENT_ENCODER(encoder);
2072 // append encoded filepath components
2073 if (dirLen > 0)
2074 encoder.EncodeSegment(Substring(filepath + dirPos,
2075 filepath + dirPos + dirLen),
2076 esc_Directory | esc_AlwaysCopy, spec);
2077 if (baseLen > 0)
2078 encoder.EncodeSegment(Substring(filepath + basePos,
2079 filepath + basePos + baseLen),
2080 esc_FileBaseName | esc_AlwaysCopy, spec);
2081 if (extLen >= 0) {
2082 spec.Append('.');
2083 if (extLen > 0)
2084 encoder.EncodeSegment(Substring(filepath + extPos,
2085 filepath + extPos + extLen),
2086 esc_FileExtension | esc_AlwaysCopy,
2087 spec);
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 '/'
2104 mPath.mLen = 1;
2105 mDirectory.mLen = 1;
2106 mFilepath.mLen = 1;
2107 // these are no longer defined
2108 mBasename.mLen = -1;
2109 mExtension.mLen = -1;
2111 return NS_OK;
2114 NS_IMETHODIMP
2115 nsStandardURL::SetParam(const nsACString &input)
2117 NS_NOTYETIMPLEMENTED("");
2118 return NS_ERROR_NOT_IMPLEMENTED;
2121 NS_IMETHODIMP
2122 nsStandardURL::SetQuery(const nsACString &input)
2124 ENSURE_MUTABLE();
2126 const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2127 const char *query = flat.get();
2129 LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
2131 if (mPath.mLen < 0)
2132 return SetPath(flat);
2134 InvalidateCache();
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);
2143 mQuery.mPos = 0;
2144 mQuery.mLen = -1;
2146 return NS_OK;
2149 PRInt32 queryLen = strlen(query);
2150 if (query[0] == '?') {
2151 query++;
2152 queryLen--;
2155 if (mQuery.mLen < 0) {
2156 if (mRef.mLen < 0)
2157 mQuery.mPos = mSpec.Length();
2158 else
2159 mQuery.mPos = mRef.mPos - 1;
2160 mSpec.Insert('?', mQuery.mPos);
2161 mQuery.mPos++;
2162 mQuery.mLen = 0;
2163 // the insertion pushes these out by 1
2164 mPath.mLen++;
2165 mRef.mPos++;
2168 // encode query if necessary
2169 nsCAutoString buf;
2170 PRBool encoded;
2171 GET_QUERY_ENCODER(encoder);
2172 encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
2173 buf, encoded);
2174 if (encoded) {
2175 query = buf.get();
2176 queryLen = buf.Length();
2179 PRInt32 shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
2181 if (shift) {
2182 mQuery.mLen = queryLen;
2183 mPath.mLen += shift;
2184 ShiftFromRef(shift);
2186 return NS_OK;
2189 NS_IMETHODIMP
2190 nsStandardURL::SetRef(const nsACString &input)
2192 ENSURE_MUTABLE();
2194 const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2195 const char *ref = flat.get();
2197 LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
2199 if (mPath.mLen < 0)
2200 return SetPath(flat);
2202 InvalidateCache();
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);
2210 mRef.mPos = 0;
2211 mRef.mLen = -1;
2213 return NS_OK;
2216 PRInt32 refLen = strlen(ref);
2217 if (ref[0] == '#') {
2218 ref++;
2219 refLen--;
2222 if (mRef.mLen < 0) {
2223 mSpec.Append('#');
2224 ++mPath.mLen; // Include the # in the path.
2225 mRef.mPos = mSpec.Length();
2226 mRef.mLen = 0;
2229 // encode ref if necessary
2230 nsCAutoString buf;
2231 PRBool encoded;
2232 GET_SEGMENT_ENCODER(encoder);
2233 encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
2234 buf, encoded);
2235 if (encoded) {
2236 ref = buf.get();
2237 refLen = buf.Length();
2240 ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
2241 mPath.mLen += (refLen - mRef.mLen);
2242 mRef.mLen = refLen;
2243 return NS_OK;
2246 NS_IMETHODIMP
2247 nsStandardURL::SetDirectory(const nsACString &input)
2249 NS_NOTYETIMPLEMENTED("");
2250 return NS_ERROR_NOT_IMPLEMENTED;
2253 NS_IMETHODIMP
2254 nsStandardURL::SetFileName(const nsACString &input)
2256 ENSURE_MUTABLE();
2258 const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2259 const char *filename = flat.get();
2261 LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
2263 if (mPath.mLen < 0)
2264 return SetPath(flat);
2266 PRInt32 shift = 0;
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;
2275 mBasename.mLen = 0;
2276 mExtension.mLen = -1;
2279 else {
2280 nsresult rv;
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);
2297 mBasename.mLen = 0;
2298 mExtension.mLen = -1;
2301 else {
2302 nsCAutoString newFilename;
2303 PRBool ignoredOut;
2304 GET_SEGMENT_ENCODER(encoder);
2305 basename.mLen = encoder.EncodeSegmentCount(filename, basename,
2306 esc_FileBaseName |
2307 esc_AlwaysCopy,
2308 newFilename,
2309 ignoredOut);
2310 if (extension.mLen >= 0) {
2311 newFilename.Append('.');
2312 extension.mLen = encoder.EncodeSegmentCount(filename, extension,
2313 esc_FileExtension |
2314 esc_AlwaysCopy,
2315 newFilename,
2316 ignoredOut);
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();
2325 else {
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;
2340 if (shift) {
2341 ShiftFromParam(shift);
2342 mFilepath.mLen += shift;
2343 mPath.mLen += shift;
2345 return NS_OK;
2348 NS_IMETHODIMP
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);
2365 NS_IMETHODIMP
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 //----------------------------------------------------------------------------
2384 nsresult
2385 nsStandardURL::EnsureFile()
2387 NS_PRECONDITION(mSupportsFileURL,
2388 "EnsureFile() called on a URL that doesn't support files!");
2389 if (mFile) {
2390 // Nothing to do
2391 return NS_OK;
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));
2408 NS_IMETHODIMP
2409 nsStandardURL::GetFile(nsIFile **result)
2411 NS_PRECONDITION(mSupportsFileURL,
2412 "GetFile() called on a URL that doesn't support files!");
2413 nsresult rv = EnsureFile();
2414 if (NS_FAILED(rv))
2415 return rv;
2417 #if defined(PR_LOGGING)
2418 if (LOG_ENABLED()) {
2419 nsCAutoString path;
2420 mFile->GetNativePath(path);
2421 LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
2422 this, mSpec.get(), path.get()));
2424 #endif
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);
2435 NS_IMETHODIMP
2436 nsStandardURL::SetFile(nsIFile *file)
2438 ENSURE_MUTABLE();
2440 NS_ENSURE_ARG_POINTER(file);
2442 nsresult rv;
2443 nsCAutoString url;
2445 rv = net_GetURLSpecFromFile(file, url);
2446 if (NS_FAILED(rv)) return rv;
2448 SetSpec(url);
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)) {
2454 InvalidateCache();
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)
2458 mFile = 0;
2461 return rv;
2464 //----------------------------------------------------------------------------
2465 // nsStandardURL::nsIStandardURL
2466 //----------------------------------------------------------------------------
2468 inline PRBool
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'));
2476 NS_IMETHODIMP
2477 nsStandardURL::Init(PRUint32 urlType,
2478 PRInt32 defaultPort,
2479 const nsACString &spec,
2480 const char *charset,
2481 nsIURI *baseURI)
2483 ENSURE_MUTABLE();
2485 InvalidateCache();
2487 switch (urlType) {
2488 case URLTYPE_STANDARD:
2489 mParser = net_GetStdURLParser();
2490 break;
2491 case URLTYPE_AUTHORITY:
2492 mParser = net_GetAuthURLParser();
2493 break;
2494 case URLTYPE_NO_AUTHORITY:
2495 mParser = net_GetNoAuthURLParser();
2496 break;
2497 default:
2498 NS_NOTREACHED("bad urlType");
2499 return NS_ERROR_INVALID_ARG;
2501 mDefaultPort = defaultPort;
2502 mURLType = urlType;
2504 mOriginCharset.Truncate();
2506 if (charset == nsnull || *charset == '\0') {
2507 // check if baseURI provides an origin charset and use that.
2508 if (baseURI)
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;
2525 if (baseURI) {
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) == '/')
2537 baseURI = nsnull;
2541 if (!baseURI)
2542 return SetSpec(spec);
2544 nsCAutoString buf;
2545 nsresult rv = baseURI->Resolve(spec, buf);
2546 if (NS_FAILED(rv)) return rv;
2548 return SetSpec(buf);
2551 NS_IMETHODIMP
2552 nsStandardURL::GetMutable(PRBool *value)
2554 *value = mMutable;
2555 return NS_OK;
2558 NS_IMETHODIMP
2559 nsStandardURL::SetMutable(PRBool value)
2561 NS_ENSURE_ARG(mMutable || !value);
2563 mMutable = value;
2564 return NS_OK;
2567 //----------------------------------------------------------------------------
2568 // nsStandardURL::nsISerializable
2569 //----------------------------------------------------------------------------
2571 NS_IMETHODIMP
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");
2578 nsresult rv;
2580 PRUint32 urlType;
2581 rv = stream->Read32(&urlType);
2582 if (NS_FAILED(rv)) return rv;
2583 mURLType = urlType;
2584 switch (mURLType) {
2585 case URLTYPE_STANDARD:
2586 mParser = net_GetStdURLParser();
2587 break;
2588 case URLTYPE_AUTHORITY:
2589 mParser = net_GetAuthURLParser();
2590 break;
2591 case URLTYPE_NO_AUTHORITY:
2592 mParser = net_GetNoAuthURLParser();
2593 break;
2594 default:
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;
2650 PRBool isMutable;
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;
2677 return NS_OK;
2680 NS_IMETHODIMP
2681 nsStandardURL::Write(nsIObjectOutputStream *stream)
2683 nsresult rv;
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.
2750 return NS_OK;
2753 //----------------------------------------------------------------------------
2754 // nsStandardURL::nsIClassInfo
2755 //----------------------------------------------------------------------------
2757 NS_IMETHODIMP
2758 nsStandardURL::GetInterfaces(PRUint32 *count, nsIID * **array)
2760 *count = 0;
2761 *array = nsnull;
2762 return NS_OK;
2765 NS_IMETHODIMP
2766 nsStandardURL::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
2768 *_retval = nsnull;
2769 return NS_OK;
2772 NS_IMETHODIMP
2773 nsStandardURL::GetContractID(char * *aContractID)
2775 *aContractID = nsnull;
2776 return NS_OK;
2779 NS_IMETHODIMP
2780 nsStandardURL::GetClassDescription(char * *aClassDescription)
2782 *aClassDescription = nsnull;
2783 return NS_OK;
2786 NS_IMETHODIMP
2787 nsStandardURL::GetClassID(nsCID * *aClassID)
2789 *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
2790 if (!*aClassID)
2791 return NS_ERROR_OUT_OF_MEMORY;
2792 return GetClassIDNoAlloc(*aClassID);
2795 NS_IMETHODIMP
2796 nsStandardURL::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
2798 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
2799 return NS_OK;
2802 NS_IMETHODIMP
2803 nsStandardURL::GetFlags(PRUint32 *aFlags)
2805 *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
2806 return NS_OK;
2809 NS_IMETHODIMP
2810 nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
2812 *aClassIDNoAlloc = kStandardURLCID;
2813 return NS_OK;