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