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