Coverage Report

Created: 2018-09-25 14:53

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