1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsStandardURL_h__
7 #define nsStandardURL_h__
12 #include "nsISerializable.h"
13 #include "nsIFileURL.h"
14 #include "nsIStandardURL.h"
15 #include "mozilla/Encoding.h"
17 #include "nsURLHelper.h"
18 #include "nsISizeOf.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/LinkedList.h"
21 #include "mozilla/MemoryReporting.h"
22 #include "nsISensitiveInfoHiddenURI.h"
23 #include "nsIURIMutator.h"
25 #ifdef NS_BUILD_REFCNT_LOGGING
26 # define DEBUG_DUMP_URLS_AT_SHUTDOWN
29 class nsIBinaryInputStream
;
30 class nsIBinaryOutputStream
;
41 class URLSegmentNumber
{
46 URLSegmentNumber() = default;
47 explicit URLSegmentNumber(T data
) : mData(data
) {
48 mParity
= CalculateParity();
50 bool operator==(URLSegmentNumber value
) const { return mData
== value
.mData
; }
51 bool operator!=(URLSegmentNumber value
) const { return mData
!= value
.mData
; }
52 bool operator>(URLSegmentNumber value
) const { return mData
> value
.mData
; }
53 URLSegmentNumber
operator+(int32_t value
) const {
54 return URLSegmentNumber(mData
+ value
);
56 URLSegmentNumber
operator+(uint32_t value
) const {
57 return URLSegmentNumber(mData
+ value
);
59 URLSegmentNumber
operator-(int32_t value
) const {
60 return URLSegmentNumber(mData
- value
);
62 URLSegmentNumber
operator-(uint32_t value
) const {
63 return URLSegmentNumber(mData
- value
);
65 URLSegmentNumber
operator+=(URLSegmentNumber value
) {
67 mParity
= CalculateParity();
70 URLSegmentNumber
operator+=(T value
) {
72 mParity
= CalculateParity();
75 URLSegmentNumber
operator-=(URLSegmentNumber value
) {
77 mParity
= CalculateParity();
80 URLSegmentNumber
operator-=(T value
) {
82 mParity
= CalculateParity();
85 operator T() const { return mData
; }
86 URLSegmentNumber
& operator=(T value
) {
88 mParity
= CalculateParity();
91 URLSegmentNumber
& operator++() {
93 mParity
= CalculateParity();
96 URLSegmentNumber
operator++(int) {
97 URLSegmentNumber value
= *this;
101 bool CalculateParity() const {
102 std::bitset
<32> bits((uint32_t)mData
);
103 return bits
.count() % 2 == 0 ? false : true;
105 bool Parity() const { return mParity
; }
108 //-----------------------------------------------------------------------------
109 // standard URL implementation
110 //-----------------------------------------------------------------------------
112 class nsStandardURL
: public nsIFileURL
,
113 public nsIStandardURL
,
114 public nsISerializable
,
116 public nsISensitiveInfoHiddenURI
117 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
119 public LinkedListElement
<nsStandardURL
>
123 virtual ~nsStandardURL();
124 explicit nsStandardURL(bool aSupportsFileURL
= false, bool aTrackURL
= true);
127 NS_DECL_THREADSAFE_ISUPPORTS
131 NS_DECL_NSISTANDARDURL
132 NS_DECL_NSISERIALIZABLE
133 NS_DECL_NSISENSITIVEINFOHIDDENURI
136 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const override
;
137 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const override
;
139 static void InitGlobalObjects();
140 static void ShutdownGlobalObjects();
142 public: /* internal -- HPUX compiler can't handle this being private */
144 // location and length of an url segment relative to mSpec
147 #ifdef EARLY_BETA_OR_EARLIER
148 URLSegmentNumber
<uint32_t> mPos
{0};
149 URLSegmentNumber
<int32_t> mLen
{-1};
155 URLSegment() = default;
156 URLSegment(uint32_t pos
, int32_t len
) : mPos(pos
), mLen(len
) {}
157 URLSegment(const URLSegment
& aCopy
) = default;
162 // Merge another segment following this one to it if they're contiguous
163 // Assumes we have something like "foo;bar" where this object is 'foo' and
165 void Merge(const nsCString
& spec
, const char separator
,
166 const URLSegment
& right
) {
167 if (mLen
>= 0 && *(spec
.get() + mPos
+ mLen
) == separator
&&
168 mPos
+ mLen
+ 1 == right
.mPos
) {
169 mLen
+= 1 + right
.mLen
;
175 // URL segment encoder : performs charset conversion and URL escaping.
177 class nsSegmentEncoder
{
179 explicit nsSegmentEncoder(const Encoding
* encoding
= nullptr);
181 // Encode the given segment if necessary, and return the length of
182 // the encoded segment. The encoded segment is appended to |aOut|
183 // if and only if encoding is required.
184 int32_t EncodeSegmentCount(const char* str
, const URLSegment
& aSeg
,
185 int16_t mask
, nsCString
& aOut
, bool& appended
,
186 uint32_t extraLen
= 0);
188 // Encode the given string if necessary, and return a reference to
189 // the encoded string. Returns a reference to |result| if encoding
190 // is required. Otherwise, a reference to |str| is returned.
191 const nsACString
& EncodeSegment(const nsACString
& str
, int16_t mask
,
195 const Encoding
* mEncoding
;
197 friend class nsSegmentEncoder
;
199 static nsresult
NormalizeIPv4(const nsACString
& host
, nsCString
& result
);
201 static nsIIDNService
* GetIDNService();
204 // enum used in a few places to specify how .ref attribute should be handled
205 enum RefHandlingEnum
{ eIgnoreRef
, eHonorRef
, eReplaceRef
};
207 // Helper to share code between Equals and EqualsExceptRef
208 // NOTE: *not* virtual, because no one needs to override this so far...
209 nsresult
EqualsInternal(nsIURI
* unknownOther
, RefHandlingEnum refHandlingMode
,
212 virtual nsStandardURL
* StartClone();
214 // Helper to share code between Clone methods.
215 nsresult
CloneInternal(RefHandlingEnum aRefHandlingMode
,
216 const nsACString
& aNewRef
, nsIURI
** aClone
);
217 // Helper method that copies member variables from the source StandardURL
218 // if copyCached = true, it will also copy mFile and mDisplayHost
219 nsresult
CopyMembers(nsStandardURL
* source
, RefHandlingEnum mode
,
220 const nsACString
& newRef
, bool copyCached
= false);
222 // Helper for subclass implementation of GetFile(). Subclasses that map
223 // URIs to files in a special way should implement this method. It should
224 // ensure that our mFile is initialized, if it's possible.
225 // returns NS_ERROR_NO_INTERFACE if the url does not map to a file
226 virtual nsresult
EnsureFile();
228 virtual nsresult
Clone(nsIURI
** aURI
);
229 virtual nsresult
SetSpecInternal(const nsACString
& input
);
230 virtual nsresult
SetScheme(const nsACString
& input
);
231 virtual nsresult
SetUserPass(const nsACString
& input
);
232 virtual nsresult
SetUsername(const nsACString
& input
);
233 virtual nsresult
SetPassword(const nsACString
& input
);
234 virtual nsresult
SetHostPort(const nsACString
& aValue
);
235 virtual nsresult
SetHost(const nsACString
& input
);
236 virtual nsresult
SetPort(int32_t port
);
237 virtual nsresult
SetPathQueryRef(const nsACString
& input
);
238 virtual nsresult
SetRef(const nsACString
& input
);
239 virtual nsresult
SetFilePath(const nsACString
& input
);
240 virtual nsresult
SetQuery(const nsACString
& input
);
241 virtual nsresult
SetQueryWithEncoding(const nsACString
& input
,
242 const Encoding
* encoding
);
243 bool Deserialize(const mozilla::ipc::URIParams
&);
244 nsresult
ReadPrivate(nsIObjectInputStream
* stream
);
247 nsresult
Init(uint32_t urlType
, int32_t defaultPort
, const nsACString
& spec
,
248 const char* charset
, nsIURI
* baseURI
);
249 nsresult
SetDefaultPort(int32_t aNewDefaultPort
);
250 nsresult
SetFile(nsIFile
* file
);
252 nsresult
SetFileNameInternal(const nsACString
& input
);
253 nsresult
SetFileBaseNameInternal(const nsACString
& input
);
254 nsresult
SetFileExtensionInternal(const nsACString
& input
);
256 int32_t Port() { return mPort
== -1 ? mDefaultPort
: mPort
; }
258 void ReplacePortInSpec(int32_t aNewPort
);
260 void InvalidateCache(bool invalidateCachedFile
= true);
262 bool ValidIPv6orHostname(const char* host
, uint32_t length
);
263 static bool IsValidOfBase(unsigned char c
, const uint32_t base
);
264 nsresult
NormalizeIDN(const nsCString
& host
, nsCString
& result
);
265 nsresult
CheckIfHostIsAscii();
266 void CoalescePath(netCoalesceFlags coalesceFlag
, char* path
);
268 uint32_t AppendSegmentToBuf(char*, uint32_t, const char*,
269 const URLSegment
& input
, URLSegment
& output
,
270 const nsCString
* esc
= nullptr,
271 bool useEsc
= false, int32_t* diff
= nullptr);
272 uint32_t AppendToBuf(char*, uint32_t, const char*, uint32_t);
274 nsresult
BuildNormalizedSpec(const char* spec
, const Encoding
* encoding
);
275 nsresult
SetSpecWithEncoding(const nsACString
& input
,
276 const Encoding
* encoding
);
278 bool SegmentIs(const URLSegment
& seg
, const char* val
,
279 bool ignoreCase
= false);
280 bool SegmentIs(const char* spec
, const URLSegment
& seg
, const char* val
,
281 bool ignoreCase
= false);
282 bool SegmentIs(const URLSegment
& seg1
, const char* val
,
283 const URLSegment
& seg2
, bool ignoreCase
= false);
285 int32_t ReplaceSegment(uint32_t pos
, uint32_t len
, const char* val
,
287 int32_t ReplaceSegment(uint32_t pos
, uint32_t len
, const nsACString
& val
);
289 nsresult
ParseURL(const char* spec
, int32_t specLen
);
290 nsresult
ParsePath(const char* spec
, uint32_t pathPos
, int32_t pathLen
= -1);
292 char* AppendToSubstring(uint32_t pos
, int32_t len
, const char* tail
);
294 // dependent substring helpers
295 nsDependentCSubstring
Segment(uint32_t pos
, int32_t len
); // see below
296 nsDependentCSubstring
Segment(const URLSegment
& s
) {
297 return Segment(s
.mPos
, s
.mLen
);
300 // dependent substring getters
301 nsDependentCSubstring
Prepath(); // see below
302 nsDependentCSubstring
Scheme() { return Segment(mScheme
); }
303 nsDependentCSubstring
Userpass(bool includeDelim
= false); // see below
304 nsDependentCSubstring
Username() { return Segment(mUsername
); }
305 nsDependentCSubstring
Password() { return Segment(mPassword
); }
306 nsDependentCSubstring
Hostport(); // see below
307 nsDependentCSubstring
Host(); // see below
308 nsDependentCSubstring
Path() { return Segment(mPath
); }
309 nsDependentCSubstring
Filepath() { return Segment(mFilepath
); }
310 nsDependentCSubstring
Directory() { return Segment(mDirectory
); }
311 nsDependentCSubstring
Filename(); // see below
312 nsDependentCSubstring
Basename() { return Segment(mBasename
); }
313 nsDependentCSubstring
Extension() { return Segment(mExtension
); }
314 nsDependentCSubstring
Query() { return Segment(mQuery
); }
315 nsDependentCSubstring
Ref() { return Segment(mRef
); }
317 // shift the URLSegments to the right by diff
318 void ShiftFromAuthority(int32_t diff
);
319 void ShiftFromUsername(int32_t diff
);
320 void ShiftFromPassword(int32_t diff
);
321 void ShiftFromHost(int32_t diff
);
322 void ShiftFromPath(int32_t diff
);
323 void ShiftFromFilepath(int32_t diff
);
324 void ShiftFromDirectory(int32_t diff
);
325 void ShiftFromBasename(int32_t diff
);
326 void ShiftFromExtension(int32_t diff
);
327 void ShiftFromQuery(int32_t diff
);
328 void ShiftFromRef(int32_t diff
);
330 // fastload helper functions
331 nsresult
ReadSegment(nsIBinaryInputStream
*, URLSegment
&);
332 nsresult
WriteSegment(nsIBinaryOutputStream
*, const URLSegment
&);
334 void FindHostLimit(nsACString::const_iterator
& aStart
,
335 nsACString::const_iterator
& aEnd
);
337 // Asserts that the URL has sane values
340 // Checks if the URL has a valid representation.
343 // mSpec contains the normalized version of the URL spec (UTF-8 encoded).
345 int32_t mDefaultPort
{-1};
348 // url parts (relative to mSpec)
350 URLSegment mAuthority
;
351 URLSegment mUsername
;
352 URLSegment mPassword
;
355 URLSegment mFilepath
;
356 URLSegment mDirectory
;
357 URLSegment mBasename
;
358 URLSegment mExtension
;
362 nsCOMPtr
<nsIURLParser
> mParser
;
364 // mFile is protected so subclasses can access it directly
366 nsCOMPtr
<nsIFile
> mFile
; // cached result for nsIFileURL::GetFile
369 // cached result for nsIURI::GetDisplayHost
370 nsCString mDisplayHost
;
372 enum { eEncoding_Unknown
, eEncoding_ASCII
, eEncoding_UTF8
};
374 uint32_t mURLType
: 2; // nsIStandardURL::URLTYPE_xxx
375 uint32_t mSupportsFileURL
: 1; // QI to nsIFileURL?
376 uint32_t mCheckedIfHostA
: 1; // If set to true, it means either that
377 // mDisplayHost has a been initialized, or
378 // that the hostname is not punycode
381 static StaticRefPtr
<nsIIDNService
> gIDN
;
382 static const char gHostLimitDigits
[];
385 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
386 void PrintSpec() const { printf(" %s\n", mSpec
.get()); }
390 // We make this implementation a template so that we can avoid writing
391 // the same code for SubstitutingURL (which extends nsStandardURL)
393 class TemplatedMutator
: public nsIURIMutator
,
394 public BaseURIMutator
<T
>,
395 public nsIStandardURLMutator
,
396 public nsIURLMutator
,
397 public nsIFileURLMutator
,
398 public nsISerializable
{
399 NS_FORWARD_SAFE_NSIURISETTERS_RET(BaseURIMutator
<T
>::mURI
)
401 [[nodiscard
]] NS_IMETHOD
Deserialize(
402 const mozilla::ipc::URIParams
& aParams
) override
{
403 return BaseURIMutator
<T
>::InitFromIPCParams(aParams
);
407 Write(nsIObjectOutputStream
* aOutputStream
) override
{
408 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
409 return NS_ERROR_NOT_IMPLEMENTED
;
412 [[nodiscard
]] NS_IMETHOD
Read(nsIObjectInputStream
* aStream
) override
{
413 return BaseURIMutator
<T
>::InitFromInputStream(aStream
);
416 [[nodiscard
]] NS_IMETHOD
Finalize(nsIURI
** aURI
) override
{
417 BaseURIMutator
<T
>::mURI
.forget(aURI
);
421 [[nodiscard
]] NS_IMETHOD
SetSpec(const nsACString
& aSpec
,
422 nsIURIMutator
** aMutator
) override
{
424 nsCOMPtr
<nsIURIMutator
> mutator
= this;
425 mutator
.forget(aMutator
);
427 return BaseURIMutator
<T
>::InitFromSpec(aSpec
);
430 [[nodiscard
]] NS_IMETHOD
Init(uint32_t aURLType
, int32_t aDefaultPort
,
431 const nsACString
& aSpec
, const char* aCharset
,
433 nsIURIMutator
** aMutator
) override
{
435 nsCOMPtr
<nsIURIMutator
> mutator
= this;
436 mutator
.forget(aMutator
);
439 if (BaseURIMutator
<T
>::mURI
) {
440 // We don't need a new URI object if we already have one
441 BaseURIMutator
<T
>::mURI
.swap(uri
);
446 uri
->Init(aURLType
, aDefaultPort
, aSpec
, aCharset
, aBaseURI
);
450 BaseURIMutator
<T
>::mURI
= std::move(uri
);
454 [[nodiscard
]] NS_IMETHODIMP
SetDefaultPort(
455 int32_t aNewDefaultPort
, nsIURIMutator
** aMutator
) override
{
456 if (!BaseURIMutator
<T
>::mURI
) {
457 return NS_ERROR_NULL_POINTER
;
460 nsCOMPtr
<nsIURIMutator
> mutator
= this;
461 mutator
.forget(aMutator
);
463 return BaseURIMutator
<T
>::mURI
->SetDefaultPort(aNewDefaultPort
);
466 [[nodiscard
]] NS_IMETHOD
SetFileName(const nsACString
& aFileName
,
467 nsIURIMutator
** aMutator
) override
{
468 if (!BaseURIMutator
<T
>::mURI
) {
469 return NS_ERROR_NULL_POINTER
;
472 nsCOMPtr
<nsIURIMutator
> mutator
= this;
473 mutator
.forget(aMutator
);
475 return BaseURIMutator
<T
>::mURI
->SetFileNameInternal(aFileName
);
478 [[nodiscard
]] NS_IMETHOD
SetFileBaseName(
479 const nsACString
& aFileBaseName
, nsIURIMutator
** aMutator
) override
{
480 if (!BaseURIMutator
<T
>::mURI
) {
481 return NS_ERROR_NULL_POINTER
;
484 nsCOMPtr
<nsIURIMutator
> mutator
= this;
485 mutator
.forget(aMutator
);
487 return BaseURIMutator
<T
>::mURI
->SetFileBaseNameInternal(aFileBaseName
);
490 [[nodiscard
]] NS_IMETHOD
SetFileExtension(
491 const nsACString
& aFileExtension
, nsIURIMutator
** aMutator
) override
{
492 if (!BaseURIMutator
<T
>::mURI
) {
493 return NS_ERROR_NULL_POINTER
;
496 nsCOMPtr
<nsIURIMutator
> mutator
= this;
497 mutator
.forget(aMutator
);
499 return BaseURIMutator
<T
>::mURI
->SetFileExtensionInternal(aFileExtension
);
502 T
* Create() override
{ return new T(mMarkedFileURL
); }
504 [[nodiscard
]] NS_IMETHOD
MarkFileURL() override
{
505 mMarkedFileURL
= true;
509 [[nodiscard
]] NS_IMETHOD
SetFile(nsIFile
* aFile
) override
{
511 if (BaseURIMutator
<T
>::mURI
) {
512 // We don't need a new URI object if we already have one
513 BaseURIMutator
<T
>::mURI
.swap(uri
);
515 uri
= new T(/* aSupportsFileURL = */ true);
518 nsresult rv
= uri
->SetFile(aFile
);
522 BaseURIMutator
<T
>::mURI
.swap(uri
);
526 explicit TemplatedMutator() = default;
529 virtual ~TemplatedMutator() = default;
531 bool mMarkedFileURL
= false;
536 class Mutator final
: public TemplatedMutator
<nsStandardURL
> {
539 explicit Mutator() = default;
542 virtual ~Mutator() = default;
545 friend BaseURIMutator
<nsStandardURL
>;
548 #define NS_THIS_STANDARDURL_IMPL_CID \
549 { /* b8e3e97b-1ccd-4b45-af5a-79596770f5d7 */ \
550 0xb8e3e97b, 0x1ccd, 0x4b45, { \
551 0xaf, 0x5a, 0x79, 0x59, 0x67, 0x70, 0xf5, 0xd7 \
555 //-----------------------------------------------------------------------------
556 // Dependent substring getters
557 //-----------------------------------------------------------------------------
559 inline nsDependentCSubstring
nsStandardURL::Segment(uint32_t pos
, int32_t len
) {
564 return Substring(mSpec
, pos
, uint32_t(len
));
567 inline nsDependentCSubstring
nsStandardURL::Prepath() {
569 if (mAuthority
.mLen
>= 0) len
= mAuthority
.mPos
+ mAuthority
.mLen
;
570 return Substring(mSpec
, 0, len
);
573 inline nsDependentCSubstring
nsStandardURL::Userpass(bool includeDelim
) {
574 uint32_t pos
= 0, len
= 0;
575 if (mUsername
.mLen
> 0 || mPassword
.mLen
> 0) {
576 if (mUsername
.mLen
> 0) {
577 pos
= mUsername
.mPos
;
578 len
= mUsername
.mLen
;
579 if (mPassword
.mLen
>= 0) {
580 len
+= (mPassword
.mLen
+ 1);
583 pos
= mPassword
.mPos
- 1;
584 len
= mPassword
.mLen
+ 1;
587 if (includeDelim
) len
++;
589 return Substring(mSpec
, pos
, len
);
592 inline nsDependentCSubstring
nsStandardURL::Hostport() {
593 uint32_t pos
= 0, len
= 0;
594 if (mAuthority
.mLen
> 0) {
596 len
= mAuthority
.mPos
+ mAuthority
.mLen
- pos
;
598 return Substring(mSpec
, pos
, len
);
601 inline nsDependentCSubstring
nsStandardURL::Host() {
602 uint32_t pos
= 0, len
= 0;
603 if (mHost
.mLen
> 0) {
606 if (mSpec
.CharAt(pos
) == '[' && mSpec
.CharAt(pos
+ len
- 1) == ']') {
611 return Substring(mSpec
, pos
, len
);
614 inline nsDependentCSubstring
nsStandardURL::Filename() {
615 uint32_t pos
= 0, len
= 0;
616 // if there is no basename, then there can be no extension
617 if (mBasename
.mLen
> 0) {
618 pos
= mBasename
.mPos
;
619 len
= mBasename
.mLen
;
620 if (mExtension
.mLen
>= 0) len
+= (mExtension
.mLen
+ 1);
622 return Substring(mSpec
, pos
, len
);
626 } // namespace mozilla
628 #endif // nsStandardURL_h__