/src/mozilla-central/netwerk/base/nsStandardURL.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsASCIIMask.h" |
10 | | #include "nsStandardURL.h" |
11 | | #include "nsCRT.h" |
12 | | #include "nsEscape.h" |
13 | | #include "nsIFile.h" |
14 | | #include "nsIObjectInputStream.h" |
15 | | #include "nsIObjectOutputStream.h" |
16 | | #include "nsIIDNService.h" |
17 | | #include "mozilla/Logging.h" |
18 | | #include "nsAutoPtr.h" |
19 | | #include "nsIURLParser.h" |
20 | | #include "nsNetCID.h" |
21 | | #include "mozilla/MemoryReporting.h" |
22 | | #include "mozilla/ipc/URIUtils.h" |
23 | | #include "mozilla/TextUtils.h" |
24 | | #include <algorithm> |
25 | | #include "nsContentUtils.h" |
26 | | #include "prprf.h" |
27 | | #include "nsReadableUtils.h" |
28 | | #include "mozilla/net/MozURL_ffi.h" |
29 | | #include "mozilla/TextUtils.h" |
30 | | |
31 | | // |
32 | | // setenv MOZ_LOG nsStandardURL:5 |
33 | | // |
34 | | static LazyLogModule gStandardURLLog("nsStandardURL"); |
35 | | |
36 | | // The Chromium code defines its own LOG macro which we don't want |
37 | | #undef LOG |
38 | 9.49M | #define LOG(args) MOZ_LOG(gStandardURLLog, LogLevel::Debug, args) |
39 | | #undef LOG_ENABLED |
40 | 1.09M | #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug) |
41 | | |
42 | | using namespace mozilla::ipc; |
43 | | |
44 | | namespace mozilla { |
45 | | namespace net { |
46 | | |
47 | | static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID); |
48 | | static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); |
49 | | |
50 | | // This will always be initialized and destroyed on the main thread, but |
51 | | // can be safely used on other threads. |
52 | | nsIIDNService *nsStandardURL::gIDN = nullptr; |
53 | | |
54 | | // This value will only be updated on the main thread once. Worker threads |
55 | | // may race when reading this values, but that's OK because in the worst |
56 | | // case we will just dispatch a noop runnable to the main thread. |
57 | | bool nsStandardURL::gInitialized = false; |
58 | | |
59 | | const char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 }; |
60 | | bool nsStandardURL::gPunycodeHost = true; |
61 | | |
62 | | // Invalid host characters |
63 | | // We still allow % because it is in the ID of addons. |
64 | | // Any percent encoded ASCII characters that are not allowed in the |
65 | | // hostname are not percent decoded, and will be parsed just fine. |
66 | | // |
67 | | // Note that the array below will be initialized at compile time, |
68 | | // so we do not need to "optimize" TestForInvalidHostCharacters. |
69 | | // |
70 | | constexpr bool TestForInvalidHostCharacters(char c) |
71 | 0 | { |
72 | 0 | // Testing for these: |
73 | 0 | // CONTROL_CHARACTERS " #/:?@[\\]*<>|\""; |
74 | 0 | return (c > 0 && c < 32) || // The control characters are [1, 31] |
75 | 0 | c == ' ' || c == '#' || c == '/' || c == ':' || c == '?' || |
76 | 0 | c == '@' || c == '[' || c == '\\' || c == ']' || c == '*' || |
77 | 0 | c == '<' || c == '>' || c == '|' || c == '"'; |
78 | 0 | } |
79 | | constexpr ASCIIMaskArray sInvalidHostChars = CreateASCIIMask(TestForInvalidHostCharacters); |
80 | | |
81 | | //---------------------------------------------------------------------------- |
82 | | // nsStandardURL::nsSegmentEncoder |
83 | | //---------------------------------------------------------------------------- |
84 | | |
85 | | nsStandardURL::nsSegmentEncoder::nsSegmentEncoder(const Encoding* encoding) |
86 | | : mEncoding(encoding) |
87 | 2.24M | { |
88 | 2.24M | if (mEncoding == UTF_8_ENCODING) { |
89 | 0 | mEncoding = nullptr; |
90 | 0 | } |
91 | 2.24M | } |
92 | | |
93 | | int32_t nsStandardURL:: |
94 | | nsSegmentEncoder::EncodeSegmentCount(const char *str, |
95 | | const URLSegment &seg, |
96 | | int16_t mask, |
97 | | nsCString& result, |
98 | | bool &appended, |
99 | | uint32_t extraLen) |
100 | 4.61M | { |
101 | 4.61M | // extraLen is characters outside the segment that will be |
102 | 4.61M | // added when the segment is not empty (like the @ following |
103 | 4.61M | // a username). |
104 | 4.61M | appended = false; |
105 | 4.61M | if (!str) |
106 | 0 | return 0; |
107 | 4.61M | int32_t len = 0; |
108 | 4.61M | if (seg.mLen > 0) { |
109 | 1.88M | uint32_t pos = seg.mPos; |
110 | 1.88M | len = seg.mLen; |
111 | 1.88M | |
112 | 1.88M | // first honor the origin charset if appropriate. as an optimization, |
113 | 1.88M | // only do this if the segment is non-ASCII. Further, if mEncoding is |
114 | 1.88M | // null, then the origin charset is UTF-8 and there is nothing to do. |
115 | 1.88M | nsAutoCString encBuf; |
116 | 1.88M | if (mEncoding && !nsCRT::IsAscii(str + pos, len)) { |
117 | 0 | // we have to encode this segment |
118 | 0 | nsresult rv; |
119 | 0 | const Encoding* ignored; |
120 | 0 | Tie(rv, ignored) = |
121 | 0 | mEncoding->Encode(Substring(str + pos, str + pos + len), encBuf); |
122 | 0 | if (NS_SUCCEEDED(rv)) { |
123 | 0 | str = encBuf.get(); |
124 | 0 | pos = 0; |
125 | 0 | len = encBuf.Length(); |
126 | 0 | } |
127 | 0 | // else some failure occurred... assume UTF-8 is ok. |
128 | 0 | } |
129 | 1.88M | |
130 | 1.88M | uint32_t initLen = result.Length(); |
131 | 1.88M | |
132 | 1.88M | // now perform any required escaping |
133 | 1.88M | if (NS_EscapeURL(str + pos, len, mask, result)) { |
134 | 387k | len = result.Length() - initLen; |
135 | 387k | appended = true; |
136 | 387k | } |
137 | 1.49M | else if (str == encBuf.get()) { |
138 | 0 | result += encBuf; // append only!! |
139 | 0 | len = encBuf.Length(); |
140 | 0 | appended = true; |
141 | 0 | } |
142 | 1.88M | len += extraLen; |
143 | 1.88M | } |
144 | 4.61M | return len; |
145 | 4.61M | } |
146 | | |
147 | | const nsACString &nsStandardURL:: |
148 | | nsSegmentEncoder::EncodeSegment(const nsACString& str, |
149 | | int16_t mask, |
150 | | nsCString& result) |
151 | 0 | { |
152 | 0 | const char *text; |
153 | 0 | bool encoded; |
154 | 0 | EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded); |
155 | 0 | if (encoded) |
156 | 0 | return result; |
157 | 0 | return str; |
158 | 0 | } |
159 | | |
160 | | //---------------------------------------------------------------------------- |
161 | | // nsStandardURL <public> |
162 | | //---------------------------------------------------------------------------- |
163 | | |
164 | | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
165 | | static StaticMutex gAllURLsMutex; |
166 | | static LinkedList<nsStandardURL> gAllURLs; |
167 | | #endif |
168 | | |
169 | | nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL) |
170 | | : mDefaultPort(-1) |
171 | | , mPort(-1) |
172 | | , mDisplayHost(nullptr) |
173 | | , mURLType(URLTYPE_STANDARD) |
174 | | , mSupportsFileURL(aSupportsFileURL) |
175 | | , mCheckedIfHostA(false) |
176 | 2.25M | { |
177 | 2.25M | LOG(("Creating nsStandardURL @%p\n", this)); |
178 | 2.25M | |
179 | 2.25M | // gInitialized changes value only once (false->true) on the main thread. |
180 | 2.25M | // It's OK to race here because in the worst case we'll just |
181 | 2.25M | // dispatch a noop runnable to the main thread. |
182 | 2.25M | MOZ_ASSERT(gInitialized); |
183 | 2.25M | |
184 | 2.25M | // default parser in case nsIStandardURL::Init is never called |
185 | 2.25M | mParser = net_GetStdURLParser(); |
186 | 2.25M | |
187 | | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
188 | | if (NS_IsMainThread()) { |
189 | | if (aTrackURL) { |
190 | | StaticMutexAutoLock lock(gAllURLsMutex); |
191 | | gAllURLs.insertBack(this); |
192 | | } |
193 | | } |
194 | | #endif |
195 | | |
196 | 2.25M | } |
197 | | |
198 | | nsStandardURL::~nsStandardURL() |
199 | 2.25M | { |
200 | 2.25M | LOG(("Destroying nsStandardURL @%p\n", this)); |
201 | 2.25M | |
202 | | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
203 | | { |
204 | | StaticMutexAutoLock lock(gAllURLsMutex); |
205 | | if (isInList()) { |
206 | | remove(); |
207 | | } |
208 | | } |
209 | | #endif |
210 | | } |
211 | | |
212 | | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
213 | | struct DumpLeakedURLs { |
214 | | DumpLeakedURLs() = default; |
215 | | ~DumpLeakedURLs(); |
216 | | }; |
217 | | |
218 | | DumpLeakedURLs::~DumpLeakedURLs() |
219 | | { |
220 | | MOZ_ASSERT(NS_IsMainThread()); |
221 | | StaticMutexAutoLock lock(gAllURLsMutex); |
222 | | if (!gAllURLs.isEmpty()) { |
223 | | printf("Leaked URLs:\n"); |
224 | | for (auto url : gAllURLs) { |
225 | | url->PrintSpec(); |
226 | | } |
227 | | gAllURLs.clear(); |
228 | | } |
229 | | } |
230 | | #endif |
231 | | |
232 | | void |
233 | | nsStandardURL::InitGlobalObjects() |
234 | 6 | { |
235 | 6 | MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); |
236 | 6 | |
237 | 6 | if (gInitialized) { |
238 | 3 | return; |
239 | 3 | } |
240 | 3 | |
241 | 3 | gInitialized = true; |
242 | 3 | |
243 | 3 | Preferences::AddBoolVarCache(&gPunycodeHost, "network.standard-url.punycode-host", true); |
244 | 3 | nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID)); |
245 | 3 | if (serv) { |
246 | 3 | NS_ADDREF(gIDN = serv.get()); |
247 | 3 | } |
248 | 3 | MOZ_DIAGNOSTIC_ASSERT(gIDN); |
249 | 3 | |
250 | 3 | // Make sure nsURLHelper::InitGlobals() gets called on the main thread |
251 | 3 | nsCOMPtr<nsIURLParser> parser = net_GetStdURLParser(); |
252 | 3 | MOZ_DIAGNOSTIC_ASSERT(parser); |
253 | 3 | Unused << parser; |
254 | 3 | } |
255 | | |
256 | | void |
257 | | nsStandardURL::ShutdownGlobalObjects() |
258 | 0 | { |
259 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); |
260 | 0 | NS_IF_RELEASE(gIDN); |
261 | 0 |
|
262 | | #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN |
263 | | if (gInitialized) { |
264 | | // This instanciates a dummy class, and will trigger the class |
265 | | // destructor when libxul is unloaded. This is equivalent to atexit(), |
266 | | // but gracefully handles dlclose(). |
267 | | static DumpLeakedURLs d; |
268 | | } |
269 | | #endif |
270 | | } |
271 | | |
272 | | //---------------------------------------------------------------------------- |
273 | | // nsStandardURL <private> |
274 | | //---------------------------------------------------------------------------- |
275 | | |
276 | | void |
277 | | nsStandardURL::Clear() |
278 | 1.15M | { |
279 | 1.15M | mSpec.Truncate(); |
280 | 1.15M | |
281 | 1.15M | mPort = -1; |
282 | 1.15M | |
283 | 1.15M | mScheme.Reset(); |
284 | 1.15M | mAuthority.Reset(); |
285 | 1.15M | mUsername.Reset(); |
286 | 1.15M | mPassword.Reset(); |
287 | 1.15M | mHost.Reset(); |
288 | 1.15M | |
289 | 1.15M | mPath.Reset(); |
290 | 1.15M | mFilepath.Reset(); |
291 | 1.15M | mDirectory.Reset(); |
292 | 1.15M | mBasename.Reset(); |
293 | 1.15M | |
294 | 1.15M | mExtension.Reset(); |
295 | 1.15M | mQuery.Reset(); |
296 | 1.15M | mRef.Reset(); |
297 | 1.15M | |
298 | 1.15M | InvalidateCache(); |
299 | 1.15M | } |
300 | | |
301 | | void |
302 | | nsStandardURL::InvalidateCache(bool invalidateCachedFile) |
303 | 3.44M | { |
304 | 3.44M | if (invalidateCachedFile) { |
305 | 3.44M | mFile = nullptr; |
306 | 3.44M | } |
307 | 3.44M | } |
308 | | |
309 | | // Return the number of "dots" in the string, or -1 if invalid. Note that the |
310 | | // number of relevant entries in the bases/starts/ends arrays is number of |
311 | | // dots + 1. |
312 | | // Since the trailing dot is allowed, we pass and adjust "length". |
313 | | // |
314 | | // length is assumed to be <= host.Length(); the callers is responsible for that |
315 | | // |
316 | | // Note that the value returned is guaranteed to be in [-1, 3] range. |
317 | | inline int32_t |
318 | | ValidateIPv4Number(const nsACString& host, |
319 | | int32_t bases[4], int32_t dotIndex[3], |
320 | | bool& onlyBase10, int32_t& length) |
321 | 1.07M | { |
322 | 1.07M | MOZ_ASSERT(length <= (int32_t)host.Length()); |
323 | 1.07M | if (length <= 0) { |
324 | 278 | return -1; |
325 | 278 | } |
326 | 1.07M | |
327 | 1.07M | bool lastWasNumber = false; // We count on this being false for i == 0 |
328 | 1.07M | int32_t dotCount = 0; |
329 | 1.07M | onlyBase10 = true; |
330 | 1.07M | |
331 | 1.12M | for (int32_t i = 0; i < length; i++) { |
332 | 1.11M | char current = host[i]; |
333 | 1.11M | if (current == '.') { |
334 | 6.59k | if (!lastWasNumber) { // A dot should not follow an X or a dot, or be first |
335 | 736 | return -1; |
336 | 736 | } |
337 | 5.86k | |
338 | 5.86k | if (dotCount > 0 && i == (length - 1)) { // Trailing dot is OK; shorten and return |
339 | 467 | length--; |
340 | 467 | return dotCount; |
341 | 467 | } |
342 | 5.39k | |
343 | 5.39k | if (dotCount > 2) { |
344 | 77 | return -1; |
345 | 77 | } |
346 | 5.31k | lastWasNumber = false; |
347 | 5.31k | dotIndex[dotCount] = i; |
348 | 5.31k | dotCount ++; |
349 | 1.10M | } else if (current == 'X' || current == 'x') { |
350 | 14.4k | if (!lastWasNumber || // An X should not follow an X or a dot or be first |
351 | 14.4k | i == (length - 1) || // No trailing Xs allowed |
352 | 14.4k | (dotCount == 0 && i != 1) || // If we had no dots, an X should be second |
353 | 14.4k | host[i-1] != '0' || // X should always follow a 0. Guaranteed i > 0 |
354 | 14.4k | // as lastWasNumber is true |
355 | 14.4k | (dotCount > 0 && host[i - 2] != '.')) { // And that zero follows a dot if it exists |
356 | 13.8k | return -1; |
357 | 13.8k | } |
358 | 539 | lastWasNumber = false; |
359 | 539 | bases[dotCount] = 16; |
360 | 539 | onlyBase10 = false; |
361 | 539 | |
362 | 1.09M | } else if (current == '0') { |
363 | 14.8k | if (i < length - 1 && // Trailing zero doesn't signal octal |
364 | 14.8k | host[i + 1] != '.' && // Lone zero is not octal |
365 | 14.8k | (i == 0 || host[i - 1] == '.')) { // Zero at start or following a dot is |
366 | 3.79k | // a candidate for octal |
367 | 3.79k | bases[dotCount] = 8; // This will turn to 16 above if X shows up |
368 | 3.79k | onlyBase10 = false; |
369 | 3.79k | } |
370 | 14.8k | lastWasNumber = true; |
371 | 14.8k | |
372 | 1.08M | } else if (current >= '1' && current <= '7') { |
373 | 21.3k | lastWasNumber = true; |
374 | 21.3k | |
375 | 1.05M | } else if (current >= '8' && current <= '9') { |
376 | 3.62k | if (bases[dotCount] == 8) { |
377 | 38 | return -1; |
378 | 38 | } |
379 | 3.58k | lastWasNumber = true; |
380 | 3.58k | |
381 | 1.05M | } else if ((current >= 'a' && current <= 'f') || |
382 | 1.05M | (current >= 'A' && current <= 'F')) { |
383 | 3.68k | if (bases[dotCount] != 16) { |
384 | 607 | return -1; |
385 | 607 | } |
386 | 3.07k | lastWasNumber = true; |
387 | 3.07k | |
388 | 1.05M | } else { |
389 | 1.05M | return -1; |
390 | 1.05M | } |
391 | 1.11M | } |
392 | 1.07M | |
393 | 1.07M | return dotCount; |
394 | 1.07M | } |
395 | | |
396 | | inline nsresult |
397 | | ParseIPv4Number10(const nsACString& input, uint32_t& number, uint32_t maxNumber) |
398 | 3.68k | { |
399 | 3.68k | uint64_t value = 0; |
400 | 3.68k | const char* current = input.BeginReading(); |
401 | 3.68k | const char* end = input.EndReading(); |
402 | 19.9k | for (; current < end; ++current) { |
403 | 16.2k | char c = *current; |
404 | 16.2k | MOZ_ASSERT(c >= '0' && c <= '9'); |
405 | 16.2k | value *= 10; |
406 | 16.2k | value += c - '0'; |
407 | 16.2k | } |
408 | 3.68k | if (value <= maxNumber) { |
409 | 3.16k | number = value; |
410 | 3.16k | return NS_OK; |
411 | 3.16k | } |
412 | 517 | |
413 | 517 | // The error case |
414 | 517 | number = 0; |
415 | 517 | return NS_ERROR_FAILURE; |
416 | 517 | } |
417 | | |
418 | | inline nsresult |
419 | | ParseIPv4Number(const nsACString& input, int32_t base, uint32_t& number, uint32_t maxNumber) |
420 | 4.52k | { |
421 | 4.52k | // Accumulate in the 64-bit value |
422 | 4.52k | uint64_t value = 0; |
423 | 4.52k | const char* current = input.BeginReading(); |
424 | 4.52k | const char* end = input.EndReading(); |
425 | 4.52k | switch(base) { |
426 | 4.52k | case 16: |
427 | 263 | ++current; |
428 | 263 | MOZ_FALLTHROUGH; |
429 | 2.57k | case 8: |
430 | 2.57k | ++current; |
431 | 2.57k | break; |
432 | 1.95k | case 10: |
433 | 1.95k | default: |
434 | 1.95k | break; |
435 | 4.52k | } |
436 | 16.2k | for (; current < end; ++current) { |
437 | 11.7k | value *= base; |
438 | 11.7k | char c = *current; |
439 | 11.7k | MOZ_ASSERT((base == 10 && IsAsciiDigit(c)) || |
440 | 11.7k | (base == 8 && c >= '0' && c <= '7') || |
441 | 11.7k | (base == 16 && IsAsciiHexDigit(c))); |
442 | 11.7k | if (IsAsciiDigit(c)) { |
443 | 9.31k | value += c - '0'; |
444 | 9.31k | } else if (c >= 'a' && c <= 'f') { |
445 | 2.44k | value += c - 'a' + 10; |
446 | 2.44k | } else if (c >= 'A' && c <= 'F') { |
447 | 0 | value += c - 'A' + 10; |
448 | 0 | } |
449 | 11.7k | } |
450 | 4.52k | |
451 | 4.52k | if (value <= maxNumber) { |
452 | 4.04k | number = value; |
453 | 4.04k | return NS_OK; |
454 | 4.04k | } |
455 | 489 | |
456 | 489 | // The error case |
457 | 489 | number = 0; |
458 | 489 | return NS_ERROR_FAILURE; |
459 | 489 | } |
460 | | |
461 | | // IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser |
462 | | /* static */ nsresult |
463 | | nsStandardURL::NormalizeIPv4(const nsACString& host, nsCString& result) |
464 | 1.07M | { |
465 | 1.07M | int32_t bases[4] = {10,10,10,10}; |
466 | 1.07M | bool onlyBase10 = true; // Track this as a special case |
467 | 1.07M | int32_t dotIndex[3]; // The positions of the dots in the string |
468 | 1.07M | |
469 | 1.07M | // The length may be adjusted by ValidateIPv4Number (ignoring the trailing period) |
470 | 1.07M | // so use "length", rather than host.Length() after that call. |
471 | 1.07M | int32_t length = static_cast<int32_t>(host.Length()); |
472 | 1.07M | int32_t dotCount = ValidateIPv4Number(host, bases, dotIndex, |
473 | 1.07M | onlyBase10, length); |
474 | 1.07M | if (dotCount < 0 || length <= 0) { |
475 | 1.06M | return NS_ERROR_FAILURE; |
476 | 1.06M | } |
477 | 4.99k | |
478 | 4.99k | // Max values specified by the spec |
479 | 4.99k | static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu, |
480 | 4.99k | 0xffffu, 0xffu}; |
481 | 4.99k | uint32_t ipv4; |
482 | 4.99k | int32_t start = (dotCount > 0 ? dotIndex[dotCount-1] + 1 : 0); |
483 | 4.99k | |
484 | 4.99k | nsresult res; |
485 | 4.99k | // Doing a special case for all items being base 10 gives ~35% speedup |
486 | 4.99k | res = (onlyBase10 ? |
487 | 2.44k | ParseIPv4Number10(Substring(host, start, length - start), |
488 | 2.44k | ipv4, upperBounds[dotCount]) : |
489 | 4.99k | ParseIPv4Number(Substring(host, start, length - start), |
490 | 2.55k | bases[dotCount], |
491 | 2.55k | ipv4, upperBounds[dotCount])); |
492 | 4.99k | if (NS_FAILED(res)) { |
493 | 397 | return NS_ERROR_FAILURE; |
494 | 397 | } |
495 | 4.59k | |
496 | 4.59k | int32_t lastUsed = -1; |
497 | 7.20k | for (int32_t i = 0; i < dotCount; i++) { |
498 | 3.21k | uint32_t number; |
499 | 3.21k | start = lastUsed + 1; |
500 | 3.21k | lastUsed = dotIndex[i]; |
501 | 3.21k | res = (onlyBase10 ? |
502 | 1.23k | ParseIPv4Number10(Substring(host, start, lastUsed - start), |
503 | 1.23k | number, 255) : |
504 | 3.21k | ParseIPv4Number(Substring(host, start, lastUsed - start), |
505 | 1.97k | bases[i], number, 255)); |
506 | 3.21k | if (NS_FAILED(res)) { |
507 | 609 | return NS_ERROR_FAILURE; |
508 | 609 | } |
509 | 2.60k | ipv4 += number << (8 * (3 - i)); |
510 | 2.60k | } |
511 | 4.59k | |
512 | 4.59k | uint8_t ipSegments[4]; |
513 | 3.99k | NetworkEndian::writeUint32(ipSegments, ipv4); |
514 | 3.99k | result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1], |
515 | 3.99k | ipSegments[2], ipSegments[3]); |
516 | 3.99k | return NS_OK; |
517 | 4.59k | } |
518 | | |
519 | | nsresult |
520 | | nsStandardURL::NormalizeIDN(const nsACString& host, nsCString& result) |
521 | 1.08M | { |
522 | 1.08M | // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8, |
523 | 1.08M | // then make sure it is normalized per IDN. |
524 | 1.08M | |
525 | 1.08M | // this function returns true if normalization succeeds. |
526 | 1.08M | |
527 | 1.08M | result.Truncate(); |
528 | 1.08M | nsresult rv; |
529 | 1.08M | |
530 | 1.08M | if (!gIDN) { |
531 | 0 | return NS_ERROR_UNEXPECTED; |
532 | 0 | } |
533 | 1.08M | |
534 | 1.08M | bool isAscii; |
535 | 1.08M | nsAutoCString normalized; |
536 | 1.08M | rv = gIDN->ConvertToDisplayIDN(host, &isAscii, normalized); |
537 | 1.08M | if (NS_FAILED(rv)) { |
538 | 6.65k | return rv; |
539 | 6.65k | } |
540 | 1.08M | |
541 | 1.08M | // The result is ASCII. No need to convert to ACE. |
542 | 1.08M | if (isAscii) { |
543 | 1.07M | result = normalized; |
544 | 1.07M | mCheckedIfHostA = true; |
545 | 1.07M | mDisplayHost.Truncate(); |
546 | 1.07M | return NS_OK; |
547 | 1.07M | } |
548 | 5.26k | |
549 | 5.26k | rv = gIDN->ConvertUTF8toACE(normalized, result); |
550 | 5.26k | if (NS_FAILED(rv)) { |
551 | 72 | return rv; |
552 | 72 | } |
553 | 5.19k | |
554 | 5.19k | mCheckedIfHostA = true; |
555 | 5.19k | mDisplayHost = normalized; |
556 | 5.19k | |
557 | 5.19k | return NS_OK; |
558 | 5.19k | } |
559 | | |
560 | | bool |
561 | | nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length) |
562 | 1.08M | { |
563 | 1.08M | if (!host || !*host) { |
564 | 278 | // Should not be NULL or empty string |
565 | 278 | return false; |
566 | 278 | } |
567 | 1.08M | |
568 | 1.08M | if (length != strlen(host)) { |
569 | 0 | // Embedded null |
570 | 0 | return false; |
571 | 0 | } |
572 | 1.08M | |
573 | 1.08M | bool openBracket = host[0] == '['; |
574 | 1.08M | bool closeBracket = host[length - 1] == ']'; |
575 | 1.08M | |
576 | 1.08M | if (openBracket && closeBracket) { |
577 | 2.70k | return net_IsValidIPv6Addr(host + 1, length - 2); |
578 | 2.70k | } |
579 | 1.07M | |
580 | 1.07M | if (openBracket || closeBracket) { |
581 | 1.47k | // Fail if only one of the brackets is present |
582 | 1.47k | return false; |
583 | 1.47k | } |
584 | 1.07M | |
585 | 1.07M | const char* end = host + length; |
586 | 1.07M | const char* iter = host; |
587 | 13.1M | for (; iter != end && *iter; ++iter) { |
588 | 12.0M | if (ASCIIMask::IsMasked(sInvalidHostChars, *iter)) { |
589 | 6.90k | return false; |
590 | 6.90k | } |
591 | 12.0M | } |
592 | 1.07M | return true; |
593 | 1.07M | } |
594 | | |
595 | | void |
596 | | nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path) |
597 | 25.7k | { |
598 | 25.7k | net_CoalesceDirs(coalesceFlag, path); |
599 | 25.7k | int32_t newLen = strlen(path); |
600 | 25.7k | if (newLen < mPath.mLen) { |
601 | 1.00k | int32_t diff = newLen - mPath.mLen; |
602 | 1.00k | mPath.mLen = newLen; |
603 | 1.00k | mDirectory.mLen += diff; |
604 | 1.00k | mFilepath.mLen += diff; |
605 | 1.00k | ShiftFromBasename(diff); |
606 | 1.00k | } |
607 | 25.7k | } |
608 | | |
609 | | uint32_t |
610 | | nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str, |
611 | | const URLSegment &segInput, URLSegment &segOutput, |
612 | | const nsCString *escapedStr, |
613 | | bool useEscaped, int32_t *diff) |
614 | 4.45M | { |
615 | 4.45M | MOZ_ASSERT(segInput.mLen == segOutput.mLen); |
616 | 4.45M | |
617 | 4.45M | if (diff) *diff = 0; |
618 | 4.45M | |
619 | 4.45M | if (segInput.mLen > 0) { |
620 | 4.05M | if (useEscaped) { |
621 | 1.45M | MOZ_ASSERT(diff); |
622 | 1.45M | segOutput.mLen = escapedStr->Length(); |
623 | 1.45M | *diff = segOutput.mLen - segInput.mLen; |
624 | 1.45M | memcpy(buf + i, escapedStr->get(), segOutput.mLen); |
625 | 2.59M | } else { |
626 | 2.59M | memcpy(buf + i, str + segInput.mPos, segInput.mLen); |
627 | 2.59M | } |
628 | 4.05M | segOutput.mPos = i; |
629 | 4.05M | i += segOutput.mLen; |
630 | 4.05M | } else { |
631 | 399k | segOutput.mPos = i; |
632 | 399k | } |
633 | 4.45M | return i; |
634 | 4.45M | } |
635 | | |
636 | | uint32_t |
637 | | nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len) |
638 | 1.10M | { |
639 | 1.10M | memcpy(buf + i, str, len); |
640 | 1.10M | return i + len; |
641 | 1.10M | } |
642 | | |
643 | | // basic algorithm: |
644 | | // 1- escape url segments (for improved GetSpec efficiency) |
645 | | // 2- allocate spec buffer |
646 | | // 3- write url segments |
647 | | // 4- update url segment positions and lengths |
648 | | nsresult |
649 | | nsStandardURL::BuildNormalizedSpec(const char *spec, |
650 | | const Encoding* encoding) |
651 | 1.12M | { |
652 | 1.12M | // Assumptions: all member URLSegments must be relative the |spec| argument |
653 | 1.12M | // passed to this function. |
654 | 1.12M | |
655 | 1.12M | // buffers for holding escaped url segments (these will remain empty unless |
656 | 1.12M | // escaping is required). |
657 | 1.12M | nsAutoCString encUsername, encPassword, encHost, encDirectory, |
658 | 1.12M | encBasename, encExtension, encQuery, encRef; |
659 | 1.12M | bool useEncUsername, useEncPassword, useEncHost = false, |
660 | 1.12M | useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef; |
661 | 1.12M | nsAutoCString portbuf; |
662 | 1.12M | |
663 | 1.12M | // |
664 | 1.12M | // escape each URL segment, if necessary, and calculate approximate normalized |
665 | 1.12M | // spec length. |
666 | 1.12M | // |
667 | 1.12M | // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref] |
668 | 1.12M | |
669 | 1.12M | uint32_t approxLen = 0; |
670 | 1.12M | |
671 | 1.12M | // the scheme is already ASCII |
672 | 1.12M | if (mScheme.mLen > 0) |
673 | 1.12M | approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always |
674 | 1.12M | |
675 | 1.12M | // encode URL segments; convert UTF-8 to origin charset and possibly escape. |
676 | 1.12M | // results written to encXXX variables only if |spec| is not already in the |
677 | 1.12M | // appropriate encoding. |
678 | 1.12M | { |
679 | 1.12M | nsSegmentEncoder encoder; |
680 | 1.12M | nsSegmentEncoder queryEncoder(encoding); |
681 | 1.12M | // Items using an extraLen of 1 don't add anything unless mLen > 0 |
682 | 1.12M | // Username@ |
683 | 1.12M | approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1); |
684 | 1.12M | // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec |
685 | 1.12M | if (mPassword.mLen >= 0) |
686 | 1.47k | approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword); |
687 | 1.12M | // mHost is handled differently below due to encoding differences |
688 | 1.12M | MOZ_ASSERT(mPort >= -1, "Invalid negative mPort"); |
689 | 1.12M | if (mPort != -1 && mPort != mDefaultPort) |
690 | 404 | { |
691 | 404 | // :port |
692 | 404 | portbuf.AppendInt(mPort); |
693 | 404 | approxLen += portbuf.Length() + 1; |
694 | 404 | } |
695 | 1.12M | |
696 | 1.12M | approxLen += 1; // reserve space for possible leading '/' - may not be needed |
697 | 1.12M | // Should just use mPath? These are pessimistic, and thus waste space |
698 | 1.12M | approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1); |
699 | 1.12M | approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename); |
700 | 1.12M | approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1); |
701 | 1.12M | |
702 | 1.12M | // These next ones *always* add their leading character even if length is 0 |
703 | 1.12M | // Handles items like "http://#" |
704 | 1.12M | // ?query |
705 | 1.12M | if (mQuery.mLen >= 0) |
706 | 120k | approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery); |
707 | 1.12M | // #ref |
708 | 1.12M | |
709 | 1.12M | if (mRef.mLen >= 0) { |
710 | 4.95k | approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref, |
711 | 4.95k | encRef, useEncRef); |
712 | 4.95k | } |
713 | 1.12M | } |
714 | 1.12M | |
715 | 1.12M | // do not escape the hostname, if IPv6 address literal, mHost will |
716 | 1.12M | // already point to a [ ] delimited IPv6 address literal. |
717 | 1.12M | // However, perform Unicode normalization on it, as IDN does. |
718 | 1.12M | // Note that we don't disallow URLs without a host - file:, etc |
719 | 1.12M | if (mHost.mLen > 0) { |
720 | 1.08M | nsAutoCString tempHost; |
721 | 1.08M | NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost); |
722 | 1.08M | if (tempHost.Contains('\0')) |
723 | 242 | return NS_ERROR_MALFORMED_URI; // null embedded in hostname |
724 | 1.08M | if (tempHost.Contains(' ')) |
725 | 0 | return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname |
726 | 1.08M | nsresult rv = NormalizeIDN(tempHost, encHost); |
727 | 1.08M | if (NS_FAILED(rv)) { |
728 | 6.72k | return rv; |
729 | 6.72k | } |
730 | 1.08M | if (!SegmentIs(spec, mScheme, "resource") && |
731 | 1.08M | !SegmentIs(spec, mScheme, "chrome")) { |
732 | 1.07M | nsAutoCString ipString; |
733 | 1.07M | if (encHost.Length() > 0 && |
734 | 1.07M | encHost.First() == '[' && encHost.Last() == ']' && |
735 | 1.07M | ValidIPv6orHostname(encHost.get(), encHost.Length())) { |
736 | 581 | rv = (nsresult) rusturl_parse_ipv6addr(&encHost, &ipString); |
737 | 581 | if (NS_FAILED(rv)) { |
738 | 0 | return rv; |
739 | 0 | } |
740 | 581 | encHost = ipString; |
741 | 1.07M | } else if (NS_SUCCEEDED(NormalizeIPv4(encHost, ipString))) { |
742 | 3.99k | encHost = ipString; |
743 | 3.99k | } |
744 | 1.07M | } |
745 | 1.08M | |
746 | 1.08M | // NormalizeIDN always copies, if the call was successful. |
747 | 1.08M | useEncHost = true; |
748 | 1.08M | approxLen += encHost.Length(); |
749 | 1.08M | |
750 | 1.08M | if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) { |
751 | 9.42k | return NS_ERROR_MALFORMED_URI; |
752 | 9.42k | } |
753 | 33.2k | } else { |
754 | 33.2k | // empty host means empty mDisplayHost |
755 | 33.2k | mDisplayHost.Truncate(); |
756 | 33.2k | mCheckedIfHostA = true; |
757 | 33.2k | } |
758 | 1.12M | |
759 | 1.12M | // We must take a copy of every single segment because they are pointing to |
760 | 1.12M | // the |spec| while we are changing their value, in case we must use |
761 | 1.12M | // encoded strings. |
762 | 1.12M | URLSegment username(mUsername); |
763 | 1.10M | URLSegment password(mPassword); |
764 | 1.10M | URLSegment host(mHost); |
765 | 1.10M | URLSegment path(mPath); |
766 | 1.10M | URLSegment directory(mDirectory); |
767 | 1.10M | URLSegment basename(mBasename); |
768 | 1.10M | URLSegment extension(mExtension); |
769 | 1.10M | URLSegment query(mQuery); |
770 | 1.10M | URLSegment ref(mRef); |
771 | 1.10M | |
772 | 1.10M | // The encoded string could be longer than the original input, so we need |
773 | 1.10M | // to check the final URI isn't longer than the max length. |
774 | 1.10M | if (approxLen + 1 > (uint32_t) net_GetURLMaxLength()) { |
775 | 0 | return NS_ERROR_MALFORMED_URI; |
776 | 0 | } |
777 | 1.10M | |
778 | 1.10M | // |
779 | 1.10M | // generate the normalized URL string |
780 | 1.10M | // |
781 | 1.10M | // approxLen should be correct or 1 high |
782 | 1.10M | if (!mSpec.SetLength(approxLen+1, fallible)) // buf needs a trailing '\0' below |
783 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
784 | 1.10M | char *buf = mSpec.BeginWriting(); |
785 | 1.10M | uint32_t i = 0; |
786 | 1.10M | int32_t diff = 0; |
787 | 1.10M | |
788 | 1.10M | if (mScheme.mLen > 0) { |
789 | 1.10M | i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme); |
790 | 1.10M | net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen); |
791 | 1.10M | i = AppendToBuf(buf, i, "://", 3); |
792 | 1.10M | } |
793 | 1.10M | |
794 | 1.10M | // record authority starting position |
795 | 1.10M | mAuthority.mPos = i; |
796 | 1.10M | |
797 | 1.10M | // append authority |
798 | 1.10M | if (mUsername.mLen > 0) { |
799 | 2.80k | i = AppendSegmentToBuf(buf, i, spec, username, mUsername, |
800 | 2.80k | &encUsername, useEncUsername, &diff); |
801 | 2.80k | ShiftFromPassword(diff); |
802 | 2.80k | if (password.mLen > 0) { |
803 | 1.44k | buf[i++] = ':'; |
804 | 1.44k | i = AppendSegmentToBuf(buf, i, spec, password, mPassword, |
805 | 1.44k | &encPassword, useEncPassword, &diff); |
806 | 1.44k | ShiftFromHost(diff); |
807 | 1.44k | } else { |
808 | 1.36k | mPassword.mLen = -1; |
809 | 1.36k | } |
810 | 2.80k | buf[i++] = '@'; |
811 | 2.80k | } |
812 | 1.10M | if (host.mLen > 0) { |
813 | 1.07M | i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost, |
814 | 1.07M | &diff); |
815 | 1.07M | ShiftFromPath(diff); |
816 | 1.07M | |
817 | 1.07M | net_ToLowerCase(buf + mHost.mPos, mHost.mLen); |
818 | 1.07M | MOZ_ASSERT(mPort >= -1, "Invalid negative mPort"); |
819 | 1.07M | if (mPort != -1 && mPort != mDefaultPort) { |
820 | 398 | buf[i++] = ':'; |
821 | 398 | // Already formatted while building approxLen |
822 | 398 | i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length()); |
823 | 398 | } |
824 | 1.07M | } |
825 | 1.10M | |
826 | 1.10M | // record authority length |
827 | 1.10M | mAuthority.mLen = i - mAuthority.mPos; |
828 | 1.10M | |
829 | 1.10M | // path must always start with a "/" |
830 | 1.10M | if (mPath.mLen <= 0) { |
831 | 32.5k | LOG(("setting path=/")); |
832 | 32.5k | mDirectory.mPos = mFilepath.mPos = mPath.mPos = i; |
833 | 32.5k | mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1; |
834 | 32.5k | // basename must exist, even if empty (bug 113508) |
835 | 32.5k | mBasename.mPos = i+1; |
836 | 32.5k | mBasename.mLen = 0; |
837 | 32.5k | buf[i++] = '/'; |
838 | 32.5k | } |
839 | 1.07M | else { |
840 | 1.07M | uint32_t leadingSlash = 0; |
841 | 1.07M | if (spec[path.mPos] != '/') { |
842 | 5.29k | LOG(("adding leading slash to path\n")); |
843 | 5.29k | leadingSlash = 1; |
844 | 5.29k | buf[i++] = '/'; |
845 | 5.29k | // basename must exist, even if empty (bugs 113508, 429347) |
846 | 5.29k | if (mBasename.mLen == -1) { |
847 | 3.54k | mBasename.mPos = basename.mPos = i; |
848 | 3.54k | mBasename.mLen = basename.mLen = 0; |
849 | 3.54k | } |
850 | 5.29k | } |
851 | 1.07M | |
852 | 1.07M | // record corrected (file)path starting position |
853 | 1.07M | mPath.mPos = mFilepath.mPos = i - leadingSlash; |
854 | 1.07M | |
855 | 1.07M | i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory, |
856 | 1.07M | &encDirectory, useEncDirectory, &diff); |
857 | 1.07M | ShiftFromBasename(diff); |
858 | 1.07M | |
859 | 1.07M | // the directory must end with a '/' |
860 | 1.07M | if (buf[i-1] != '/') { |
861 | 653 | buf[i++] = '/'; |
862 | 653 | mDirectory.mLen++; |
863 | 653 | } |
864 | 1.07M | |
865 | 1.07M | i = AppendSegmentToBuf(buf, i, spec, basename, mBasename, |
866 | 1.07M | &encBasename, useEncBasename, &diff); |
867 | 1.07M | ShiftFromExtension(diff); |
868 | 1.07M | |
869 | 1.07M | // make corrections to directory segment if leadingSlash |
870 | 1.07M | if (leadingSlash) { |
871 | 5.29k | mDirectory.mPos = mPath.mPos; |
872 | 5.29k | if (mDirectory.mLen >= 0) |
873 | 550 | mDirectory.mLen += leadingSlash; |
874 | 4.74k | else |
875 | 4.74k | mDirectory.mLen = 1; |
876 | 5.29k | } |
877 | 1.07M | |
878 | 1.07M | if (mExtension.mLen >= 0) { |
879 | 2.46k | buf[i++] = '.'; |
880 | 2.46k | i = AppendSegmentToBuf(buf, i, spec, extension, mExtension, |
881 | 2.46k | &encExtension, useEncExtension, &diff); |
882 | 2.46k | ShiftFromQuery(diff); |
883 | 2.46k | } |
884 | 1.07M | // calculate corrected filepath length |
885 | 1.07M | mFilepath.mLen = i - mFilepath.mPos; |
886 | 1.07M | |
887 | 1.07M | if (mQuery.mLen >= 0) { |
888 | 119k | buf[i++] = '?'; |
889 | 119k | i = AppendSegmentToBuf(buf, i, spec, query, mQuery, |
890 | 119k | &encQuery, useEncQuery, |
891 | 119k | &diff); |
892 | 119k | ShiftFromRef(diff); |
893 | 119k | } |
894 | 1.07M | if (mRef.mLen >= 0) { |
895 | 4.92k | buf[i++] = '#'; |
896 | 4.92k | i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef, |
897 | 4.92k | &diff); |
898 | 4.92k | } |
899 | 1.07M | // calculate corrected path length |
900 | 1.07M | mPath.mLen = i - mPath.mPos; |
901 | 1.07M | } |
902 | 1.10M | |
903 | 1.10M | buf[i] = '\0'; |
904 | 1.10M | |
905 | 1.10M | // https://url.spec.whatwg.org/#path-state (1.4.1.2) |
906 | 1.10M | // https://url.spec.whatwg.org/#windows-drive-letter |
907 | 1.10M | if (SegmentIs(buf, mScheme, "file")) { |
908 | 4.48k | char* path = &buf[mPath.mPos]; |
909 | 4.48k | if (mPath.mLen >= 3 && path[0] == '/' |
910 | 4.48k | && IsAsciiAlpha(path[1]) |
911 | 4.48k | && path[2] == '|') { |
912 | 258 | buf[mPath.mPos + 2] = ':'; |
913 | 258 | } |
914 | 4.48k | } |
915 | 1.10M | |
916 | 1.10M | if (mDirectory.mLen > 1) { |
917 | 25.7k | netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; |
918 | 25.7k | if (SegmentIs(buf,mScheme,"ftp")) { |
919 | 1.06k | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
920 | 1.06k | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
921 | 1.06k | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
922 | 1.06k | } |
923 | 25.7k | CoalescePath(coalesceFlag, buf + mDirectory.mPos); |
924 | 25.7k | } |
925 | 1.10M | mSpec.SetLength(strlen(buf)); |
926 | 1.10M | NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!"); |
927 | 1.10M | MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), |
928 | 1.10M | "The spec should never be this long, we missed a check."); |
929 | 1.10M | |
930 | 1.10M | return NS_OK; |
931 | 1.10M | } |
932 | | |
933 | | bool |
934 | | nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase) |
935 | 1.08M | { |
936 | 1.08M | // one or both may be null |
937 | 1.08M | if (!val || mSpec.IsEmpty()) |
938 | 0 | return (!val && (mSpec.IsEmpty() || seg.mLen < 0)); |
939 | 1.08M | if (seg.mLen < 0) |
940 | 0 | return false; |
941 | 1.08M | // if the first |seg.mLen| chars of |val| match, then |val| must |
942 | 1.08M | // also be null terminated at |seg.mLen|. |
943 | 1.08M | if (ignoreCase) |
944 | 0 | return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen) |
945 | 0 | && (val[seg.mLen] == '\0'); |
946 | 1.08M | |
947 | 1.08M | return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen) && |
948 | 1.08M | (val[seg.mLen] == '\0'); |
949 | 1.08M | } |
950 | | |
951 | | bool |
952 | | nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase) |
953 | 4.96M | { |
954 | 4.96M | // one or both may be null |
955 | 4.96M | if (!val || !spec) |
956 | 0 | return (!val && (!spec || seg.mLen < 0)); |
957 | 4.96M | if (seg.mLen < 0) |
958 | 0 | return false; |
959 | 4.96M | // if the first |seg.mLen| chars of |val| match, then |val| must |
960 | 4.96M | // also be null terminated at |seg.mLen|. |
961 | 4.96M | if (ignoreCase) |
962 | 1.67M | return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen) |
963 | 1.67M | && (val[seg.mLen] == '\0'); |
964 | 3.29M | |
965 | 3.29M | return !strncmp(spec + seg.mPos, val, seg.mLen) && (val[seg.mLen] == '\0'); |
966 | 3.29M | } |
967 | | |
968 | | bool |
969 | | nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase) |
970 | 1.67M | { |
971 | 1.67M | if (seg1.mLen != seg2.mLen) |
972 | 1.67M | return false; |
973 | 2.69k | if (seg1.mLen == -1 || (!val && mSpec.IsEmpty())) |
974 | 0 | return true; // both are empty |
975 | 2.69k | if (!val) |
976 | 0 | return false; |
977 | 2.69k | if (ignoreCase) |
978 | 2.69k | return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); |
979 | 0 | |
980 | 0 | return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); |
981 | 0 | } |
982 | | |
983 | | int32_t |
984 | | nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen) |
985 | 2.32k | { |
986 | 2.32k | if (val && valLen) { |
987 | 1.69k | if (len == 0) |
988 | 1.69k | mSpec.Insert(val, pos, valLen); |
989 | 0 | else |
990 | 0 | mSpec.Replace(pos, len, nsDependentCString(val, valLen)); |
991 | 1.69k | return valLen - len; |
992 | 1.69k | } |
993 | 632 | |
994 | 632 | // else remove the specified segment |
995 | 632 | mSpec.Cut(pos, len); |
996 | 632 | return -int32_t(len); |
997 | 632 | } |
998 | | |
999 | | int32_t |
1000 | | nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val) |
1001 | 0 | { |
1002 | 0 | if (len == 0) |
1003 | 0 | mSpec.Insert(val, pos); |
1004 | 0 | else |
1005 | 0 | mSpec.Replace(pos, len, val); |
1006 | 0 | return val.Length() - len; |
1007 | 0 | } |
1008 | | |
1009 | | nsresult |
1010 | | nsStandardURL::ParseURL(const char *spec, int32_t specLen) |
1011 | 1.12M | { |
1012 | 1.12M | nsresult rv; |
1013 | 1.12M | |
1014 | 1.12M | if (specLen > net_GetURLMaxLength()) { |
1015 | 0 | return NS_ERROR_MALFORMED_URI; |
1016 | 0 | } |
1017 | 1.12M | |
1018 | 1.12M | // |
1019 | 1.12M | // parse given URL string |
1020 | 1.12M | // |
1021 | 1.12M | rv = mParser->ParseURL(spec, specLen, |
1022 | 1.12M | &mScheme.mPos, &mScheme.mLen, |
1023 | 1.12M | &mAuthority.mPos, &mAuthority.mLen, |
1024 | 1.12M | &mPath.mPos, &mPath.mLen); |
1025 | 1.12M | if (NS_FAILED(rv)) return rv; |
1026 | 1.12M | |
1027 | | #ifdef DEBUG |
1028 | | if (mScheme.mLen <= 0) { |
1029 | | printf("spec=%s\n", spec); |
1030 | | NS_WARNING("malformed url: no scheme"); |
1031 | | } |
1032 | | #endif |
1033 | | |
1034 | 1.12M | if (mAuthority.mLen > 0) { |
1035 | 1.09M | rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen, |
1036 | 1.09M | &mUsername.mPos, &mUsername.mLen, |
1037 | 1.09M | &mPassword.mPos, &mPassword.mLen, |
1038 | 1.09M | &mHost.mPos, &mHost.mLen, |
1039 | 1.09M | &mPort); |
1040 | 1.09M | if (NS_FAILED(rv)) return rv; |
1041 | 1.08M | |
1042 | 1.08M | // Don't allow mPort to be set to this URI's default port |
1043 | 1.08M | if (mPort == mDefaultPort) |
1044 | 9.58k | mPort = -1; |
1045 | 1.08M | |
1046 | 1.08M | mUsername.mPos += mAuthority.mPos; |
1047 | 1.08M | mPassword.mPos += mAuthority.mPos; |
1048 | 1.08M | mHost.mPos += mAuthority.mPos; |
1049 | 1.08M | } |
1050 | 1.12M | |
1051 | 1.12M | if (mPath.mLen > 0) |
1052 | 1.07M | rv = ParsePath(spec, mPath.mPos, mPath.mLen); |
1053 | 1.12M | |
1054 | 1.12M | return rv; |
1055 | 1.12M | } |
1056 | | |
1057 | | nsresult |
1058 | | nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen) |
1059 | 1.07M | { |
1060 | 1.07M | LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen)); |
1061 | 1.07M | |
1062 | 1.07M | if (pathLen > net_GetURLMaxLength()) { |
1063 | 0 | return NS_ERROR_MALFORMED_URI; |
1064 | 0 | } |
1065 | 1.07M | |
1066 | 1.07M | nsresult rv = mParser->ParsePath(spec + pathPos, pathLen, |
1067 | 1.07M | &mFilepath.mPos, &mFilepath.mLen, |
1068 | 1.07M | &mQuery.mPos, &mQuery.mLen, |
1069 | 1.07M | &mRef.mPos, &mRef.mLen); |
1070 | 1.07M | if (NS_FAILED(rv)) return rv; |
1071 | 1.07M | |
1072 | 1.07M | mFilepath.mPos += pathPos; |
1073 | 1.07M | mQuery.mPos += pathPos; |
1074 | 1.07M | mRef.mPos += pathPos; |
1075 | 1.07M | |
1076 | 1.07M | if (mFilepath.mLen > 0) { |
1077 | 1.07M | rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen, |
1078 | 1.07M | &mDirectory.mPos, &mDirectory.mLen, |
1079 | 1.07M | &mBasename.mPos, &mBasename.mLen, |
1080 | 1.07M | &mExtension.mPos, &mExtension.mLen); |
1081 | 1.07M | if (NS_FAILED(rv)) return rv; |
1082 | 1.07M | |
1083 | 1.07M | mDirectory.mPos += mFilepath.mPos; |
1084 | 1.07M | mBasename.mPos += mFilepath.mPos; |
1085 | 1.07M | mExtension.mPos += mFilepath.mPos; |
1086 | 1.07M | } |
1087 | 1.07M | return NS_OK; |
1088 | 1.07M | } |
1089 | | |
1090 | | char * |
1091 | | nsStandardURL::AppendToSubstring(uint32_t pos, |
1092 | | int32_t len, |
1093 | | const char *tail) |
1094 | 1.06M | { |
1095 | 1.06M | // Verify pos and length are within boundaries |
1096 | 1.06M | if (pos > mSpec.Length()) |
1097 | 0 | return nullptr; |
1098 | 1.06M | if (len < 0) |
1099 | 0 | return nullptr; |
1100 | 1.06M | if ((uint32_t)len > (mSpec.Length() - pos)) |
1101 | 0 | return nullptr; |
1102 | 1.06M | if (!tail) |
1103 | 0 | return nullptr; |
1104 | 1.06M | |
1105 | 1.06M | uint32_t tailLen = strlen(tail); |
1106 | 1.06M | |
1107 | 1.06M | // Check for int overflow for proposed length of combined string |
1108 | 1.06M | if (UINT32_MAX - ((uint32_t)len + 1) < tailLen) |
1109 | 0 | return nullptr; |
1110 | 1.06M | |
1111 | 1.06M | char *result = (char *) moz_xmalloc(len + tailLen + 1); |
1112 | 1.06M | memcpy(result, mSpec.get() + pos, len); |
1113 | 1.06M | memcpy(result + len, tail, tailLen); |
1114 | 1.06M | result[len + tailLen] = '\0'; |
1115 | 1.06M | return result; |
1116 | 1.06M | } |
1117 | | |
1118 | | nsresult |
1119 | | nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg) |
1120 | 0 | { |
1121 | 0 | nsresult rv; |
1122 | 0 |
|
1123 | 0 | rv = stream->Read32(&seg.mPos); |
1124 | 0 | if (NS_FAILED(rv)) return rv; |
1125 | 0 | |
1126 | 0 | rv = stream->Read32((uint32_t *) &seg.mLen); |
1127 | 0 | if (NS_FAILED(rv)) return rv; |
1128 | 0 | |
1129 | 0 | return NS_OK; |
1130 | 0 | } |
1131 | | |
1132 | | nsresult |
1133 | | nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg) |
1134 | 0 | { |
1135 | 0 | nsresult rv; |
1136 | 0 |
|
1137 | 0 | rv = stream->Write32(seg.mPos); |
1138 | 0 | if (NS_FAILED(rv)) return rv; |
1139 | 0 | |
1140 | 0 | rv = stream->Write32(uint32_t(seg.mLen)); |
1141 | 0 | if (NS_FAILED(rv)) return rv; |
1142 | 0 | |
1143 | 0 | return NS_OK; |
1144 | 0 | } |
1145 | | |
1146 | | #define SHIFT_FROM(name, what) \ |
1147 | | void \ |
1148 | 4.21M | nsStandardURL::name(int32_t diff) \ |
1149 | 4.21M | { \ |
1150 | 4.21M | if (!diff) return; \ |
1151 | 4.21M | if (what.mLen >= 0) { \ |
1152 | 144k | CheckedInt<int32_t> pos = what.mPos; \ |
1153 | 144k | pos += diff; \ |
1154 | 144k | MOZ_ASSERT(pos.isValid()); \ |
1155 | 144k | what.mPos = pos.value(); \ |
1156 | 144k | } |
1157 | | |
1158 | | #define SHIFT_FROM_NEXT(name, what, next) \ |
1159 | 4.56M | SHIFT_FROM(name, what) \ |
1160 | 4.56M | next(diff); \ |
1161 | 4.56M | } Unexecuted instantiation: mozilla::net::nsStandardURL::ShiftFromAuthority(int) Unexecuted instantiation: mozilla::net::nsStandardURL::ShiftFromUsername(int) mozilla::net::nsStandardURL::ShiftFromPassword(int) Line | Count | Source | 1159 | 3.95k | SHIFT_FROM(name, what) \ | 1160 | 3.95k | next(diff); \ | 1161 | 3.95k | } |
mozilla::net::nsStandardURL::ShiftFromHost(int) Line | Count | Source | 1159 | 4.00k | SHIFT_FROM(name, what) \ | 1160 | 4.00k | next(diff); \ | 1161 | 4.00k | } |
mozilla::net::nsStandardURL::ShiftFromPath(int) Line | Count | Source | 1159 | 1.08M | SHIFT_FROM(name, what) \ | 1160 | 1.08M | next(diff); \ | 1161 | 1.08M | } |
mozilla::net::nsStandardURL::ShiftFromFilepath(int) Line | Count | Source | 1159 | 32.1k | SHIFT_FROM(name, what) \ | 1160 | 32.1k | next(diff); \ | 1161 | 32.1k | } |
mozilla::net::nsStandardURL::ShiftFromDirectory(int) Line | Count | Source | 1159 | 32.1k | SHIFT_FROM(name, what) \ | 1160 | 32.1k | next(diff); \ | 1161 | 32.1k | } |
mozilla::net::nsStandardURL::ShiftFromBasename(int) Line | Count | Source | 1159 | 1.11M | SHIFT_FROM(name, what) \ | 1160 | 1.11M | next(diff); \ | 1161 | 1.11M | } |
mozilla::net::nsStandardURL::ShiftFromExtension(int) Line | Count | Source | 1159 | 1.49M | SHIFT_FROM(name, what) \ | 1160 | 1.49M | next(diff); \ | 1161 | 1.49M | } |
mozilla::net::nsStandardURL::ShiftFromQuery(int) Line | Count | Source | 1159 | 801k | SHIFT_FROM(name, what) \ | 1160 | 801k | next(diff); \ | 1161 | 801k | } |
|
1162 | | |
1163 | | #define SHIFT_FROM_LAST(name, what) \ |
1164 | 919k | SHIFT_FROM(name, what) \ |
1165 | 919k | } |
1166 | | |
1167 | | SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername) |
1168 | | SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword) |
1169 | | SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost) |
1170 | | SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath) |
1171 | | SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath) |
1172 | | SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory) |
1173 | | SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename) |
1174 | | SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension) |
1175 | | SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery) |
1176 | | SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef) |
1177 | | SHIFT_FROM_LAST(ShiftFromRef, mRef) |
1178 | | |
1179 | | //---------------------------------------------------------------------------- |
1180 | | // nsStandardURL::nsISupports |
1181 | | //---------------------------------------------------------------------------- |
1182 | | |
1183 | | NS_IMPL_ADDREF(nsStandardURL) |
1184 | | NS_IMPL_RELEASE(nsStandardURL) |
1185 | | |
1186 | 4.91M | NS_INTERFACE_MAP_BEGIN(nsStandardURL) |
1187 | 4.91M | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL) |
1188 | 4.91M | NS_INTERFACE_MAP_ENTRY(nsIURI) |
1189 | 3.28M | NS_INTERFACE_MAP_ENTRY(nsIURL) |
1190 | 3.28M | NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL) |
1191 | 3.28M | NS_INTERFACE_MAP_ENTRY(nsIStandardURL) |
1192 | 3.28M | NS_INTERFACE_MAP_ENTRY(nsISerializable) |
1193 | 3.27M | NS_INTERFACE_MAP_ENTRY(nsIClassInfo) |
1194 | 3.27M | NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI) |
1195 | 3.27M | NS_INTERFACE_MAP_ENTRY(nsISensitiveInfoHiddenURI) |
1196 | 3.27M | // see nsStandardURL::Equals |
1197 | 3.27M | if (aIID.Equals(kThisImplCID)) |
1198 | 0 | foundInterface = static_cast<nsIURI *>(this); |
1199 | 3.27M | else |
1200 | 3.27M | NS_INTERFACE_MAP_ENTRY(nsISizeOf) |
1201 | 3.27M | NS_INTERFACE_MAP_END |
1202 | | |
1203 | | //---------------------------------------------------------------------------- |
1204 | | // nsStandardURL::nsIURI |
1205 | | //---------------------------------------------------------------------------- |
1206 | | |
1207 | | // result may contain unescaped UTF-8 characters |
1208 | | NS_IMETHODIMP |
1209 | | nsStandardURL::GetSpec(nsACString &result) |
1210 | 332 | { |
1211 | 332 | MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), |
1212 | 332 | "The spec should never be this long, we missed a check."); |
1213 | 332 | nsresult rv = NS_OK; |
1214 | 332 | if (gPunycodeHost) { |
1215 | 332 | result = mSpec; |
1216 | 332 | } else { // XXX: This code path may be slow |
1217 | 0 | rv = GetDisplaySpec(result); |
1218 | 0 | } |
1219 | 332 | return rv; |
1220 | 332 | } |
1221 | | |
1222 | | // result may contain unescaped UTF-8 characters |
1223 | | NS_IMETHODIMP |
1224 | | nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result) |
1225 | 0 | { |
1226 | 0 | nsresult rv = GetSpec(result); |
1227 | 0 | if (NS_FAILED(rv)) { |
1228 | 0 | return rv; |
1229 | 0 | } |
1230 | 0 | if (mPassword.mLen >= 0) { |
1231 | 0 | result.ReplaceLiteral(mPassword.mPos, mPassword.mLen, "****"); |
1232 | 0 | } |
1233 | 0 | return NS_OK; |
1234 | 0 | } |
1235 | | |
1236 | | // result may contain unescaped UTF-8 characters |
1237 | | NS_IMETHODIMP |
1238 | | nsStandardURL::GetSpecIgnoringRef(nsACString &result) |
1239 | 0 | { |
1240 | 0 | // URI without ref is 0 to one char before ref |
1241 | 0 | if (mRef.mLen < 0) { |
1242 | 0 | return GetSpec(result); |
1243 | 0 | } |
1244 | 0 | |
1245 | 0 | URLSegment noRef(0, mRef.mPos - 1); |
1246 | 0 | result = Segment(noRef); |
1247 | 0 |
|
1248 | 0 | MOZ_ASSERT(mCheckedIfHostA); |
1249 | 0 | if (!gPunycodeHost && !mDisplayHost.IsEmpty()) { |
1250 | 0 | result.Replace(mHost.mPos, mHost.mLen, mDisplayHost); |
1251 | 0 | } |
1252 | 0 |
|
1253 | 0 | return NS_OK; |
1254 | 0 | } |
1255 | | |
1256 | | nsresult |
1257 | | nsStandardURL::CheckIfHostIsAscii() |
1258 | 0 | { |
1259 | 0 | nsresult rv; |
1260 | 0 | if (mCheckedIfHostA) { |
1261 | 0 | return NS_OK; |
1262 | 0 | } |
1263 | 0 | |
1264 | 0 | mCheckedIfHostA = true; |
1265 | 0 |
|
1266 | 0 | if (!gIDN) { |
1267 | 0 | return NS_ERROR_NOT_INITIALIZED; |
1268 | 0 | } |
1269 | 0 | |
1270 | 0 | nsAutoCString displayHost; |
1271 | 0 | bool isAscii; |
1272 | 0 | rv = gIDN->ConvertToDisplayIDN(Host(), &isAscii, displayHost); |
1273 | 0 | if (NS_FAILED(rv)) { |
1274 | 0 | mDisplayHost.Truncate(); |
1275 | 0 | mCheckedIfHostA = false; |
1276 | 0 | return rv; |
1277 | 0 | } |
1278 | 0 | |
1279 | 0 | if (!isAscii) { |
1280 | 0 | mDisplayHost = displayHost; |
1281 | 0 | } |
1282 | 0 |
|
1283 | 0 | return NS_OK; |
1284 | 0 | } |
1285 | | |
1286 | | NS_IMETHODIMP |
1287 | | nsStandardURL::GetDisplaySpec(nsACString &aUnicodeSpec) |
1288 | 0 | { |
1289 | 0 | aUnicodeSpec.Assign(mSpec); |
1290 | 0 | MOZ_ASSERT(mCheckedIfHostA); |
1291 | 0 | if (!mDisplayHost.IsEmpty()) { |
1292 | 0 | aUnicodeSpec.Replace(mHost.mPos, mHost.mLen, mDisplayHost); |
1293 | 0 | } |
1294 | 0 |
|
1295 | 0 | return NS_OK; |
1296 | 0 | } |
1297 | | |
1298 | | NS_IMETHODIMP |
1299 | | nsStandardURL::GetDisplayHostPort(nsACString &aUnicodeHostPort) |
1300 | 0 | { |
1301 | 0 | nsAutoCString unicodeHostPort; |
1302 | 0 |
|
1303 | 0 | nsresult rv = GetDisplayHost(unicodeHostPort); |
1304 | 0 | if (NS_FAILED(rv)) { |
1305 | 0 | return rv; |
1306 | 0 | } |
1307 | 0 | |
1308 | 0 | if (StringBeginsWith(Hostport(), NS_LITERAL_CSTRING("["))) { |
1309 | 0 | aUnicodeHostPort.AssignLiteral("["); |
1310 | 0 | aUnicodeHostPort.Append(unicodeHostPort); |
1311 | 0 | aUnicodeHostPort.AppendLiteral("]"); |
1312 | 0 | } else { |
1313 | 0 | aUnicodeHostPort.Assign(unicodeHostPort); |
1314 | 0 | } |
1315 | 0 |
|
1316 | 0 | uint32_t pos = mHost.mPos + mHost.mLen; |
1317 | 0 | if (pos < mPath.mPos) |
1318 | 0 | aUnicodeHostPort += Substring(mSpec, pos, mPath.mPos - pos); |
1319 | 0 |
|
1320 | 0 | return NS_OK; |
1321 | 0 | } |
1322 | | |
1323 | | NS_IMETHODIMP |
1324 | | nsStandardURL::GetDisplayHost(nsACString &aUnicodeHost) |
1325 | 0 | { |
1326 | 0 | MOZ_ASSERT(mCheckedIfHostA); |
1327 | 0 | if (mDisplayHost.IsEmpty()) { |
1328 | 0 | return GetAsciiHost(aUnicodeHost); |
1329 | 0 | } |
1330 | 0 | |
1331 | 0 | aUnicodeHost = mDisplayHost; |
1332 | 0 | return NS_OK; |
1333 | 0 | } |
1334 | | |
1335 | | |
1336 | | // result may contain unescaped UTF-8 characters |
1337 | | NS_IMETHODIMP |
1338 | | nsStandardURL::GetPrePath(nsACString &result) |
1339 | 0 | { |
1340 | 0 | result = Prepath(); |
1341 | 0 | MOZ_ASSERT(mCheckedIfHostA); |
1342 | 0 | if (!gPunycodeHost && !mDisplayHost.IsEmpty()) { |
1343 | 0 | result.Replace(mHost.mPos, mHost.mLen, mDisplayHost); |
1344 | 0 | } |
1345 | 0 | return NS_OK; |
1346 | 0 | } |
1347 | | |
1348 | | // result may contain unescaped UTF-8 characters |
1349 | | NS_IMETHODIMP |
1350 | | nsStandardURL::GetDisplayPrePath(nsACString &result) |
1351 | 0 | { |
1352 | 0 | result = Prepath(); |
1353 | 0 | MOZ_ASSERT(mCheckedIfHostA); |
1354 | 0 | if (!mDisplayHost.IsEmpty()) { |
1355 | 0 | result.Replace(mHost.mPos, mHost.mLen, mDisplayHost); |
1356 | 0 | } |
1357 | 0 | return NS_OK; |
1358 | 0 | } |
1359 | | |
1360 | | // result is strictly US-ASCII |
1361 | | NS_IMETHODIMP |
1362 | | nsStandardURL::GetScheme(nsACString &result) |
1363 | 1.27M | { |
1364 | 1.27M | result = Scheme(); |
1365 | 1.27M | return NS_OK; |
1366 | 1.27M | } |
1367 | | |
1368 | | // result may contain unescaped UTF-8 characters |
1369 | | NS_IMETHODIMP |
1370 | | nsStandardURL::GetUserPass(nsACString &result) |
1371 | 0 | { |
1372 | 0 | result = Userpass(); |
1373 | 0 | return NS_OK; |
1374 | 0 | } |
1375 | | |
1376 | | // result may contain unescaped UTF-8 characters |
1377 | | NS_IMETHODIMP |
1378 | | nsStandardURL::GetUsername(nsACString &result) |
1379 | 0 | { |
1380 | 0 | result = Username(); |
1381 | 0 | return NS_OK; |
1382 | 0 | } |
1383 | | |
1384 | | // result may contain unescaped UTF-8 characters |
1385 | | NS_IMETHODIMP |
1386 | | nsStandardURL::GetPassword(nsACString &result) |
1387 | 0 | { |
1388 | 0 | result = Password(); |
1389 | 0 | return NS_OK; |
1390 | 0 | } |
1391 | | |
1392 | | NS_IMETHODIMP |
1393 | | nsStandardURL::GetHostPort(nsACString &result) |
1394 | 0 | { |
1395 | 0 | nsresult rv; |
1396 | 0 | if (gPunycodeHost) { |
1397 | 0 | rv = GetAsciiHostPort(result); |
1398 | 0 | } else { |
1399 | 0 | rv = GetDisplayHostPort(result); |
1400 | 0 | } |
1401 | 0 | return rv; |
1402 | 0 | } |
1403 | | |
1404 | | NS_IMETHODIMP |
1405 | | nsStandardURL::GetHost(nsACString &result) |
1406 | 2.72k | { |
1407 | 2.72k | nsresult rv; |
1408 | 2.72k | if (gPunycodeHost) { |
1409 | 2.72k | rv = GetAsciiHost(result); |
1410 | 2.72k | } else { |
1411 | 0 | rv = GetDisplayHost(result); |
1412 | 0 | } |
1413 | 2.72k | return rv; |
1414 | 2.72k | } |
1415 | | |
1416 | | NS_IMETHODIMP |
1417 | | nsStandardURL::GetPort(int32_t *result) |
1418 | 289 | { |
1419 | 289 | // should never be more than 16 bit |
1420 | 289 | MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max()); |
1421 | 289 | *result = mPort; |
1422 | 289 | return NS_OK; |
1423 | 289 | } |
1424 | | |
1425 | | // result may contain unescaped UTF-8 characters |
1426 | | NS_IMETHODIMP |
1427 | | nsStandardURL::GetPathQueryRef(nsACString &result) |
1428 | 26.2k | { |
1429 | 26.2k | result = Path(); |
1430 | 26.2k | return NS_OK; |
1431 | 26.2k | } |
1432 | | |
1433 | | // result is ASCII |
1434 | | NS_IMETHODIMP |
1435 | | nsStandardURL::GetAsciiSpec(nsACString &result) |
1436 | 5 | { |
1437 | 5 | result = mSpec; |
1438 | 5 | return NS_OK; |
1439 | 5 | } |
1440 | | |
1441 | | // result is ASCII |
1442 | | NS_IMETHODIMP |
1443 | | nsStandardURL::GetAsciiHostPort(nsACString &result) |
1444 | 5.77k | { |
1445 | 5.77k | result = Hostport(); |
1446 | 5.77k | return NS_OK; |
1447 | 5.77k | } |
1448 | | |
1449 | | // result is ASCII |
1450 | | NS_IMETHODIMP |
1451 | | nsStandardURL::GetAsciiHost(nsACString &result) |
1452 | 3.04k | { |
1453 | 3.04k | result = Host(); |
1454 | 3.04k | return NS_OK; |
1455 | 3.04k | } |
1456 | | |
1457 | | static bool |
1458 | | IsSpecialProtocol(const nsACString &input) |
1459 | 3.86M | { |
1460 | 3.86M | nsACString::const_iterator start, end; |
1461 | 3.86M | input.BeginReading(start); |
1462 | 3.86M | nsACString::const_iterator iterator(start); |
1463 | 3.86M | input.EndReading(end); |
1464 | 3.86M | |
1465 | 17.6M | while (iterator != end && *iterator != ':') { |
1466 | 13.8M | iterator++; |
1467 | 13.8M | } |
1468 | 3.86M | |
1469 | 3.86M | nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get())); |
1470 | 3.86M | |
1471 | 3.86M | return protocol.LowerCaseEqualsLiteral("http") || |
1472 | 3.86M | protocol.LowerCaseEqualsLiteral("https") || |
1473 | 3.86M | protocol.LowerCaseEqualsLiteral("ftp") || |
1474 | 3.86M | protocol.LowerCaseEqualsLiteral("ws") || |
1475 | 3.86M | protocol.LowerCaseEqualsLiteral("wss") || |
1476 | 3.86M | protocol.LowerCaseEqualsLiteral("file") || |
1477 | 3.86M | protocol.LowerCaseEqualsLiteral("gopher"); |
1478 | 3.86M | } |
1479 | | |
1480 | | nsresult |
1481 | | nsStandardURL::SetSpecInternal(const nsACString &input) |
1482 | 1.59k | { |
1483 | 1.59k | return SetSpecWithEncoding(input, nullptr); |
1484 | 1.59k | } |
1485 | | |
1486 | | nsresult |
1487 | | nsStandardURL::SetSpecWithEncoding(const nsACString &input, |
1488 | | const Encoding* encoding) |
1489 | 1.12M | { |
1490 | 1.12M | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
1491 | 1.12M | LOG(("nsStandardURL::SetSpec [spec=%s]\n", flat.get())); |
1492 | 1.12M | |
1493 | 1.12M | if (input.Length() > (uint32_t) net_GetURLMaxLength()) { |
1494 | 0 | return NS_ERROR_MALFORMED_URI; |
1495 | 0 | } |
1496 | 1.12M | |
1497 | 1.12M | // filter out unexpected chars "\r\n\t" if necessary |
1498 | 1.12M | nsAutoCString filteredURI; |
1499 | 1.12M | net_FilterURIString(flat, filteredURI); |
1500 | 1.12M | |
1501 | 1.12M | if (filteredURI.Length() == 0) { |
1502 | 0 | return NS_ERROR_MALFORMED_URI; |
1503 | 0 | } |
1504 | 1.12M | |
1505 | 1.12M | // Make a backup of the curent URL |
1506 | 1.12M | nsStandardURL prevURL(false,false); |
1507 | 1.12M | prevURL.CopyMembers(this, eHonorRef, EmptyCString()); |
1508 | 1.12M | Clear(); |
1509 | 1.12M | |
1510 | 1.12M | if (IsSpecialProtocol(filteredURI)) { |
1511 | 1.09M | // Bug 652186: Replace all backslashes with slashes when parsing paths |
1512 | 1.09M | // Stop when we reach the query or the hash. |
1513 | 1.09M | auto start = filteredURI.BeginWriting(); |
1514 | 1.09M | auto end = filteredURI.EndWriting(); |
1515 | 30.4M | while (start != end) { |
1516 | 29.4M | if (*start == '?' || *start == '#') { |
1517 | 122k | break; |
1518 | 122k | } |
1519 | 29.3M | if (*start == '\\') { |
1520 | 5.56k | *start = '/'; |
1521 | 5.56k | } |
1522 | 29.3M | start++; |
1523 | 29.3M | } |
1524 | 1.09M | } |
1525 | 1.12M | |
1526 | 1.12M | const char *spec = filteredURI.get(); |
1527 | 1.12M | int32_t specLength = filteredURI.Length(); |
1528 | 1.12M | |
1529 | 1.12M | // parse the given URL... |
1530 | 1.12M | nsresult rv = ParseURL(spec, specLength); |
1531 | 1.12M | if (NS_SUCCEEDED(rv)) { |
1532 | 1.12M | // finally, use the URLSegment member variables to build a normalized |
1533 | 1.12M | // copy of |spec| |
1534 | 1.12M | rv = BuildNormalizedSpec(spec, encoding); |
1535 | 1.12M | } |
1536 | 1.12M | |
1537 | 1.12M | // Make sure that a URLTYPE_AUTHORITY has a non-empty hostname. |
1538 | 1.12M | if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) { |
1539 | 6.31k | rv = NS_ERROR_MALFORMED_URI; |
1540 | 6.31k | } |
1541 | 1.12M | |
1542 | 1.12M | if (NS_FAILED(rv)) { |
1543 | 28.6k | Clear(); |
1544 | 28.6k | // If parsing the spec has failed, restore the old URL |
1545 | 28.6k | // so we don't end up with an empty URL. |
1546 | 28.6k | CopyMembers(&prevURL, eHonorRef, EmptyCString()); |
1547 | 28.6k | return rv; |
1548 | 28.6k | } |
1549 | 1.09M | |
1550 | 1.09M | if (LOG_ENABLED()) { |
1551 | 0 | LOG((" spec = %s\n", mSpec.get())); |
1552 | 0 | LOG((" port = %d\n", mPort)); |
1553 | 0 | LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen)); |
1554 | 0 | LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen)); |
1555 | 0 | LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen)); |
1556 | 0 | LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen)); |
1557 | 0 | LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen)); |
1558 | 0 | LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen)); |
1559 | 0 | LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen)); |
1560 | 0 | LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen)); |
1561 | 0 | LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen)); |
1562 | 0 | LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen)); |
1563 | 0 | LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen)); |
1564 | 0 | LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen)); |
1565 | 0 | } |
1566 | 1.09M | |
1567 | 1.09M | return rv; |
1568 | 1.09M | } |
1569 | | |
1570 | | nsresult |
1571 | | nsStandardURL::SetScheme(const nsACString &input) |
1572 | 0 | { |
1573 | 0 | const nsPromiseFlatCString &scheme = PromiseFlatCString(input); |
1574 | 0 |
|
1575 | 0 | LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get())); |
1576 | 0 |
|
1577 | 0 | if (scheme.IsEmpty()) { |
1578 | 0 | NS_WARNING("cannot remove the scheme from an url"); |
1579 | 0 | return NS_ERROR_UNEXPECTED; |
1580 | 0 | } |
1581 | 0 | if (mScheme.mLen < 0) { |
1582 | 0 | NS_WARNING("uninitialized"); |
1583 | 0 | return NS_ERROR_NOT_INITIALIZED; |
1584 | 0 | } |
1585 | 0 |
|
1586 | 0 | if (!net_IsValidScheme(scheme)) { |
1587 | 0 | NS_WARNING("the given url scheme contains invalid characters"); |
1588 | 0 | return NS_ERROR_UNEXPECTED; |
1589 | 0 | } |
1590 | 0 |
|
1591 | 0 | if (mSpec.Length() + input.Length() - Scheme().Length() > (uint32_t) net_GetURLMaxLength()) { |
1592 | 0 | return NS_ERROR_MALFORMED_URI; |
1593 | 0 | } |
1594 | 0 | |
1595 | 0 | InvalidateCache(); |
1596 | 0 |
|
1597 | 0 | int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme); |
1598 | 0 |
|
1599 | 0 | if (shift) { |
1600 | 0 | mScheme.mLen = scheme.Length(); |
1601 | 0 | ShiftFromAuthority(shift); |
1602 | 0 | } |
1603 | 0 |
|
1604 | 0 | // ensure new scheme is lowercase |
1605 | 0 | // |
1606 | 0 | // XXX the string code unfortunately doesn't provide a ToLowerCase |
1607 | 0 | // that operates on a substring. |
1608 | 0 | net_ToLowerCase((char *) mSpec.get(), mScheme.mLen); |
1609 | 0 | return NS_OK; |
1610 | 0 | } |
1611 | | |
1612 | | nsresult |
1613 | | nsStandardURL::SetUserPass(const nsACString &input) |
1614 | 0 | { |
1615 | 0 | const nsPromiseFlatCString &userpass = PromiseFlatCString(input); |
1616 | 0 |
|
1617 | 0 | LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get())); |
1618 | 0 |
|
1619 | 0 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
1620 | 0 | if (userpass.IsEmpty()) |
1621 | 0 | return NS_OK; |
1622 | 0 | NS_WARNING("cannot set user:pass on no-auth url"); |
1623 | 0 | return NS_ERROR_UNEXPECTED; |
1624 | 0 | } |
1625 | 0 | if (mAuthority.mLen < 0) { |
1626 | 0 | NS_WARNING("uninitialized"); |
1627 | 0 | return NS_ERROR_NOT_INITIALIZED; |
1628 | 0 | } |
1629 | 0 |
|
1630 | 0 | if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) { |
1631 | 0 | return NS_ERROR_MALFORMED_URI; |
1632 | 0 | } |
1633 | 0 | |
1634 | 0 | InvalidateCache(); |
1635 | 0 |
|
1636 | 0 | if (userpass.IsEmpty()) { |
1637 | 0 | // remove user:pass |
1638 | 0 | if (mUsername.mLen > 0) { |
1639 | 0 | if (mPassword.mLen > 0) |
1640 | 0 | mUsername.mLen += (mPassword.mLen + 1); |
1641 | 0 | mUsername.mLen++; |
1642 | 0 | mSpec.Cut(mUsername.mPos, mUsername.mLen); |
1643 | 0 | mAuthority.mLen -= mUsername.mLen; |
1644 | 0 | ShiftFromHost(-mUsername.mLen); |
1645 | 0 | mUsername.mLen = -1; |
1646 | 0 | mPassword.mLen = -1; |
1647 | 0 | } |
1648 | 0 |
|
1649 | 0 | return NS_OK; |
1650 | 0 | } |
1651 | 0 |
|
1652 | 0 | NS_ASSERTION(mHost.mLen >= 0, "uninitialized"); |
1653 | 0 |
|
1654 | 0 | nsresult rv; |
1655 | 0 | uint32_t usernamePos, passwordPos; |
1656 | 0 | int32_t usernameLen, passwordLen; |
1657 | 0 |
|
1658 | 0 | rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(), |
1659 | 0 | &usernamePos, &usernameLen, |
1660 | 0 | &passwordPos, &passwordLen); |
1661 | 0 | if (NS_FAILED(rv)) return rv; |
1662 | 0 | |
1663 | 0 | // build new user:pass in |buf| |
1664 | 0 | nsAutoCString buf; |
1665 | 0 | if (usernameLen > 0) { |
1666 | 0 | nsSegmentEncoder encoder; |
1667 | 0 | bool ignoredOut; |
1668 | 0 | usernameLen = encoder.EncodeSegmentCount(userpass.get(), |
1669 | 0 | URLSegment(usernamePos, |
1670 | 0 | usernameLen), |
1671 | 0 | esc_Username | esc_AlwaysCopy, |
1672 | 0 | buf, ignoredOut); |
1673 | 0 | if (passwordLen > 0) { |
1674 | 0 | buf.Append(':'); |
1675 | 0 | passwordLen = encoder.EncodeSegmentCount(userpass.get(), |
1676 | 0 | URLSegment(passwordPos, |
1677 | 0 | passwordLen), |
1678 | 0 | esc_Password | |
1679 | 0 | esc_AlwaysCopy, buf, |
1680 | 0 | ignoredOut); |
1681 | 0 | } else { |
1682 | 0 | passwordLen = -1; |
1683 | 0 | } |
1684 | 0 | if (mUsername.mLen < 0) |
1685 | 0 | buf.Append('@'); |
1686 | 0 | } |
1687 | 0 |
|
1688 | 0 | uint32_t shift = 0; |
1689 | 0 |
|
1690 | 0 | if (mUsername.mLen < 0) { |
1691 | 0 | // no existing user:pass |
1692 | 0 | if (!buf.IsEmpty()) { |
1693 | 0 | mSpec.Insert(buf, mHost.mPos); |
1694 | 0 | mUsername.mPos = mHost.mPos; |
1695 | 0 | shift = buf.Length(); |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | else { |
1699 | 0 | // replace existing user:pass |
1700 | 0 | uint32_t userpassLen = mUsername.mLen; |
1701 | 0 | if (mPassword.mLen >= 0) |
1702 | 0 | userpassLen += (mPassword.mLen + 1); |
1703 | 0 | mSpec.Replace(mUsername.mPos, userpassLen, buf); |
1704 | 0 | shift = buf.Length() - userpassLen; |
1705 | 0 | } |
1706 | 0 | if (shift) { |
1707 | 0 | ShiftFromHost(shift); |
1708 | 0 | mAuthority.mLen += shift; |
1709 | 0 | } |
1710 | 0 | // update positions and lengths |
1711 | 0 | mUsername.mLen = usernameLen; |
1712 | 0 | mPassword.mLen = passwordLen; |
1713 | 0 | if (passwordLen > 0) { |
1714 | 0 | mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; |
1715 | 0 | } |
1716 | 0 |
|
1717 | 0 | return NS_OK; |
1718 | 0 | } |
1719 | | |
1720 | | nsresult |
1721 | | nsStandardURL::SetUsername(const nsACString &input) |
1722 | 0 | { |
1723 | 0 | const nsPromiseFlatCString &username = PromiseFlatCString(input); |
1724 | 0 |
|
1725 | 0 | LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get())); |
1726 | 0 |
|
1727 | 0 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
1728 | 0 | if (username.IsEmpty()) |
1729 | 0 | return NS_OK; |
1730 | 0 | NS_WARNING("cannot set username on no-auth url"); |
1731 | 0 | return NS_ERROR_UNEXPECTED; |
1732 | 0 | } |
1733 | 0 | |
1734 | 0 | if (username.IsEmpty()) |
1735 | 0 | return SetUserPass(username); |
1736 | 0 | |
1737 | 0 | if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) { |
1738 | 0 | return NS_ERROR_MALFORMED_URI; |
1739 | 0 | } |
1740 | 0 | |
1741 | 0 | InvalidateCache(); |
1742 | 0 |
|
1743 | 0 | // escape username if necessary |
1744 | 0 | nsAutoCString buf; |
1745 | 0 | nsSegmentEncoder encoder; |
1746 | 0 | const nsACString &escUsername = |
1747 | 0 | encoder.EncodeSegment(username, esc_Username, buf); |
1748 | 0 |
|
1749 | 0 | int32_t shift; |
1750 | 0 |
|
1751 | 0 | if (mUsername.mLen < 0) { |
1752 | 0 | mUsername.mPos = mAuthority.mPos; |
1753 | 0 | mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos); |
1754 | 0 | shift = escUsername.Length() + 1; |
1755 | 0 | } |
1756 | 0 | else |
1757 | 0 | shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername); |
1758 | 0 |
|
1759 | 0 | if (shift) { |
1760 | 0 | mUsername.mLen = escUsername.Length(); |
1761 | 0 | mAuthority.mLen += shift; |
1762 | 0 | ShiftFromPassword(shift); |
1763 | 0 | } |
1764 | 0 |
|
1765 | 0 | return NS_OK; |
1766 | 0 | } |
1767 | | |
1768 | | nsresult |
1769 | | nsStandardURL::SetPassword(const nsACString &input) |
1770 | 0 | { |
1771 | 0 | const nsPromiseFlatCString &password = PromiseFlatCString(input); |
1772 | 0 |
|
1773 | 0 | auto clearedPassword = MakeScopeExit([&password, this]() { |
1774 | 0 | // Check that if this method is called with the empty string then the |
1775 | 0 | // password is definitely cleared when exiting this method. |
1776 | 0 | if (password.IsEmpty()) { |
1777 | 0 | MOZ_DIAGNOSTIC_ASSERT(this->Password().IsEmpty()); |
1778 | 0 | } |
1779 | 0 | Unused << this; // silence compiler -Wunused-lambda-capture |
1780 | 0 | }); |
1781 | 0 |
|
1782 | 0 | LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get())); |
1783 | 0 |
|
1784 | 0 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
1785 | 0 | if (password.IsEmpty()) |
1786 | 0 | return NS_OK; |
1787 | 0 | NS_WARNING("cannot set password on no-auth url"); |
1788 | 0 | return NS_ERROR_UNEXPECTED; |
1789 | 0 | } |
1790 | 0 | if (mUsername.mLen <= 0) { |
1791 | 0 | if (password.IsEmpty()) { |
1792 | 0 | MOZ_DIAGNOSTIC_ASSERT(Password().IsEmpty()); |
1793 | 0 | return NS_OK; |
1794 | 0 | } |
1795 | 0 | NS_WARNING("cannot set password without existing username"); |
1796 | 0 | return NS_ERROR_FAILURE; |
1797 | 0 | } |
1798 | 0 | |
1799 | 0 | if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) { |
1800 | 0 | return NS_ERROR_MALFORMED_URI; |
1801 | 0 | } |
1802 | 0 | |
1803 | 0 | InvalidateCache(); |
1804 | 0 |
|
1805 | 0 | if (password.IsEmpty()) { |
1806 | 0 | if (mPassword.mLen >= 0) { |
1807 | 0 | // cut(":password") |
1808 | 0 | mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1); |
1809 | 0 | ShiftFromHost(-(mPassword.mLen + 1)); |
1810 | 0 | mAuthority.mLen -= (mPassword.mLen + 1); |
1811 | 0 | mPassword.mLen = -1; |
1812 | 0 | } |
1813 | 0 | return NS_OK; |
1814 | 0 | } |
1815 | 0 |
|
1816 | 0 | // escape password if necessary |
1817 | 0 | nsAutoCString buf; |
1818 | 0 | nsSegmentEncoder encoder; |
1819 | 0 | const nsACString &escPassword = |
1820 | 0 | encoder.EncodeSegment(password, esc_Password, buf); |
1821 | 0 |
|
1822 | 0 | int32_t shift; |
1823 | 0 |
|
1824 | 0 | if (mPassword.mLen < 0) { |
1825 | 0 | mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; |
1826 | 0 | mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1); |
1827 | 0 | shift = escPassword.Length() + 1; |
1828 | 0 | } |
1829 | 0 | else |
1830 | 0 | shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword); |
1831 | 0 |
|
1832 | 0 | if (shift) { |
1833 | 0 | mPassword.mLen = escPassword.Length(); |
1834 | 0 | mAuthority.mLen += shift; |
1835 | 0 | ShiftFromHost(shift); |
1836 | 0 | } |
1837 | 0 | return NS_OK; |
1838 | 0 | } |
1839 | | |
1840 | | void |
1841 | | nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart, |
1842 | | nsACString::const_iterator& aEnd) |
1843 | 0 | { |
1844 | 0 | for (int32_t i = 0; gHostLimitDigits[i]; ++i) { |
1845 | 0 | nsACString::const_iterator c(aStart); |
1846 | 0 | if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) { |
1847 | 0 | aEnd = c; |
1848 | 0 | } |
1849 | 0 | } |
1850 | 0 | } |
1851 | | |
1852 | | // If aValue only has a host part and no port number, the port |
1853 | | // will not be reset!!! |
1854 | | nsresult |
1855 | | nsStandardURL::SetHostPort(const nsACString &aValue) |
1856 | 0 | { |
1857 | 0 | // We cannot simply call nsIURI::SetHost because that would treat the name as |
1858 | 0 | // an IPv6 address (like http:://[server:443]/). We also cannot call |
1859 | 0 | // nsIURI::SetHostPort because that isn't implemented. Sadfaces. |
1860 | 0 |
|
1861 | 0 | nsACString::const_iterator start, end; |
1862 | 0 | aValue.BeginReading(start); |
1863 | 0 | aValue.EndReading(end); |
1864 | 0 | nsACString::const_iterator iter(start); |
1865 | 0 | bool isIPv6 = false; |
1866 | 0 |
|
1867 | 0 | FindHostLimit(start, end); |
1868 | 0 |
|
1869 | 0 | if (*start == '[') { // IPv6 address |
1870 | 0 | if (!FindCharInReadable(']', iter, end)) { |
1871 | 0 | // the ] character is missing |
1872 | 0 | return NS_ERROR_MALFORMED_URI; |
1873 | 0 | } |
1874 | 0 | // iter now at the ']' character |
1875 | 0 | isIPv6 = true; |
1876 | 0 | } else { |
1877 | 0 | nsACString::const_iterator iter2(start); |
1878 | 0 | if (FindCharInReadable(']', iter2, end)) { |
1879 | 0 | // if the first char isn't [ then there should be no ] character |
1880 | 0 | return NS_ERROR_MALFORMED_URI; |
1881 | 0 | } |
1882 | 0 | } |
1883 | 0 | |
1884 | 0 | FindCharInReadable(':', iter, end); |
1885 | 0 |
|
1886 | 0 | if (!isIPv6 && iter != end) { |
1887 | 0 | nsACString::const_iterator iter2(iter); |
1888 | 0 | iter2++; // Skip over the first ':' character |
1889 | 0 | if (FindCharInReadable(':', iter2, end)) { |
1890 | 0 | // If there is more than one ':' character it suggests an IPv6 |
1891 | 0 | // The format should be [2001::1]:80 where the port is optional |
1892 | 0 | return NS_ERROR_MALFORMED_URI; |
1893 | 0 | } |
1894 | 0 | } |
1895 | 0 | |
1896 | 0 | nsresult rv = SetHost(Substring(start, iter)); |
1897 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1898 | 0 |
|
1899 | 0 | if (iter == end) { |
1900 | 0 | // does not end in colon |
1901 | 0 | return NS_OK; |
1902 | 0 | } |
1903 | 0 | |
1904 | 0 | iter++; // advance over the colon |
1905 | 0 | if (iter == end) { |
1906 | 0 | // port number is missing |
1907 | 0 | return NS_OK; |
1908 | 0 | } |
1909 | 0 | |
1910 | 0 | nsCString portStr(Substring(iter, end)); |
1911 | 0 | int32_t port = portStr.ToInteger(&rv); |
1912 | 0 | if (NS_FAILED(rv)) { |
1913 | 0 | // Failure parsing the port number |
1914 | 0 | return NS_OK; |
1915 | 0 | } |
1916 | 0 | |
1917 | 0 | Unused << SetPort(port); |
1918 | 0 | return NS_OK; |
1919 | 0 | } |
1920 | | |
1921 | | nsresult |
1922 | | nsStandardURL::SetHost(const nsACString &input) |
1923 | 0 | { |
1924 | 0 | const nsPromiseFlatCString &hostname = PromiseFlatCString(input); |
1925 | 0 |
|
1926 | 0 | nsACString::const_iterator start, end; |
1927 | 0 | hostname.BeginReading(start); |
1928 | 0 | hostname.EndReading(end); |
1929 | 0 |
|
1930 | 0 | FindHostLimit(start, end); |
1931 | 0 |
|
1932 | 0 | const nsCString unescapedHost(Substring(start, end)); |
1933 | 0 | // Do percent decoding on the the input. |
1934 | 0 | nsAutoCString flat; |
1935 | 0 | NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(), |
1936 | 0 | esc_AlwaysCopy | esc_Host, flat); |
1937 | 0 | const char *host = flat.get(); |
1938 | 0 |
|
1939 | 0 | LOG(("nsStandardURL::SetHost [host=%s]\n", host)); |
1940 | 0 |
|
1941 | 0 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
1942 | 0 | if (flat.IsEmpty()) |
1943 | 0 | return NS_OK; |
1944 | 0 | NS_WARNING("cannot set host on no-auth url"); |
1945 | 0 | return NS_ERROR_UNEXPECTED; |
1946 | 0 | } |
1947 | 0 | if (flat.IsEmpty()) { |
1948 | 0 | // Setting an empty hostname is not allowed for |
1949 | 0 | // URLTYPE_STANDARD and URLTYPE_AUTHORITY. |
1950 | 0 | return NS_ERROR_UNEXPECTED; |
1951 | 0 | } |
1952 | 0 | |
1953 | 0 | if (strlen(host) < flat.Length()) |
1954 | 0 | return NS_ERROR_MALFORMED_URI; // found embedded null |
1955 | 0 | |
1956 | 0 | // For consistency with SetSpec/nsURLParsers, don't allow spaces |
1957 | 0 | // in the hostname. |
1958 | 0 | if (strchr(host, ' ')) |
1959 | 0 | return NS_ERROR_MALFORMED_URI; |
1960 | 0 | |
1961 | 0 | if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) { |
1962 | 0 | return NS_ERROR_MALFORMED_URI; |
1963 | 0 | } |
1964 | 0 | |
1965 | 0 | InvalidateCache(); |
1966 | 0 |
|
1967 | 0 | uint32_t len; |
1968 | 0 | nsAutoCString hostBuf; |
1969 | 0 | nsresult rv = NormalizeIDN(flat, hostBuf); |
1970 | 0 | if (NS_FAILED(rv)) { |
1971 | 0 | return rv; |
1972 | 0 | } |
1973 | 0 | |
1974 | 0 | if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) { |
1975 | 0 | nsAutoCString ipString; |
1976 | 0 | if (hostBuf.Length() > 0 && |
1977 | 0 | hostBuf.First() == '[' && hostBuf.Last() == ']' && |
1978 | 0 | ValidIPv6orHostname(hostBuf.get(), hostBuf.Length())) { |
1979 | 0 | rv = (nsresult) rusturl_parse_ipv6addr(&hostBuf, &ipString); |
1980 | 0 | if (NS_FAILED(rv)) { |
1981 | 0 | return rv; |
1982 | 0 | } |
1983 | 0 | hostBuf = ipString; |
1984 | 0 | } else if (NS_SUCCEEDED(NormalizeIPv4(hostBuf, ipString))) { |
1985 | 0 | hostBuf = ipString; |
1986 | 0 | } |
1987 | 0 | } |
1988 | 0 |
|
1989 | 0 | // NormalizeIDN always copies if the call was successful |
1990 | 0 | host = hostBuf.get(); |
1991 | 0 | len = hostBuf.Length(); |
1992 | 0 |
|
1993 | 0 | if (!ValidIPv6orHostname(host, len)) { |
1994 | 0 | return NS_ERROR_MALFORMED_URI; |
1995 | 0 | } |
1996 | 0 | |
1997 | 0 | if (mHost.mLen < 0) { |
1998 | 0 | int port_length = 0; |
1999 | 0 | if (mPort != -1) { |
2000 | 0 | nsAutoCString buf; |
2001 | 0 | buf.Assign(':'); |
2002 | 0 | buf.AppendInt(mPort); |
2003 | 0 | port_length = buf.Length(); |
2004 | 0 | } |
2005 | 0 | if (mAuthority.mLen > 0) { |
2006 | 0 | mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length; |
2007 | 0 | mHost.mLen = 0; |
2008 | 0 | } else if (mScheme.mLen > 0) { |
2009 | 0 | mHost.mPos = mScheme.mPos + mScheme.mLen + 3; |
2010 | 0 | mHost.mLen = 0; |
2011 | 0 | } |
2012 | 0 | } |
2013 | 0 |
|
2014 | 0 | int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len); |
2015 | 0 |
|
2016 | 0 | if (shift) { |
2017 | 0 | mHost.mLen = len; |
2018 | 0 | mAuthority.mLen += shift; |
2019 | 0 | ShiftFromPath(shift); |
2020 | 0 | } |
2021 | 0 |
|
2022 | 0 | // Now canonicalize the host to lowercase |
2023 | 0 | net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen); |
2024 | 0 | return NS_OK; |
2025 | 0 | } |
2026 | | |
2027 | | nsresult |
2028 | | nsStandardURL::SetPort(int32_t port) |
2029 | 0 | { |
2030 | 0 | LOG(("nsStandardURL::SetPort [port=%d]\n", port)); |
2031 | 0 |
|
2032 | 0 | if ((port == mPort) || (mPort == -1 && port == mDefaultPort)) |
2033 | 0 | return NS_OK; |
2034 | 0 | |
2035 | 0 | // ports must be >= 0 and 16 bit |
2036 | 0 | // -1 == use default |
2037 | 0 | if (port < -1 || port > std::numeric_limits<uint16_t>::max()) |
2038 | 0 | return NS_ERROR_MALFORMED_URI; |
2039 | 0 | |
2040 | 0 | if (mURLType == URLTYPE_NO_AUTHORITY) { |
2041 | 0 | NS_WARNING("cannot set port on no-auth url"); |
2042 | 0 | return NS_ERROR_UNEXPECTED; |
2043 | 0 | } |
2044 | 0 |
|
2045 | 0 | InvalidateCache(); |
2046 | 0 | if (port == mDefaultPort) { |
2047 | 0 | port = -1; |
2048 | 0 | } |
2049 | 0 |
|
2050 | 0 | ReplacePortInSpec(port); |
2051 | 0 |
|
2052 | 0 | mPort = port; |
2053 | 0 | return NS_OK; |
2054 | 0 | } |
2055 | | |
2056 | | /** |
2057 | | * Replaces the existing port in mSpec with aNewPort. |
2058 | | * |
2059 | | * The caller is responsible for: |
2060 | | * - Calling InvalidateCache (since our mSpec is changing). |
2061 | | * - Checking whether aNewPort is mDefaultPort (in which case the |
2062 | | * caller should pass aNewPort=-1). |
2063 | | */ |
2064 | | void |
2065 | | nsStandardURL::ReplacePortInSpec(int32_t aNewPort) |
2066 | 0 | { |
2067 | 0 | NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1, |
2068 | 0 | "Caller should check its passed-in value and pass -1 instead of " |
2069 | 0 | "mDefaultPort, to avoid encoding default port into mSpec"); |
2070 | 0 |
|
2071 | 0 | // Create the (possibly empty) string that we're planning to replace: |
2072 | 0 | nsAutoCString buf; |
2073 | 0 | if (mPort != -1) { |
2074 | 0 | buf.Assign(':'); |
2075 | 0 | buf.AppendInt(mPort); |
2076 | 0 | } |
2077 | 0 | // Find the position & length of that string: |
2078 | 0 | const uint32_t replacedLen = buf.Length(); |
2079 | 0 | const uint32_t replacedStart = |
2080 | 0 | mAuthority.mPos + mAuthority.mLen - replacedLen; |
2081 | 0 |
|
2082 | 0 | // Create the (possibly empty) replacement string: |
2083 | 0 | if (aNewPort == -1) { |
2084 | 0 | buf.Truncate(); |
2085 | 0 | } else { |
2086 | 0 | buf.Assign(':'); |
2087 | 0 | buf.AppendInt(aNewPort); |
2088 | 0 | } |
2089 | 0 | // Perform the replacement: |
2090 | 0 | mSpec.Replace(replacedStart, replacedLen, buf); |
2091 | 0 |
|
2092 | 0 | // Bookkeeping to reflect the new length: |
2093 | 0 | int32_t shift = buf.Length() - replacedLen; |
2094 | 0 | mAuthority.mLen += shift; |
2095 | 0 | ShiftFromPath(shift); |
2096 | 0 | } |
2097 | | |
2098 | | nsresult |
2099 | | nsStandardURL::SetPathQueryRef(const nsACString &input) |
2100 | 1.59k | { |
2101 | 1.59k | const nsPromiseFlatCString &path = PromiseFlatCString(input); |
2102 | 1.59k | LOG(("nsStandardURL::SetPathQueryRef [path=%s]\n", path.get())); |
2103 | 1.59k | |
2104 | 1.59k | InvalidateCache(); |
2105 | 1.59k | |
2106 | 1.59k | if (!path.IsEmpty()) { |
2107 | 1.59k | nsAutoCString spec; |
2108 | 1.59k | |
2109 | 1.59k | spec.Assign(mSpec.get(), mPath.mPos); |
2110 | 1.59k | if (path.First() != '/') |
2111 | 0 | spec.Append('/'); |
2112 | 1.59k | spec.Append(path); |
2113 | 1.59k | |
2114 | 1.59k | return SetSpecInternal(spec); |
2115 | 1.59k | } |
2116 | 0 | if (mPath.mLen >= 1) { |
2117 | 0 | mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1); |
2118 | 0 | // these contain only a '/' |
2119 | 0 | mPath.mLen = 1; |
2120 | 0 | mDirectory.mLen = 1; |
2121 | 0 | mFilepath.mLen = 1; |
2122 | 0 | // these are no longer defined |
2123 | 0 | mBasename.mLen = -1; |
2124 | 0 | mExtension.mLen = -1; |
2125 | 0 | mQuery.mLen = -1; |
2126 | 0 | mRef.mLen = -1; |
2127 | 0 | } |
2128 | 0 | return NS_OK; |
2129 | 0 | } |
2130 | | |
2131 | | // When updating this also update SubstitutingURL::Mutator |
2132 | | // Queries this list of interfaces. If none match, it queries mURI. |
2133 | | NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsStandardURL::Mutator, |
2134 | | nsIURISetters, |
2135 | | nsIURIMutator, |
2136 | | nsIStandardURLMutator, |
2137 | | nsIURLMutator, |
2138 | | nsIFileURLMutator, |
2139 | | nsISerializable) |
2140 | | |
2141 | | NS_IMETHODIMP |
2142 | | nsStandardURL::Mutate(nsIURIMutator** aMutator) |
2143 | 3.91k | { |
2144 | 3.91k | RefPtr<nsStandardURL::Mutator> mutator = new nsStandardURL::Mutator(); |
2145 | 3.91k | nsresult rv = mutator->InitFromURI(this); |
2146 | 3.91k | if (NS_FAILED(rv)) { |
2147 | 0 | return rv; |
2148 | 0 | } |
2149 | 3.91k | mutator.forget(aMutator); |
2150 | 3.91k | return NS_OK; |
2151 | 3.91k | } |
2152 | | |
2153 | | NS_IMETHODIMP |
2154 | | nsStandardURL::Equals(nsIURI *unknownOther, bool *result) |
2155 | 0 | { |
2156 | 0 | return EqualsInternal(unknownOther, eHonorRef, result); |
2157 | 0 | } |
2158 | | |
2159 | | NS_IMETHODIMP |
2160 | | nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result) |
2161 | 0 | { |
2162 | 0 | return EqualsInternal(unknownOther, eIgnoreRef, result); |
2163 | 0 | } |
2164 | | |
2165 | | nsresult |
2166 | | nsStandardURL::EqualsInternal(nsIURI *unknownOther, |
2167 | | nsStandardURL::RefHandlingEnum refHandlingMode, |
2168 | | bool *result) |
2169 | 0 | { |
2170 | 0 | NS_ENSURE_ARG_POINTER(unknownOther); |
2171 | 0 | MOZ_ASSERT(result, "null pointer"); |
2172 | 0 |
|
2173 | 0 | RefPtr<nsStandardURL> other; |
2174 | 0 | nsresult rv = unknownOther->QueryInterface(kThisImplCID, |
2175 | 0 | getter_AddRefs(other)); |
2176 | 0 | if (NS_FAILED(rv)) { |
2177 | 0 | *result = false; |
2178 | 0 | return NS_OK; |
2179 | 0 | } |
2180 | 0 | |
2181 | 0 | // First, check whether one URIs is an nsIFileURL while the other |
2182 | 0 | // is not. If that's the case, they're different. |
2183 | 0 | if (mSupportsFileURL != other->mSupportsFileURL) { |
2184 | 0 | *result = false; |
2185 | 0 | return NS_OK; |
2186 | 0 | } |
2187 | 0 | |
2188 | 0 | // Next check parts of a URI that, if different, automatically make the |
2189 | 0 | // URIs different |
2190 | 0 | if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) || |
2191 | 0 | // Check for host manually, since conversion to file will |
2192 | 0 | // ignore the host! |
2193 | 0 | !SegmentIs(mHost, other->mSpec.get(), other->mHost) || |
2194 | 0 | !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) || |
2195 | 0 | !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) || |
2196 | 0 | !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) || |
2197 | 0 | Port() != other->Port()) { |
2198 | 0 | // No need to compare files or other URI parts -- these are different |
2199 | 0 | // beasties |
2200 | 0 | *result = false; |
2201 | 0 | return NS_OK; |
2202 | 0 | } |
2203 | 0 | |
2204 | 0 | if (refHandlingMode == eHonorRef && |
2205 | 0 | !SegmentIs(mRef, other->mSpec.get(), other->mRef)) { |
2206 | 0 | *result = false; |
2207 | 0 | return NS_OK; |
2208 | 0 | } |
2209 | 0 | |
2210 | 0 | // Then check for exact identity of URIs. If we have it, they're equal |
2211 | 0 | if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) && |
2212 | 0 | SegmentIs(mBasename, other->mSpec.get(), other->mBasename) && |
2213 | 0 | SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) { |
2214 | 0 | *result = true; |
2215 | 0 | return NS_OK; |
2216 | 0 | } |
2217 | 0 | |
2218 | 0 | // At this point, the URIs are not identical, but they only differ in the |
2219 | 0 | // directory/filename/extension. If these are file URLs, then get the |
2220 | 0 | // corresponding file objects and compare those, since two filenames that |
2221 | 0 | // differ, eg, only in case could still be equal. |
2222 | 0 | if (mSupportsFileURL) { |
2223 | 0 | // Assume not equal for failure cases... but failures in GetFile are |
2224 | 0 | // really failures, more or less, so propagate them to caller. |
2225 | 0 | *result = false; |
2226 | 0 |
|
2227 | 0 | rv = EnsureFile(); |
2228 | 0 | nsresult rv2 = other->EnsureFile(); |
2229 | 0 | // special case for resource:// urls that don't resolve to files |
2230 | 0 | if (rv == NS_ERROR_NO_INTERFACE && rv == rv2) |
2231 | 0 | return NS_OK; |
2232 | 0 | |
2233 | 0 | if (NS_FAILED(rv)) { |
2234 | 0 | LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file", |
2235 | 0 | this, mSpec.get())); |
2236 | 0 | return rv; |
2237 | 0 | } |
2238 | 0 | NS_ASSERTION(mFile, "EnsureFile() lied!"); |
2239 | 0 | rv = rv2; |
2240 | 0 | if (NS_FAILED(rv)) { |
2241 | 0 | LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file", |
2242 | 0 | other.get(), other->mSpec.get())); |
2243 | 0 | return rv; |
2244 | 0 | } |
2245 | 0 | NS_ASSERTION(other->mFile, "EnsureFile() lied!"); |
2246 | 0 | return mFile->Equals(other->mFile, result); |
2247 | 0 | } |
2248 | 0 | |
2249 | 0 | // The URLs are not identical, and they do not correspond to the |
2250 | 0 | // same file, so they are different. |
2251 | 0 | *result = false; |
2252 | 0 |
|
2253 | 0 | return NS_OK; |
2254 | 0 | } |
2255 | | |
2256 | | NS_IMETHODIMP |
2257 | | nsStandardURL::SchemeIs(const char *scheme, bool *result) |
2258 | 23.1k | { |
2259 | 23.1k | MOZ_ASSERT(result, "null pointer"); |
2260 | 23.1k | |
2261 | 23.1k | *result = SegmentIs(mScheme, scheme); |
2262 | 23.1k | return NS_OK; |
2263 | 23.1k | } |
2264 | | |
2265 | | /* virtual */ nsStandardURL* |
2266 | | nsStandardURL::StartClone() |
2267 | 3.91k | { |
2268 | 3.91k | nsStandardURL *clone = new nsStandardURL(); |
2269 | 3.91k | return clone; |
2270 | 3.91k | } |
2271 | | |
2272 | | nsresult |
2273 | | nsStandardURL::Clone(nsIURI **result) |
2274 | 3.91k | { |
2275 | 3.91k | return CloneInternal(eHonorRef, EmptyCString(), result); |
2276 | 3.91k | } |
2277 | | |
2278 | | nsresult |
2279 | | nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode, |
2280 | | const nsACString& newRef, |
2281 | | nsIURI **result) |
2282 | | |
2283 | 3.91k | { |
2284 | 3.91k | RefPtr<nsStandardURL> clone = StartClone(); |
2285 | 3.91k | if (!clone) |
2286 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2287 | 3.91k | |
2288 | 3.91k | // Copy local members into clone. |
2289 | 3.91k | // Also copies the cached members mFile, mDisplayHost |
2290 | 3.91k | clone->CopyMembers(this, refHandlingMode, newRef, true); |
2291 | 3.91k | |
2292 | 3.91k | clone.forget(result); |
2293 | 3.91k | return NS_OK; |
2294 | 3.91k | } |
2295 | | |
2296 | | nsresult nsStandardURL::CopyMembers(nsStandardURL * source, |
2297 | | nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef, |
2298 | | bool copyCached) |
2299 | 1.15M | { |
2300 | 1.15M | mSpec = source->mSpec; |
2301 | 1.15M | mDefaultPort = source->mDefaultPort; |
2302 | 1.15M | mPort = source->mPort; |
2303 | 1.15M | mScheme = source->mScheme; |
2304 | 1.15M | mAuthority = source->mAuthority; |
2305 | 1.15M | mUsername = source->mUsername; |
2306 | 1.15M | mPassword = source->mPassword; |
2307 | 1.15M | mHost = source->mHost; |
2308 | 1.15M | mPath = source->mPath; |
2309 | 1.15M | mFilepath = source->mFilepath; |
2310 | 1.15M | mDirectory = source->mDirectory; |
2311 | 1.15M | mBasename = source->mBasename; |
2312 | 1.15M | mExtension = source->mExtension; |
2313 | 1.15M | mQuery = source->mQuery; |
2314 | 1.15M | mRef = source->mRef; |
2315 | 1.15M | mURLType = source->mURLType; |
2316 | 1.15M | mParser = source->mParser; |
2317 | 1.15M | mSupportsFileURL = source->mSupportsFileURL; |
2318 | 1.15M | mCheckedIfHostA = source->mCheckedIfHostA; |
2319 | 1.15M | mDisplayHost = source->mDisplayHost; |
2320 | 1.15M | |
2321 | 1.15M | if (copyCached) { |
2322 | 3.91k | mFile = source->mFile; |
2323 | 1.15M | } else { |
2324 | 1.15M | InvalidateCache(true); |
2325 | 1.15M | } |
2326 | 1.15M | |
2327 | 1.15M | if (refHandlingMode == eIgnoreRef) { |
2328 | 0 | SetRef(EmptyCString()); |
2329 | 1.15M | } else if (refHandlingMode == eReplaceRef) { |
2330 | 0 | SetRef(newRef); |
2331 | 0 | } |
2332 | 1.15M | |
2333 | 1.15M | |
2334 | 1.15M | return NS_OK; |
2335 | 1.15M | } |
2336 | | |
2337 | | NS_IMETHODIMP |
2338 | | nsStandardURL::Resolve(const nsACString &in, nsACString &out) |
2339 | 2.73M | { |
2340 | 2.73M | const nsPromiseFlatCString &flat = PromiseFlatCString(in); |
2341 | 2.73M | // filter out unexpected chars "\r\n\t" if necessary |
2342 | 2.73M | nsAutoCString buf; |
2343 | 2.73M | net_FilterURIString(flat, buf); |
2344 | 2.73M | |
2345 | 2.73M | const char *relpath = buf.get(); |
2346 | 2.73M | int32_t relpathLen = buf.Length(); |
2347 | 2.73M | |
2348 | 2.73M | char *result = nullptr; |
2349 | 2.73M | |
2350 | 2.73M | LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n", |
2351 | 2.73M | this, mSpec.get(), relpath)); |
2352 | 2.73M | |
2353 | 2.73M | NS_ASSERTION(mParser, "no parser: unitialized"); |
2354 | 2.73M | |
2355 | 2.73M | // NOTE: there is no need for this function to produce normalized |
2356 | 2.73M | // output. normalization will occur when the result is used to |
2357 | 2.73M | // initialize a nsStandardURL object. |
2358 | 2.73M | |
2359 | 2.73M | if (mScheme.mLen < 0) { |
2360 | 0 | NS_WARNING("unable to Resolve URL: this URL not initialized"); |
2361 | 0 | return NS_ERROR_NOT_INITIALIZED; |
2362 | 0 | } |
2363 | 2.73M | |
2364 | 2.73M | nsresult rv; |
2365 | 2.73M | URLSegment scheme; |
2366 | 2.73M | char *resultPath = nullptr; |
2367 | 2.73M | bool relative = false; |
2368 | 2.73M | uint32_t offset = 0; |
2369 | 2.73M | netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; |
2370 | 2.73M | |
2371 | 2.73M | // relative urls should never contain a host, so we always want to use |
2372 | 2.73M | // the noauth url parser. |
2373 | 2.73M | // use it to extract a possible scheme |
2374 | 2.73M | rv = mParser->ParseURL(relpath, |
2375 | 2.73M | relpathLen, |
2376 | 2.73M | &scheme.mPos, &scheme.mLen, |
2377 | 2.73M | nullptr, nullptr, |
2378 | 2.73M | nullptr, nullptr); |
2379 | 2.73M | |
2380 | 2.73M | // if the parser fails (for example because there is no valid scheme) |
2381 | 2.73M | // reset the scheme and assume a relative url |
2382 | 2.73M | if (NS_FAILED(rv)) scheme.Reset(); |
2383 | 2.73M | |
2384 | 2.73M | nsAutoCString protocol(Segment(scheme)); |
2385 | 2.73M | nsAutoCString baseProtocol(Scheme()); |
2386 | 2.73M | |
2387 | 2.73M | // We need to do backslash replacement for the following cases: |
2388 | 2.73M | // 1. The input is an absolute path with a http/https/ftp scheme |
2389 | 2.73M | // 2. The input is a relative path, and the base URL has a http/https/ftp scheme |
2390 | 2.73M | if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) || |
2391 | 2.73M | IsSpecialProtocol(protocol)) { |
2392 | 1.09M | |
2393 | 1.09M | auto start = buf.BeginWriting(); |
2394 | 1.09M | auto end = buf.EndWriting(); |
2395 | 10.3M | while (start != end) { |
2396 | 9.41M | if (*start == '?' || *start == '#') { |
2397 | 124k | break; |
2398 | 124k | } |
2399 | 9.28M | if (*start == '\\') { |
2400 | 4.41k | *start = '/'; |
2401 | 4.41k | } |
2402 | 9.28M | start++; |
2403 | 9.28M | } |
2404 | 1.09M | } |
2405 | 2.73M | |
2406 | 2.73M | if (scheme.mLen >= 0) { |
2407 | 1.67M | // add some flags to coalesceFlag if it is an ftp-url |
2408 | 1.67M | // need this later on when coalescing the resulting URL |
2409 | 1.67M | if (SegmentIs(relpath, scheme, "ftp", true)) { |
2410 | 1.86k | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
2411 | 1.86k | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
2412 | 1.86k | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
2413 | 1.86k | |
2414 | 1.86k | } |
2415 | 1.67M | // this URL appears to be absolute |
2416 | 1.67M | // but try to find out more |
2417 | 1.67M | if (SegmentIs(mScheme, relpath, scheme, true)) { |
2418 | 309 | // mScheme and Scheme are the same |
2419 | 309 | // but this can still be relative |
2420 | 309 | if (strncmp(relpath + scheme.mPos + scheme.mLen, "://", 3) == 0) { |
2421 | 229 | // now this is really absolute |
2422 | 229 | // because a :// follows the scheme |
2423 | 229 | result = NS_xstrdup(relpath); |
2424 | 229 | } else { |
2425 | 80 | // This is a deprecated form of relative urls like |
2426 | 80 | // http:file or http:/path/file |
2427 | 80 | // we will support it for now ... |
2428 | 80 | relative = true; |
2429 | 80 | offset = scheme.mLen + 1; |
2430 | 80 | } |
2431 | 1.67M | } else { |
2432 | 1.67M | // the schemes are not the same, we are also done |
2433 | 1.67M | // because we have to assume this is absolute |
2434 | 1.67M | result = NS_xstrdup(relpath); |
2435 | 1.67M | } |
2436 | 1.67M | } else { |
2437 | 1.06M | // add some flags to coalesceFlag if it is an ftp-url |
2438 | 1.06M | // need this later on when coalescing the resulting URL |
2439 | 1.06M | if (SegmentIs(mScheme,"ftp")) { |
2440 | 0 | coalesceFlag = (netCoalesceFlags) (coalesceFlag |
2441 | 0 | | NET_COALESCE_ALLOW_RELATIVE_ROOT |
2442 | 0 | | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); |
2443 | 0 | } |
2444 | 1.06M | if (relpath[0] == '/' && relpath[1] == '/') { |
2445 | 22.0k | // this URL //host/path is almost absolute |
2446 | 22.0k | result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath); |
2447 | 1.03M | } else { |
2448 | 1.03M | // then it must be relative |
2449 | 1.03M | relative = true; |
2450 | 1.03M | } |
2451 | 1.06M | } |
2452 | 2.73M | if (relative) { |
2453 | 1.03M | uint32_t len = 0; |
2454 | 1.03M | const char *realrelpath = relpath + offset; |
2455 | 1.03M | switch (*realrelpath) { |
2456 | 1.03M | case '/': |
2457 | 257k | // overwrite everything after the authority |
2458 | 257k | len = mAuthority.mPos + mAuthority.mLen; |
2459 | 257k | break; |
2460 | 1.03M | case '?': |
2461 | 932 | // overwrite the existing ?query and #ref |
2462 | 932 | if (mQuery.mLen >= 0) |
2463 | 0 | len = mQuery.mPos - 1; |
2464 | 932 | else if (mRef.mLen >= 0) |
2465 | 0 | len = mRef.mPos - 1; |
2466 | 932 | else |
2467 | 932 | len = mPath.mPos + mPath.mLen; |
2468 | 932 | break; |
2469 | 1.03M | case '#': |
2470 | 4.31k | case '\0': |
2471 | 4.31k | // overwrite the existing #ref |
2472 | 4.31k | if (mRef.mLen < 0) |
2473 | 4.31k | len = mPath.mPos + mPath.mLen; |
2474 | 0 | else |
2475 | 0 | len = mRef.mPos - 1; |
2476 | 4.31k | break; |
2477 | 776k | default: |
2478 | 776k | if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) { |
2479 | 0 | if (Filename().Equals(NS_LITERAL_CSTRING("%2F"), |
2480 | 0 | nsCaseInsensitiveCStringComparator())) { |
2481 | 0 | // if ftp URL ends with %2F then simply |
2482 | 0 | // append relative part because %2F also |
2483 | 0 | // marks the root directory with ftp-urls |
2484 | 0 | len = mFilepath.mPos + mFilepath.mLen; |
2485 | 0 | } else { |
2486 | 0 | // overwrite everything after the directory |
2487 | 0 | len = mDirectory.mPos + mDirectory.mLen; |
2488 | 0 | } |
2489 | 776k | } else { |
2490 | 776k | // overwrite everything after the directory |
2491 | 776k | len = mDirectory.mPos + mDirectory.mLen; |
2492 | 776k | } |
2493 | 1.03M | } |
2494 | 1.03M | result = AppendToSubstring(0, len, realrelpath); |
2495 | 1.03M | // locate result path |
2496 | 1.03M | resultPath = result + mPath.mPos; |
2497 | 1.03M | } |
2498 | 2.73M | if (!result) |
2499 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2500 | 2.73M | |
2501 | 2.73M | if (resultPath) |
2502 | 1.03M | net_CoalesceDirs(coalesceFlag, resultPath); |
2503 | 1.69M | else { |
2504 | 1.69M | // locate result path |
2505 | 1.69M | resultPath = PL_strstr(result, "://"); |
2506 | 1.69M | if (resultPath) { |
2507 | 22.7k | resultPath = PL_strchr(resultPath + 3, '/'); |
2508 | 22.7k | if (resultPath) |
2509 | 4.98k | net_CoalesceDirs(coalesceFlag,resultPath); |
2510 | 22.7k | } |
2511 | 1.69M | } |
2512 | 2.73M | out.Adopt(result); |
2513 | 2.73M | return NS_OK; |
2514 | 2.73M | } |
2515 | | |
2516 | | // result may contain unescaped UTF-8 characters |
2517 | | NS_IMETHODIMP |
2518 | | nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult) |
2519 | 0 | { |
2520 | 0 | NS_ENSURE_ARG_POINTER(uri2); |
2521 | 0 |
|
2522 | 0 | // if uri's are equal, then return uri as is |
2523 | 0 | bool isEquals = false; |
2524 | 0 | if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) |
2525 | 0 | return GetSpec(aResult); |
2526 | 0 | |
2527 | 0 | aResult.Truncate(); |
2528 | 0 |
|
2529 | 0 | // check pre-path; if they don't match, then return empty string |
2530 | 0 | nsStandardURL *stdurl2; |
2531 | 0 | nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); |
2532 | 0 | isEquals = NS_SUCCEEDED(rv) |
2533 | 0 | && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) |
2534 | 0 | && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) |
2535 | 0 | && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) |
2536 | 0 | && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) |
2537 | 0 | && (Port() == stdurl2->Port()); |
2538 | 0 | if (!isEquals) |
2539 | 0 | { |
2540 | 0 | if (NS_SUCCEEDED(rv)) |
2541 | 0 | NS_RELEASE(stdurl2); |
2542 | 0 | return NS_OK; |
2543 | 0 | } |
2544 | 0 |
|
2545 | 0 | // scan for first mismatched character |
2546 | 0 | const char *thisIndex, *thatIndex, *startCharPos; |
2547 | 0 | startCharPos = mSpec.get() + mDirectory.mPos; |
2548 | 0 | thisIndex = startCharPos; |
2549 | 0 | thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; |
2550 | 0 | while ((*thisIndex == *thatIndex) && *thisIndex) |
2551 | 0 | { |
2552 | 0 | thisIndex++; |
2553 | 0 | thatIndex++; |
2554 | 0 | } |
2555 | 0 |
|
2556 | 0 | // backup to just after previous slash so we grab an appropriate path |
2557 | 0 | // segment such as a directory (not partial segments) |
2558 | 0 | // todo: also check for file matches which include '?' and '#' |
2559 | 0 | while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/')) |
2560 | 0 | thisIndex--; |
2561 | 0 |
|
2562 | 0 | // grab spec from beginning to thisIndex |
2563 | 0 | aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get()); |
2564 | 0 |
|
2565 | 0 | NS_RELEASE(stdurl2); |
2566 | 0 | return rv; |
2567 | 0 | } |
2568 | | |
2569 | | NS_IMETHODIMP |
2570 | | nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult) |
2571 | 0 | { |
2572 | 0 | NS_ENSURE_ARG_POINTER(uri2); |
2573 | 0 |
|
2574 | 0 | aResult.Truncate(); |
2575 | 0 |
|
2576 | 0 | // if uri's are equal, then return empty string |
2577 | 0 | bool isEquals = false; |
2578 | 0 | if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) |
2579 | 0 | return NS_OK; |
2580 | 0 | |
2581 | 0 | nsStandardURL *stdurl2; |
2582 | 0 | nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); |
2583 | 0 | isEquals = NS_SUCCEEDED(rv) |
2584 | 0 | && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) |
2585 | 0 | && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) |
2586 | 0 | && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) |
2587 | 0 | && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) |
2588 | 0 | && (Port() == stdurl2->Port()); |
2589 | 0 | if (!isEquals) |
2590 | 0 | { |
2591 | 0 | if (NS_SUCCEEDED(rv)) |
2592 | 0 | NS_RELEASE(stdurl2); |
2593 | 0 |
|
2594 | 0 | return uri2->GetSpec(aResult); |
2595 | 0 | } |
2596 | 0 |
|
2597 | 0 | // scan for first mismatched character |
2598 | 0 | const char *thisIndex, *thatIndex, *startCharPos; |
2599 | 0 | startCharPos = mSpec.get() + mDirectory.mPos; |
2600 | 0 | thisIndex = startCharPos; |
2601 | 0 | thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; |
2602 | 0 |
|
2603 | | #ifdef XP_WIN |
2604 | | bool isFileScheme = SegmentIs(mScheme, "file"); |
2605 | | if (isFileScheme) |
2606 | | { |
2607 | | // on windows, we need to match the first segment of the path |
2608 | | // if these don't match then we need to return an absolute path |
2609 | | // skip over any leading '/' in path |
2610 | | while ((*thisIndex == *thatIndex) && (*thisIndex == '/')) |
2611 | | { |
2612 | | thisIndex++; |
2613 | | thatIndex++; |
2614 | | } |
2615 | | // look for end of first segment |
2616 | | while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/')) |
2617 | | { |
2618 | | thisIndex++; |
2619 | | thatIndex++; |
2620 | | } |
2621 | | |
2622 | | // if we didn't match through the first segment, return absolute path |
2623 | | if ((*thisIndex != '/') || (*thatIndex != '/')) |
2624 | | { |
2625 | | NS_RELEASE(stdurl2); |
2626 | | return uri2->GetSpec(aResult); |
2627 | | } |
2628 | | } |
2629 | | #endif |
2630 | |
|
2631 | 0 | while ((*thisIndex == *thatIndex) && *thisIndex) |
2632 | 0 | { |
2633 | 0 | thisIndex++; |
2634 | 0 | thatIndex++; |
2635 | 0 | } |
2636 | 0 |
|
2637 | 0 | // backup to just after previous slash so we grab an appropriate path |
2638 | 0 | // segment such as a directory (not partial segments) |
2639 | 0 | // todo: also check for file matches with '#' and '?' |
2640 | 0 | while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos)) |
2641 | 0 | thatIndex--; |
2642 | 0 |
|
2643 | 0 | const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen; |
2644 | 0 |
|
2645 | 0 | // need to account for slashes and add corresponding "../" |
2646 | 0 | for (; thisIndex <= limit && *thisIndex; ++thisIndex) |
2647 | 0 | { |
2648 | 0 | if (*thisIndex == '/') |
2649 | 0 | aResult.AppendLiteral("../"); |
2650 | 0 | } |
2651 | 0 |
|
2652 | 0 | // grab spec from thisIndex to end |
2653 | 0 | uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get(); |
2654 | 0 | aResult.Append(Substring(stdurl2->mSpec, startPos, |
2655 | 0 | stdurl2->mSpec.Length() - startPos)); |
2656 | 0 |
|
2657 | 0 | NS_RELEASE(stdurl2); |
2658 | 0 | return rv; |
2659 | 0 | } |
2660 | | |
2661 | | //---------------------------------------------------------------------------- |
2662 | | // nsStandardURL::nsIURL |
2663 | | //---------------------------------------------------------------------------- |
2664 | | |
2665 | | // result may contain unescaped UTF-8 characters |
2666 | | NS_IMETHODIMP |
2667 | | nsStandardURL::GetFilePath(nsACString &result) |
2668 | 32 | { |
2669 | 32 | result = Filepath(); |
2670 | 32 | return NS_OK; |
2671 | 32 | } |
2672 | | |
2673 | | // result may contain unescaped UTF-8 characters |
2674 | | NS_IMETHODIMP |
2675 | | nsStandardURL::GetQuery(nsACString &result) |
2676 | 0 | { |
2677 | 0 | result = Query(); |
2678 | 0 | return NS_OK; |
2679 | 0 | } |
2680 | | |
2681 | | // result may contain unescaped UTF-8 characters |
2682 | | NS_IMETHODIMP |
2683 | | nsStandardURL::GetRef(nsACString &result) |
2684 | 2.32k | { |
2685 | 2.32k | result = Ref(); |
2686 | 2.32k | return NS_OK; |
2687 | 2.32k | } |
2688 | | |
2689 | | NS_IMETHODIMP |
2690 | | nsStandardURL::GetHasRef(bool *result) |
2691 | 2.32k | { |
2692 | 2.32k | *result = (mRef.mLen >= 0); |
2693 | 2.32k | return NS_OK; |
2694 | 2.32k | } |
2695 | | |
2696 | | // result may contain unescaped UTF-8 characters |
2697 | | NS_IMETHODIMP |
2698 | | nsStandardURL::GetDirectory(nsACString &result) |
2699 | 0 | { |
2700 | 0 | result = Directory(); |
2701 | 0 | return NS_OK; |
2702 | 0 | } |
2703 | | |
2704 | | // result may contain unescaped UTF-8 characters |
2705 | | NS_IMETHODIMP |
2706 | | nsStandardURL::GetFileName(nsACString &result) |
2707 | 0 | { |
2708 | 0 | result = Filename(); |
2709 | 0 | return NS_OK; |
2710 | 0 | } |
2711 | | |
2712 | | // result may contain unescaped UTF-8 characters |
2713 | | NS_IMETHODIMP |
2714 | | nsStandardURL::GetFileBaseName(nsACString &result) |
2715 | 0 | { |
2716 | 0 | result = Basename(); |
2717 | 0 | return NS_OK; |
2718 | 0 | } |
2719 | | |
2720 | | // result may contain unescaped UTF-8 characters |
2721 | | NS_IMETHODIMP |
2722 | | nsStandardURL::GetFileExtension(nsACString &result) |
2723 | 0 | { |
2724 | 0 | result = Extension(); |
2725 | 0 | return NS_OK; |
2726 | 0 | } |
2727 | | |
2728 | | nsresult |
2729 | | nsStandardURL::SetFilePath(const nsACString &input) |
2730 | 0 | { |
2731 | 0 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
2732 | 0 | const char *filepath = flat.get(); |
2733 | 0 |
|
2734 | 0 | LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath)); |
2735 | 0 |
|
2736 | 0 | // if there isn't a filepath, then there can't be anything |
2737 | 0 | // after the path either. this url is likely uninitialized. |
2738 | 0 | if (mFilepath.mLen < 0) |
2739 | 0 | return SetPathQueryRef(flat); |
2740 | 0 | |
2741 | 0 | if (filepath && *filepath) { |
2742 | 0 | nsAutoCString spec; |
2743 | 0 | uint32_t dirPos, basePos, extPos; |
2744 | 0 | int32_t dirLen, baseLen, extLen; |
2745 | 0 | nsresult rv; |
2746 | 0 |
|
2747 | 0 | rv = mParser->ParseFilePath(filepath, flat.Length(), |
2748 | 0 | &dirPos, &dirLen, |
2749 | 0 | &basePos, &baseLen, |
2750 | 0 | &extPos, &extLen); |
2751 | 0 | if (NS_FAILED(rv)) return rv; |
2752 | 0 | |
2753 | 0 | // build up new candidate spec |
2754 | 0 | spec.Assign(mSpec.get(), mPath.mPos); |
2755 | 0 |
|
2756 | 0 | // ensure leading '/' |
2757 | 0 | if (filepath[dirPos] != '/') |
2758 | 0 | spec.Append('/'); |
2759 | 0 |
|
2760 | 0 | nsSegmentEncoder encoder; |
2761 | 0 |
|
2762 | 0 | // append encoded filepath components |
2763 | 0 | if (dirLen > 0) |
2764 | 0 | encoder.EncodeSegment(Substring(filepath + dirPos, |
2765 | 0 | filepath + dirPos + dirLen), |
2766 | 0 | esc_Directory | esc_AlwaysCopy, spec); |
2767 | 0 | if (baseLen > 0) |
2768 | 0 | encoder.EncodeSegment(Substring(filepath + basePos, |
2769 | 0 | filepath + basePos + baseLen), |
2770 | 0 | esc_FileBaseName | esc_AlwaysCopy, spec); |
2771 | 0 | if (extLen >= 0) { |
2772 | 0 | spec.Append('.'); |
2773 | 0 | if (extLen > 0) |
2774 | 0 | encoder.EncodeSegment(Substring(filepath + extPos, |
2775 | 0 | filepath + extPos + extLen), |
2776 | 0 | esc_FileExtension | esc_AlwaysCopy, |
2777 | 0 | spec); |
2778 | 0 | } |
2779 | 0 |
|
2780 | 0 | // compute the ending position of the current filepath |
2781 | 0 | if (mFilepath.mLen >= 0) { |
2782 | 0 | uint32_t end = mFilepath.mPos + mFilepath.mLen; |
2783 | 0 | if (mSpec.Length() > end) |
2784 | 0 | spec.Append(mSpec.get() + end, mSpec.Length() - end); |
2785 | 0 | } |
2786 | 0 |
|
2787 | 0 | return SetSpecInternal(spec); |
2788 | 0 | } |
2789 | 0 | if (mPath.mLen > 1) { |
2790 | 0 | mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1); |
2791 | 0 | // left shift query, and ref |
2792 | 0 | ShiftFromQuery(1 - mFilepath.mLen); |
2793 | 0 | // these contain only a '/' |
2794 | 0 | mPath.mLen = 1; |
2795 | 0 | mDirectory.mLen = 1; |
2796 | 0 | mFilepath.mLen = 1; |
2797 | 0 | // these are no longer defined |
2798 | 0 | mBasename.mLen = -1; |
2799 | 0 | mExtension.mLen = -1; |
2800 | 0 | } |
2801 | 0 | return NS_OK; |
2802 | 0 | } |
2803 | | |
2804 | | inline bool |
2805 | | IsUTFEncoding(const Encoding* aEncoding) |
2806 | 1.12M | { |
2807 | 1.12M | return aEncoding == UTF_8_ENCODING || |
2808 | 1.12M | aEncoding == UTF_16BE_ENCODING || |
2809 | 1.12M | aEncoding == UTF_16LE_ENCODING; |
2810 | 1.12M | } |
2811 | | |
2812 | | nsresult |
2813 | | nsStandardURL::SetQuery(const nsACString &input) |
2814 | 0 | { |
2815 | 0 | return SetQueryWithEncoding(input, nullptr); |
2816 | 0 | } |
2817 | | |
2818 | | nsresult |
2819 | | nsStandardURL::SetQueryWithEncoding(const nsACString &input, |
2820 | | const Encoding* encoding) |
2821 | 0 | { |
2822 | 0 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
2823 | 0 | const char *query = flat.get(); |
2824 | 0 |
|
2825 | 0 | LOG(("nsStandardURL::SetQuery [query=%s]\n", query)); |
2826 | 0 |
|
2827 | 0 | if (IsUTFEncoding(encoding)) { |
2828 | 0 | encoding = nullptr; |
2829 | 0 | } |
2830 | 0 |
|
2831 | 0 | if (mPath.mLen < 0) |
2832 | 0 | return SetPathQueryRef(flat); |
2833 | 0 | |
2834 | 0 | if (mSpec.Length() + input.Length() - Query().Length() > (uint32_t) net_GetURLMaxLength()) { |
2835 | 0 | return NS_ERROR_MALFORMED_URI; |
2836 | 0 | } |
2837 | 0 | |
2838 | 0 | InvalidateCache(); |
2839 | 0 |
|
2840 | 0 | if (!query || !*query) { |
2841 | 0 | // remove existing query |
2842 | 0 | if (mQuery.mLen >= 0) { |
2843 | 0 | // remove query and leading '?' |
2844 | 0 | mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1); |
2845 | 0 | ShiftFromRef(-(mQuery.mLen + 1)); |
2846 | 0 | mPath.mLen -= (mQuery.mLen + 1); |
2847 | 0 | mQuery.mPos = 0; |
2848 | 0 | mQuery.mLen = -1; |
2849 | 0 | } |
2850 | 0 | return NS_OK; |
2851 | 0 | } |
2852 | 0 |
|
2853 | 0 | int32_t queryLen = flat.Length(); |
2854 | 0 | if (query[0] == '?') { |
2855 | 0 | query++; |
2856 | 0 | queryLen--; |
2857 | 0 | } |
2858 | 0 |
|
2859 | 0 | if (mQuery.mLen < 0) { |
2860 | 0 | if (mRef.mLen < 0) |
2861 | 0 | mQuery.mPos = mSpec.Length(); |
2862 | 0 | else |
2863 | 0 | mQuery.mPos = mRef.mPos - 1; |
2864 | 0 | mSpec.Insert('?', mQuery.mPos); |
2865 | 0 | mQuery.mPos++; |
2866 | 0 | mQuery.mLen = 0; |
2867 | 0 | // the insertion pushes these out by 1 |
2868 | 0 | mPath.mLen++; |
2869 | 0 | mRef.mPos++; |
2870 | 0 | } |
2871 | 0 |
|
2872 | 0 | // encode query if necessary |
2873 | 0 | nsAutoCString buf; |
2874 | 0 | bool encoded; |
2875 | 0 | nsSegmentEncoder encoder(encoding); |
2876 | 0 | encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query, |
2877 | 0 | buf, encoded); |
2878 | 0 | if (encoded) { |
2879 | 0 | query = buf.get(); |
2880 | 0 | queryLen = buf.Length(); |
2881 | 0 | } |
2882 | 0 |
|
2883 | 0 | int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen); |
2884 | 0 |
|
2885 | 0 | if (shift) { |
2886 | 0 | mQuery.mLen = queryLen; |
2887 | 0 | mPath.mLen += shift; |
2888 | 0 | ShiftFromRef(shift); |
2889 | 0 | } |
2890 | 0 | return NS_OK; |
2891 | 0 | } |
2892 | | |
2893 | | nsresult |
2894 | | nsStandardURL::SetRef(const nsACString &input) |
2895 | 2.32k | { |
2896 | 2.32k | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
2897 | 2.32k | const char *ref = flat.get(); |
2898 | 2.32k | |
2899 | 2.32k | LOG(("nsStandardURL::SetRef [ref=%s]\n", ref)); |
2900 | 2.32k | |
2901 | 2.32k | if (mPath.mLen < 0) |
2902 | 0 | return SetPathQueryRef(flat); |
2903 | 2.32k | |
2904 | 2.32k | if (mSpec.Length() + input.Length() - Ref().Length() > (uint32_t) net_GetURLMaxLength()) { |
2905 | 0 | return NS_ERROR_MALFORMED_URI; |
2906 | 0 | } |
2907 | 2.32k | |
2908 | 2.32k | InvalidateCache(); |
2909 | 2.32k | |
2910 | 2.32k | if (!ref || !*ref) { |
2911 | 0 | // remove existing ref |
2912 | 0 | if (mRef.mLen >= 0) { |
2913 | 0 | // remove ref and leading '#' |
2914 | 0 | mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1); |
2915 | 0 | mPath.mLen -= (mRef.mLen + 1); |
2916 | 0 | mRef.mPos = 0; |
2917 | 0 | mRef.mLen = -1; |
2918 | 0 | } |
2919 | 0 | return NS_OK; |
2920 | 0 | } |
2921 | 2.32k | |
2922 | 2.32k | int32_t refLen = flat.Length(); |
2923 | 2.32k | if (ref[0] == '#') { |
2924 | 2.32k | ref++; |
2925 | 2.32k | refLen--; |
2926 | 2.32k | } |
2927 | 2.32k | |
2928 | 2.32k | if (mRef.mLen < 0) { |
2929 | 2.32k | mSpec.Append('#'); |
2930 | 2.32k | ++mPath.mLen; // Include the # in the path. |
2931 | 2.32k | mRef.mPos = mSpec.Length(); |
2932 | 2.32k | mRef.mLen = 0; |
2933 | 2.32k | } |
2934 | 2.32k | |
2935 | 2.32k | // If precent encoding is necessary, `ref` will point to `buf`'s content. |
2936 | 2.32k | // `buf` needs to outlive any use of the `ref` pointer. |
2937 | 2.32k | nsAutoCString buf; |
2938 | 2.32k | // encode ref if necessary |
2939 | 2.32k | bool encoded; |
2940 | 2.32k | nsSegmentEncoder encoder; |
2941 | 2.32k | encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref, |
2942 | 2.32k | buf, encoded); |
2943 | 2.32k | if (encoded) { |
2944 | 752 | ref = buf.get(); |
2945 | 752 | refLen = buf.Length(); |
2946 | 752 | } |
2947 | 2.32k | |
2948 | 2.32k | int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen); |
2949 | 2.32k | mPath.mLen += shift; |
2950 | 2.32k | mRef.mLen = refLen; |
2951 | 2.32k | return NS_OK; |
2952 | 2.32k | } |
2953 | | |
2954 | | nsresult |
2955 | | nsStandardURL::SetFileNameInternal(const nsACString &input) |
2956 | 0 | { |
2957 | 0 | const nsPromiseFlatCString &flat = PromiseFlatCString(input); |
2958 | 0 | const char *filename = flat.get(); |
2959 | 0 |
|
2960 | 0 | LOG(("nsStandardURL::SetFileNameInternal [filename=%s]\n", filename)); |
2961 | 0 |
|
2962 | 0 | if (mPath.mLen < 0) |
2963 | 0 | return SetPathQueryRef(flat); |
2964 | 0 | |
2965 | 0 | if (mSpec.Length() + input.Length() - Filename().Length() > (uint32_t) net_GetURLMaxLength()) { |
2966 | 0 | return NS_ERROR_MALFORMED_URI; |
2967 | 0 | } |
2968 | 0 | |
2969 | 0 | int32_t shift = 0; |
2970 | 0 |
|
2971 | 0 | if (!(filename && *filename)) { |
2972 | 0 | // remove the filename |
2973 | 0 | if (mBasename.mLen > 0) { |
2974 | 0 | if (mExtension.mLen >= 0) |
2975 | 0 | mBasename.mLen += (mExtension.mLen + 1); |
2976 | 0 | mSpec.Cut(mBasename.mPos, mBasename.mLen); |
2977 | 0 | shift = -mBasename.mLen; |
2978 | 0 | mBasename.mLen = 0; |
2979 | 0 | mExtension.mLen = -1; |
2980 | 0 | } |
2981 | 0 | } |
2982 | 0 | else { |
2983 | 0 | nsresult rv; |
2984 | 0 | URLSegment basename, extension; |
2985 | 0 |
|
2986 | 0 | // let the parser locate the basename and extension |
2987 | 0 | rv = mParser->ParseFileName(filename, flat.Length(), |
2988 | 0 | &basename.mPos, &basename.mLen, |
2989 | 0 | &extension.mPos, &extension.mLen); |
2990 | 0 | if (NS_FAILED(rv)) return rv; |
2991 | 0 | |
2992 | 0 | if (basename.mLen < 0) { |
2993 | 0 | // remove existing filename |
2994 | 0 | if (mBasename.mLen >= 0) { |
2995 | 0 | uint32_t len = mBasename.mLen; |
2996 | 0 | if (mExtension.mLen >= 0) |
2997 | 0 | len += (mExtension.mLen + 1); |
2998 | 0 | mSpec.Cut(mBasename.mPos, len); |
2999 | 0 | shift = -int32_t(len); |
3000 | 0 | mBasename.mLen = 0; |
3001 | 0 | mExtension.mLen = -1; |
3002 | 0 | } |
3003 | 0 | } |
3004 | 0 | else { |
3005 | 0 | nsAutoCString newFilename; |
3006 | 0 | bool ignoredOut; |
3007 | 0 | nsSegmentEncoder encoder; |
3008 | 0 | basename.mLen = encoder.EncodeSegmentCount(filename, basename, |
3009 | 0 | esc_FileBaseName | |
3010 | 0 | esc_AlwaysCopy, |
3011 | 0 | newFilename, |
3012 | 0 | ignoredOut); |
3013 | 0 | if (extension.mLen >= 0) { |
3014 | 0 | newFilename.Append('.'); |
3015 | 0 | extension.mLen = encoder.EncodeSegmentCount(filename, extension, |
3016 | 0 | esc_FileExtension | |
3017 | 0 | esc_AlwaysCopy, |
3018 | 0 | newFilename, |
3019 | 0 | ignoredOut); |
3020 | 0 | } |
3021 | 0 |
|
3022 | 0 | if (mBasename.mLen < 0) { |
3023 | 0 | // insert new filename |
3024 | 0 | mBasename.mPos = mDirectory.mPos + mDirectory.mLen; |
3025 | 0 | mSpec.Insert(newFilename, mBasename.mPos); |
3026 | 0 | shift = newFilename.Length(); |
3027 | 0 | } |
3028 | 0 | else { |
3029 | 0 | // replace existing filename |
3030 | 0 | uint32_t oldLen = uint32_t(mBasename.mLen); |
3031 | 0 | if (mExtension.mLen >= 0) |
3032 | 0 | oldLen += (mExtension.mLen + 1); |
3033 | 0 | mSpec.Replace(mBasename.mPos, oldLen, newFilename); |
3034 | 0 | shift = newFilename.Length() - oldLen; |
3035 | 0 | } |
3036 | 0 |
|
3037 | 0 | mBasename.mLen = basename.mLen; |
3038 | 0 | mExtension.mLen = extension.mLen; |
3039 | 0 | if (mExtension.mLen >= 0) |
3040 | 0 | mExtension.mPos = mBasename.mPos + mBasename.mLen + 1; |
3041 | 0 | } |
3042 | 0 | } |
3043 | 0 | if (shift) { |
3044 | 0 | ShiftFromQuery(shift); |
3045 | 0 | mFilepath.mLen += shift; |
3046 | 0 | mPath.mLen += shift; |
3047 | 0 | } |
3048 | 0 | return NS_OK; |
3049 | 0 | } |
3050 | | |
3051 | | nsresult |
3052 | | nsStandardURL::SetFileBaseNameInternal(const nsACString &input) |
3053 | 0 | { |
3054 | 0 | nsAutoCString extension; |
3055 | 0 | nsresult rv = GetFileExtension(extension); |
3056 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
3057 | 0 |
|
3058 | 0 | nsAutoCString newFileName(input); |
3059 | 0 |
|
3060 | 0 | if (!extension.IsEmpty()) { |
3061 | 0 | newFileName.Append('.'); |
3062 | 0 | newFileName.Append(extension); |
3063 | 0 | } |
3064 | 0 |
|
3065 | 0 | return SetFileNameInternal(newFileName); |
3066 | 0 | } |
3067 | | |
3068 | | nsresult |
3069 | | nsStandardURL::SetFileExtensionInternal(const nsACString &input) |
3070 | 0 | { |
3071 | 0 | nsAutoCString newFileName; |
3072 | 0 | nsresult rv = GetFileBaseName(newFileName); |
3073 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
3074 | 0 |
|
3075 | 0 | if (!input.IsEmpty()) { |
3076 | 0 | newFileName.Append('.'); |
3077 | 0 | newFileName.Append(input); |
3078 | 0 | } |
3079 | 0 |
|
3080 | 0 | return SetFileNameInternal(newFileName); |
3081 | 0 | } |
3082 | | |
3083 | | //---------------------------------------------------------------------------- |
3084 | | // nsStandardURL::nsIFileURL |
3085 | | //---------------------------------------------------------------------------- |
3086 | | |
3087 | | nsresult |
3088 | | nsStandardURL::EnsureFile() |
3089 | 9 | { |
3090 | 9 | MOZ_ASSERT(mSupportsFileURL, |
3091 | 9 | "EnsureFile() called on a URL that doesn't support files!"); |
3092 | 9 | |
3093 | 9 | if (mFile) { |
3094 | 0 | // Nothing to do |
3095 | 0 | return NS_OK; |
3096 | 0 | } |
3097 | 9 | |
3098 | 9 | // Parse the spec if we don't have a cached result |
3099 | 9 | if (mSpec.IsEmpty()) { |
3100 | 0 | NS_WARNING("url not initialized"); |
3101 | 0 | return NS_ERROR_NOT_INITIALIZED; |
3102 | 0 | } |
3103 | 9 | |
3104 | 9 | if (!SegmentIs(mScheme, "file")) { |
3105 | 0 | NS_WARNING("not a file URL"); |
3106 | 0 | return NS_ERROR_FAILURE; |
3107 | 0 | } |
3108 | 9 | |
3109 | 9 | return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile)); |
3110 | 9 | } |
3111 | | |
3112 | | NS_IMETHODIMP |
3113 | | nsStandardURL::GetFile(nsIFile **result) |
3114 | 13 | { |
3115 | 13 | MOZ_ASSERT(mSupportsFileURL, |
3116 | 13 | "GetFile() called on a URL that doesn't support files!"); |
3117 | 13 | |
3118 | 13 | nsresult rv = EnsureFile(); |
3119 | 13 | if (NS_FAILED(rv)) |
3120 | 13 | return rv; |
3121 | 9 | |
3122 | 9 | if (LOG_ENABLED()) { |
3123 | 0 | LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n", |
3124 | 0 | this, mSpec.get(), mFile->HumanReadablePath().get())); |
3125 | 0 | } |
3126 | 9 | |
3127 | 9 | // clone the file, so the caller can modify it. |
3128 | 9 | // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the |
3129 | 9 | // nsIFile returned from this method; but it seems that some folks do |
3130 | 9 | // (see bug 161921). until we can be sure that all the consumers are |
3131 | 9 | // behaving themselves, we'll stay on the safe side and clone the file. |
3132 | 9 | // see bug 212724 about fixing the consumers. |
3133 | 9 | return mFile->Clone(result); |
3134 | 9 | } |
3135 | | |
3136 | | nsresult |
3137 | | nsStandardURL::SetFile(nsIFile *file) |
3138 | 0 | { |
3139 | 0 | NS_ENSURE_ARG_POINTER(file); |
3140 | 0 |
|
3141 | 0 | nsresult rv; |
3142 | 0 | nsAutoCString url; |
3143 | 0 |
|
3144 | 0 | rv = net_GetURLSpecFromFile(file, url); |
3145 | 0 | if (NS_FAILED(rv)) return rv; |
3146 | 0 | |
3147 | 0 | uint32_t oldURLType = mURLType; |
3148 | 0 | uint32_t oldDefaultPort = mDefaultPort; |
3149 | 0 | rv = Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, url, nullptr, nullptr); |
3150 | 0 |
|
3151 | 0 | if (NS_FAILED(rv)) { |
3152 | 0 | // Restore the old url type and default port if the call to Init fails. |
3153 | 0 | mURLType = oldURLType; |
3154 | 0 | mDefaultPort = oldDefaultPort; |
3155 | 0 | return rv; |
3156 | 0 | } |
3157 | 0 | |
3158 | 0 | // must clone |file| since its value is not guaranteed to remain constant |
3159 | 0 | InvalidateCache(); |
3160 | 0 | if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) { |
3161 | 0 | NS_WARNING("nsIFile::Clone failed"); |
3162 | 0 | // failure to clone is not fatal (GetFile will generate mFile) |
3163 | 0 | mFile = nullptr; |
3164 | 0 | } |
3165 | 0 |
|
3166 | 0 | return NS_OK; |
3167 | 0 | } |
3168 | | |
3169 | | //---------------------------------------------------------------------------- |
3170 | | // nsStandardURL::nsIStandardURL |
3171 | | //---------------------------------------------------------------------------- |
3172 | | |
3173 | | nsresult |
3174 | | nsStandardURL::Init(uint32_t urlType, |
3175 | | int32_t defaultPort, |
3176 | | const nsACString &spec, |
3177 | | const char *charset, |
3178 | | nsIURI *baseURI) |
3179 | 1.12M | { |
3180 | 1.12M | if (spec.Length() > (uint32_t) net_GetURLMaxLength() || |
3181 | 1.12M | defaultPort > std::numeric_limits<uint16_t>::max()) { |
3182 | 0 | return NS_ERROR_MALFORMED_URI; |
3183 | 0 | } |
3184 | 1.12M | |
3185 | 1.12M | InvalidateCache(); |
3186 | 1.12M | |
3187 | 1.12M | switch (urlType) { |
3188 | 1.12M | case URLTYPE_STANDARD: |
3189 | 29.2k | mParser = net_GetStdURLParser(); |
3190 | 29.2k | break; |
3191 | 1.12M | case URLTYPE_AUTHORITY: |
3192 | 1.08M | mParser = net_GetAuthURLParser(); |
3193 | 1.08M | break; |
3194 | 1.12M | case URLTYPE_NO_AUTHORITY: |
3195 | 7.00k | mParser = net_GetNoAuthURLParser(); |
3196 | 7.00k | break; |
3197 | 1.12M | default: |
3198 | 0 | MOZ_ASSERT_UNREACHABLE("bad urlType"); |
3199 | 0 | return NS_ERROR_INVALID_ARG; |
3200 | 1.12M | } |
3201 | 1.12M | mDefaultPort = defaultPort; |
3202 | 1.12M | mURLType = urlType; |
3203 | 1.12M | |
3204 | 1.12M | auto encoding = |
3205 | 1.12M | charset ? Encoding::ForLabelNoReplacement(MakeStringSpan(charset)) |
3206 | 1.12M | : nullptr; |
3207 | 1.12M | // URI can't be encoded in UTF-16BE or UTF-16LE. Truncate encoding |
3208 | 1.12M | // if it is one of utf encodings (since a null encoding implies |
3209 | 1.12M | // UTF-8, this is safe even if encoding is UTF-8). |
3210 | 1.12M | if (IsUTFEncoding(encoding)) { |
3211 | 0 | encoding = nullptr; |
3212 | 0 | } |
3213 | 1.12M | |
3214 | 1.12M | if (baseURI && net_IsAbsoluteURL(spec)) { |
3215 | 3.08k | baseURI = nullptr; |
3216 | 3.08k | } |
3217 | 1.12M | |
3218 | 1.12M | if (!baseURI) |
3219 | 11.1k | return SetSpecWithEncoding(spec, encoding); |
3220 | 1.11M | |
3221 | 1.11M | nsAutoCString buf; |
3222 | 1.11M | nsresult rv = baseURI->Resolve(spec, buf); |
3223 | 1.11M | if (NS_FAILED(rv)) return rv; |
3224 | 1.11M | |
3225 | 1.11M | return SetSpecWithEncoding(buf, encoding); |
3226 | 1.11M | } |
3227 | | |
3228 | | nsresult |
3229 | | nsStandardURL::SetDefaultPort(int32_t aNewDefaultPort) |
3230 | 0 | { |
3231 | 0 | InvalidateCache(); |
3232 | 0 |
|
3233 | 0 | // should never be more than 16 bit |
3234 | 0 | if (aNewDefaultPort >= std::numeric_limits<uint16_t>::max()) { |
3235 | 0 | return NS_ERROR_MALFORMED_URI; |
3236 | 0 | } |
3237 | 0 | |
3238 | 0 | // If we're already using the new default-port as a custom port, then clear |
3239 | 0 | // it off of our mSpec & set mPort to -1, to indicate that we'll be using |
3240 | 0 | // the default from now on (which happens to match what we already had). |
3241 | 0 | if (mPort == aNewDefaultPort) { |
3242 | 0 | ReplacePortInSpec(-1); |
3243 | 0 | mPort = -1; |
3244 | 0 | } |
3245 | 0 | mDefaultPort = aNewDefaultPort; |
3246 | 0 |
|
3247 | 0 | return NS_OK; |
3248 | 0 | } |
3249 | | |
3250 | | //---------------------------------------------------------------------------- |
3251 | | // nsStandardURL::nsISerializable |
3252 | | //---------------------------------------------------------------------------- |
3253 | | |
3254 | | NS_IMETHODIMP |
3255 | | nsStandardURL::Read(nsIObjectInputStream *stream) |
3256 | 0 | { |
3257 | 0 | MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); |
3258 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
3259 | 0 | } |
3260 | | |
3261 | | nsresult |
3262 | | nsStandardURL::ReadPrivate(nsIObjectInputStream *stream) |
3263 | 0 | { |
3264 | 0 | MOZ_ASSERT(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host"); |
3265 | 0 |
|
3266 | 0 | nsresult rv; |
3267 | 0 |
|
3268 | 0 | uint32_t urlType; |
3269 | 0 | rv = stream->Read32(&urlType); |
3270 | 0 | if (NS_FAILED(rv)) return rv; |
3271 | 0 | mURLType = urlType; |
3272 | 0 | switch (mURLType) { |
3273 | 0 | case URLTYPE_STANDARD: |
3274 | 0 | mParser = net_GetStdURLParser(); |
3275 | 0 | break; |
3276 | 0 | case URLTYPE_AUTHORITY: |
3277 | 0 | mParser = net_GetAuthURLParser(); |
3278 | 0 | break; |
3279 | 0 | case URLTYPE_NO_AUTHORITY: |
3280 | 0 | mParser = net_GetNoAuthURLParser(); |
3281 | 0 | break; |
3282 | 0 | default: |
3283 | 0 | MOZ_ASSERT_UNREACHABLE("bad urlType"); |
3284 | 0 | return NS_ERROR_FAILURE; |
3285 | 0 | } |
3286 | 0 |
|
3287 | 0 | rv = stream->Read32((uint32_t *) &mPort); |
3288 | 0 | if (NS_FAILED(rv)) return rv; |
3289 | 0 | |
3290 | 0 | rv = stream->Read32((uint32_t *) &mDefaultPort); |
3291 | 0 | if (NS_FAILED(rv)) return rv; |
3292 | 0 | |
3293 | 0 | rv = NS_ReadOptionalCString(stream, mSpec); |
3294 | 0 | if (NS_FAILED(rv)) return rv; |
3295 | 0 | |
3296 | 0 | rv = ReadSegment(stream, mScheme); |
3297 | 0 | if (NS_FAILED(rv)) return rv; |
3298 | 0 | |
3299 | 0 | rv = ReadSegment(stream, mAuthority); |
3300 | 0 | if (NS_FAILED(rv)) return rv; |
3301 | 0 | |
3302 | 0 | rv = ReadSegment(stream, mUsername); |
3303 | 0 | if (NS_FAILED(rv)) return rv; |
3304 | 0 | |
3305 | 0 | rv = ReadSegment(stream, mPassword); |
3306 | 0 | if (NS_FAILED(rv)) return rv; |
3307 | 0 | |
3308 | 0 | rv = ReadSegment(stream, mHost); |
3309 | 0 | if (NS_FAILED(rv)) return rv; |
3310 | 0 | |
3311 | 0 | rv = ReadSegment(stream, mPath); |
3312 | 0 | if (NS_FAILED(rv)) return rv; |
3313 | 0 | |
3314 | 0 | rv = ReadSegment(stream, mFilepath); |
3315 | 0 | if (NS_FAILED(rv)) return rv; |
3316 | 0 | |
3317 | 0 | rv = ReadSegment(stream, mDirectory); |
3318 | 0 | if (NS_FAILED(rv)) return rv; |
3319 | 0 | |
3320 | 0 | rv = ReadSegment(stream, mBasename); |
3321 | 0 | if (NS_FAILED(rv)) return rv; |
3322 | 0 | |
3323 | 0 | rv = ReadSegment(stream, mExtension); |
3324 | 0 | if (NS_FAILED(rv)) return rv; |
3325 | 0 | |
3326 | 0 | // handle forward compatibility from older serializations that included mParam |
3327 | 0 | URLSegment old_param; |
3328 | 0 | rv = ReadSegment(stream, old_param); |
3329 | 0 | if (NS_FAILED(rv)) return rv; |
3330 | 0 | |
3331 | 0 | rv = ReadSegment(stream, mQuery); |
3332 | 0 | if (NS_FAILED(rv)) return rv; |
3333 | 0 | |
3334 | 0 | rv = ReadSegment(stream, mRef); |
3335 | 0 | if (NS_FAILED(rv)) return rv; |
3336 | 0 | |
3337 | 0 | nsAutoCString oldOriginCharset; |
3338 | 0 | rv = NS_ReadOptionalCString(stream, oldOriginCharset); |
3339 | 0 | if (NS_FAILED(rv)) return rv; |
3340 | 0 | |
3341 | 0 | bool isMutable; |
3342 | 0 | rv = stream->ReadBoolean(&isMutable); |
3343 | 0 | if (NS_FAILED(rv)) return rv; |
3344 | 0 | Unused << isMutable; |
3345 | 0 |
|
3346 | 0 | bool supportsFileURL; |
3347 | 0 | rv = stream->ReadBoolean(&supportsFileURL); |
3348 | 0 | if (NS_FAILED(rv)) return rv; |
3349 | 0 | mSupportsFileURL = supportsFileURL; |
3350 | 0 |
|
3351 | 0 | // wait until object is set up, then modify path to include the param |
3352 | 0 | if (old_param.mLen >= 0) { // note that mLen=0 is ";" |
3353 | 0 | // If this wasn't empty, it marks characters between the end of the |
3354 | 0 | // file and start of the query - mPath should include the param, |
3355 | 0 | // query and ref already. Bump the mFilePath and |
3356 | 0 | // directory/basename/extension components to include this. |
3357 | 0 | mFilepath.Merge(mSpec, ';', old_param); |
3358 | 0 | mDirectory.Merge(mSpec, ';', old_param); |
3359 | 0 | mBasename.Merge(mSpec, ';', old_param); |
3360 | 0 | mExtension.Merge(mSpec, ';', old_param); |
3361 | 0 | } |
3362 | 0 |
|
3363 | 0 | rv = CheckIfHostIsAscii(); |
3364 | 0 | if (NS_FAILED(rv)) { |
3365 | 0 | return rv; |
3366 | 0 | } |
3367 | 0 | |
3368 | 0 | return NS_OK; |
3369 | 0 | } |
3370 | | |
3371 | | NS_IMETHODIMP |
3372 | | nsStandardURL::Write(nsIObjectOutputStream *stream) |
3373 | 0 | { |
3374 | 0 | MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), |
3375 | 0 | "The spec should never be this long, we missed a check."); |
3376 | 0 | nsresult rv; |
3377 | 0 |
|
3378 | 0 | rv = stream->Write32(mURLType); |
3379 | 0 | if (NS_FAILED(rv)) return rv; |
3380 | 0 | |
3381 | 0 | rv = stream->Write32(uint32_t(mPort)); |
3382 | 0 | if (NS_FAILED(rv)) return rv; |
3383 | 0 | |
3384 | 0 | rv = stream->Write32(uint32_t(mDefaultPort)); |
3385 | 0 | if (NS_FAILED(rv)) return rv; |
3386 | 0 | |
3387 | 0 | rv = NS_WriteOptionalStringZ(stream, mSpec.get()); |
3388 | 0 | if (NS_FAILED(rv)) return rv; |
3389 | 0 | |
3390 | 0 | rv = WriteSegment(stream, mScheme); |
3391 | 0 | if (NS_FAILED(rv)) return rv; |
3392 | 0 | |
3393 | 0 | rv = WriteSegment(stream, mAuthority); |
3394 | 0 | if (NS_FAILED(rv)) return rv; |
3395 | 0 | |
3396 | 0 | rv = WriteSegment(stream, mUsername); |
3397 | 0 | if (NS_FAILED(rv)) return rv; |
3398 | 0 | |
3399 | 0 | rv = WriteSegment(stream, mPassword); |
3400 | 0 | if (NS_FAILED(rv)) return rv; |
3401 | 0 | |
3402 | 0 | rv = WriteSegment(stream, mHost); |
3403 | 0 | if (NS_FAILED(rv)) return rv; |
3404 | 0 | |
3405 | 0 | rv = WriteSegment(stream, mPath); |
3406 | 0 | if (NS_FAILED(rv)) return rv; |
3407 | 0 | |
3408 | 0 | rv = WriteSegment(stream, mFilepath); |
3409 | 0 | if (NS_FAILED(rv)) return rv; |
3410 | 0 | |
3411 | 0 | rv = WriteSegment(stream, mDirectory); |
3412 | 0 | if (NS_FAILED(rv)) return rv; |
3413 | 0 | |
3414 | 0 | rv = WriteSegment(stream, mBasename); |
3415 | 0 | if (NS_FAILED(rv)) return rv; |
3416 | 0 | |
3417 | 0 | rv = WriteSegment(stream, mExtension); |
3418 | 0 | if (NS_FAILED(rv)) return rv; |
3419 | 0 | |
3420 | 0 | // for backwards compatibility since we removed mParam. Note that this will mean that |
3421 | 0 | // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they |
3422 | 0 | // after the removal of special handling). It only matters if you downgrade a browser to before |
3423 | 0 | // the patch. |
3424 | 0 | URLSegment empty; |
3425 | 0 | rv = WriteSegment(stream, empty); |
3426 | 0 | if (NS_FAILED(rv)) return rv; |
3427 | 0 | |
3428 | 0 | rv = WriteSegment(stream, mQuery); |
3429 | 0 | if (NS_FAILED(rv)) return rv; |
3430 | 0 | |
3431 | 0 | rv = WriteSegment(stream, mRef); |
3432 | 0 | if (NS_FAILED(rv)) return rv; |
3433 | 0 | |
3434 | 0 | // former origin charset |
3435 | 0 | rv = NS_WriteOptionalStringZ(stream, EmptyCString().get()); |
3436 | 0 | if (NS_FAILED(rv)) return rv; |
3437 | 0 | |
3438 | 0 | // former mMutable |
3439 | 0 | rv = stream->WriteBoolean(false); |
3440 | 0 | if (NS_FAILED(rv)) return rv; |
3441 | 0 | |
3442 | 0 | rv = stream->WriteBoolean(mSupportsFileURL); |
3443 | 0 | if (NS_FAILED(rv)) return rv; |
3444 | 0 | |
3445 | 0 | // mDisplayHost is just a cache that can be recovered as needed. |
3446 | 0 | |
3447 | 0 | return NS_OK; |
3448 | 0 | } |
3449 | | |
3450 | | //--------------------------------------------------------------------------- |
3451 | | // nsStandardURL::nsIIPCSerializableURI |
3452 | | //--------------------------------------------------------------------------- |
3453 | | |
3454 | | inline |
3455 | | ipc::StandardURLSegment |
3456 | | ToIPCSegment(const nsStandardURL::URLSegment& aSegment) |
3457 | 0 | { |
3458 | 0 | return ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen); |
3459 | 0 | } |
3460 | | |
3461 | | inline |
3462 | | MOZ_MUST_USE bool |
3463 | | FromIPCSegment(const nsACString& aSpec, const ipc::StandardURLSegment& aSegment, nsStandardURL::URLSegment& aTarget) |
3464 | 0 | { |
3465 | 0 | // This seems to be just an empty segment. |
3466 | 0 | if (aSegment.length() == -1) { |
3467 | 0 | aTarget = nsStandardURL::URLSegment(); |
3468 | 0 | return true; |
3469 | 0 | } |
3470 | 0 | |
3471 | 0 | // A value of -1 means an empty segment, but < -1 is undefined. |
3472 | 0 | if (NS_WARN_IF(aSegment.length() < -1)) { |
3473 | 0 | return false; |
3474 | 0 | } |
3475 | 0 | |
3476 | 0 | CheckedInt<uint32_t> segmentLen = aSegment.position(); |
3477 | 0 | segmentLen += aSegment.length(); |
3478 | 0 | // Make sure the segment does not extend beyond the spec. |
3479 | 0 | if (NS_WARN_IF(!segmentLen.isValid() || segmentLen.value() > aSpec.Length())) { |
3480 | 0 | return false; |
3481 | 0 | } |
3482 | 0 | |
3483 | 0 | aTarget.mPos = aSegment.position(); |
3484 | 0 | aTarget.mLen = aSegment.length(); |
3485 | 0 |
|
3486 | 0 | return true; |
3487 | 0 | } |
3488 | | |
3489 | | void |
3490 | | nsStandardURL::Serialize(URIParams& aParams) |
3491 | 0 | { |
3492 | 0 | MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), |
3493 | 0 | "The spec should never be this long, we missed a check."); |
3494 | 0 | StandardURLParams params; |
3495 | 0 |
|
3496 | 0 | params.urlType() = mURLType; |
3497 | 0 | params.port() = mPort; |
3498 | 0 | params.defaultPort() = mDefaultPort; |
3499 | 0 | params.spec() = mSpec; |
3500 | 0 | params.scheme() = ToIPCSegment(mScheme); |
3501 | 0 | params.authority() = ToIPCSegment(mAuthority); |
3502 | 0 | params.username() = ToIPCSegment(mUsername); |
3503 | 0 | params.password() = ToIPCSegment(mPassword); |
3504 | 0 | params.host() = ToIPCSegment(mHost); |
3505 | 0 | params.path() = ToIPCSegment(mPath); |
3506 | 0 | params.filePath() = ToIPCSegment(mFilepath); |
3507 | 0 | params.directory() = ToIPCSegment(mDirectory); |
3508 | 0 | params.baseName() = ToIPCSegment(mBasename); |
3509 | 0 | params.extension() = ToIPCSegment(mExtension); |
3510 | 0 | params.query() = ToIPCSegment(mQuery); |
3511 | 0 | params.ref() = ToIPCSegment(mRef); |
3512 | 0 | params.supportsFileURL() = !!mSupportsFileURL; |
3513 | 0 | // mDisplayHost is just a cache that can be recovered as needed. |
3514 | 0 |
|
3515 | 0 | aParams = params; |
3516 | 0 | } |
3517 | | |
3518 | | bool |
3519 | | nsStandardURL::Deserialize(const URIParams& aParams) |
3520 | 0 | { |
3521 | 0 | MOZ_ASSERT(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host"); |
3522 | 0 | MOZ_ASSERT(!mFile, "Shouldn't have cached file"); |
3523 | 0 |
|
3524 | 0 | if (aParams.type() != URIParams::TStandardURLParams) { |
3525 | 0 | NS_ERROR("Received unknown parameters from the other process!"); |
3526 | 0 | return false; |
3527 | 0 | } |
3528 | 0 |
|
3529 | 0 | const StandardURLParams& params = aParams.get_StandardURLParams(); |
3530 | 0 |
|
3531 | 0 | mURLType = params.urlType(); |
3532 | 0 | switch (mURLType) { |
3533 | 0 | case URLTYPE_STANDARD: |
3534 | 0 | mParser = net_GetStdURLParser(); |
3535 | 0 | break; |
3536 | 0 | case URLTYPE_AUTHORITY: |
3537 | 0 | mParser = net_GetAuthURLParser(); |
3538 | 0 | break; |
3539 | 0 | case URLTYPE_NO_AUTHORITY: |
3540 | 0 | mParser = net_GetNoAuthURLParser(); |
3541 | 0 | break; |
3542 | 0 | default: |
3543 | 0 | MOZ_ASSERT_UNREACHABLE("bad urlType"); |
3544 | 0 | return false; |
3545 | 0 | } |
3546 | 0 |
|
3547 | 0 | mPort = params.port(); |
3548 | 0 | mDefaultPort = params.defaultPort(); |
3549 | 0 | mSpec = params.spec(); |
3550 | 0 | NS_ENSURE_TRUE(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), false); |
3551 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.scheme(), mScheme), false); |
3552 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.authority(), mAuthority), false); |
3553 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.username(), mUsername), false); |
3554 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.password(), mPassword), false); |
3555 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.host(), mHost), false); |
3556 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.path(), mPath), false); |
3557 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.filePath(), mFilepath), false); |
3558 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.directory(), mDirectory), false); |
3559 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.baseName(), mBasename), false); |
3560 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.extension(), mExtension), false); |
3561 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.query(), mQuery), false); |
3562 | 0 | NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.ref(), mRef), false); |
3563 | 0 |
|
3564 | 0 | mSupportsFileURL = params.supportsFileURL(); |
3565 | 0 |
|
3566 | 0 | nsresult rv = CheckIfHostIsAscii(); |
3567 | 0 | if (NS_FAILED(rv)) { |
3568 | 0 | return false; |
3569 | 0 | } |
3570 | 0 | |
3571 | 0 | // Some sanity checks |
3572 | 0 | NS_ENSURE_TRUE(mScheme.mPos == 0, false); |
3573 | 0 | NS_ENSURE_TRUE(mScheme.mLen > 0, false); |
3574 | 0 | // Make sure scheme is followed by :// (3 characters) |
3575 | 0 | NS_ENSURE_TRUE(mScheme.mLen < INT32_MAX - 3, false); // avoid overflow |
3576 | 0 | NS_ENSURE_TRUE(mSpec.Length() >= (uint32_t) mScheme.mLen + 3, false); |
3577 | 0 | NS_ENSURE_TRUE(nsDependentCSubstring(mSpec, mScheme.mLen, 3).EqualsLiteral("://"), false); |
3578 | 0 | NS_ENSURE_TRUE(mPath.mLen != -1 && mSpec.CharAt(mPath.mPos) == '/', false); |
3579 | 0 | NS_ENSURE_TRUE(mPath.mPos == mFilepath.mPos, false); |
3580 | 0 | NS_ENSURE_TRUE(mQuery.mLen == -1 || mSpec.CharAt(mQuery.mPos - 1) == '?', false); |
3581 | 0 | NS_ENSURE_TRUE(mRef.mLen == -1 || mSpec.CharAt(mRef.mPos - 1) == '#', false); |
3582 | 0 |
|
3583 | 0 | return true; |
3584 | 0 | } |
3585 | | |
3586 | | //---------------------------------------------------------------------------- |
3587 | | // nsStandardURL::nsIClassInfo |
3588 | | //---------------------------------------------------------------------------- |
3589 | | |
3590 | | NS_IMETHODIMP |
3591 | | nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array) |
3592 | 841 | { |
3593 | 841 | *count = 0; |
3594 | 841 | *array = nullptr; |
3595 | 841 | return NS_OK; |
3596 | 841 | } |
3597 | | |
3598 | | NS_IMETHODIMP |
3599 | | nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval) |
3600 | 841 | { |
3601 | 841 | *_retval = nullptr; |
3602 | 841 | return NS_OK; |
3603 | 841 | } |
3604 | | |
3605 | | NS_IMETHODIMP |
3606 | | nsStandardURL::GetContractID(nsACString& aContractID) |
3607 | 0 | { |
3608 | 0 | aContractID.SetIsVoid(true); |
3609 | 0 | return NS_OK; |
3610 | 0 | } |
3611 | | |
3612 | | NS_IMETHODIMP |
3613 | | nsStandardURL::GetClassDescription(nsACString& aClassDescription) |
3614 | 0 | { |
3615 | 0 | aClassDescription.SetIsVoid(true); |
3616 | 0 | return NS_OK; |
3617 | 0 | } |
3618 | | |
3619 | | NS_IMETHODIMP |
3620 | | nsStandardURL::GetClassID(nsCID * *aClassID) |
3621 | 0 | { |
3622 | 0 | *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID)); |
3623 | 0 | return GetClassIDNoAlloc(*aClassID); |
3624 | 0 | } |
3625 | | |
3626 | | NS_IMETHODIMP |
3627 | | nsStandardURL::GetFlags(uint32_t *aFlags) |
3628 | 0 | { |
3629 | 0 | *aFlags = nsIClassInfo::MAIN_THREAD_ONLY; |
3630 | 0 | return NS_OK; |
3631 | 0 | } |
3632 | | |
3633 | | NS_IMETHODIMP |
3634 | | nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) |
3635 | 0 | { |
3636 | 0 | *aClassIDNoAlloc = kStandardURLCID; |
3637 | 0 | return NS_OK; |
3638 | 0 | } |
3639 | | |
3640 | | //---------------------------------------------------------------------------- |
3641 | | // nsStandardURL::nsISizeOf |
3642 | | //---------------------------------------------------------------------------- |
3643 | | |
3644 | | size_t |
3645 | | nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
3646 | 0 | { |
3647 | 0 | return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + |
3648 | 0 | mDisplayHost.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
3649 | 0 |
|
3650 | 0 | // Measurement of the following members may be added later if DMD finds it is |
3651 | 0 | // worthwhile: |
3652 | 0 | // - mParser |
3653 | 0 | // - mFile |
3654 | 0 | } |
3655 | | |
3656 | | size_t |
3657 | 0 | nsStandardURL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { |
3658 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
3659 | 0 | } |
3660 | | |
3661 | | } // namespace net |
3662 | | } // namespace mozilla |
3663 | | |
3664 | | // For unit tests. Including nsStandardURL.h seems to cause problems |
3665 | | nsresult |
3666 | | Test_NormalizeIPv4(const nsACString& host, nsCString& result) |
3667 | 0 | { |
3668 | 0 | return mozilla::net::nsStandardURL::NormalizeIPv4(host, result); |
3669 | 0 | } |