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