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
);
202 // enum used in a few places to specify how .ref attribute should be handled
203 enum RefHandlingEnum
{ eIgnoreRef
, eHonorRef
, eReplaceRef
};
205 // Helper to share code between Equals and EqualsExceptRef
206 // NOTE: *not* virtual, because no one needs to override this so far...
207 nsresult
EqualsInternal(nsIURI
* unknownOther
, RefHandlingEnum refHandlingMode
,
210 virtual nsStandardURL
* StartClone();
212 // Helper to share code between Clone methods.
213 nsresult
CloneInternal(RefHandlingEnum aRefHandlingMode
,
214 const nsACString
& aNewRef
, nsIURI
** aClone
);
215 // Helper method that copies member variables from the source StandardURL
216 // if copyCached = true, it will also copy mFile and mDisplayHost
217 nsresult
CopyMembers(nsStandardURL
* source
, RefHandlingEnum mode
,
218 const nsACString
& newRef
, bool copyCached
= false);
220 // Helper for subclass implementation of GetFile(). Subclasses that map
221 // URIs to files in a special way should implement this method. It should
222 // ensure that our mFile is initialized, if it's possible.
223 // returns NS_ERROR_NO_INTERFACE if the url does not map to a file
224 virtual nsresult
EnsureFile();
226 virtual nsresult
Clone(nsIURI
** aURI
);
227 virtual nsresult
SetSpecInternal(const nsACString
& input
);
228 virtual nsresult
SetScheme(const nsACString
& input
);
229 virtual nsresult
SetUserPass(const nsACString
& input
);
230 virtual nsresult
SetUsername(const nsACString
& input
);
231 virtual nsresult
SetPassword(const nsACString
& input
);
232 virtual nsresult
SetHostPort(const nsACString
& aValue
);
233 virtual nsresult
SetHost(const nsACString
& input
);
234 virtual nsresult
SetPort(int32_t port
);
235 virtual nsresult
SetPathQueryRef(const nsACString
& input
);
236 virtual nsresult
SetRef(const nsACString
& input
);
237 virtual nsresult
SetFilePath(const nsACString
& input
);
238 virtual nsresult
SetQuery(const nsACString
& input
);
239 virtual nsresult
SetQueryWithEncoding(const nsACString
& input
,
240 const Encoding
* encoding
);
241 bool Deserialize(const mozilla::ipc::URIParams
&);
242 nsresult
ReadPrivate(nsIObjectInputStream
* stream
);
245 nsresult
Init(uint32_t urlType
, int32_t defaultPort
, const nsACString
& spec
,
246 const char* charset
, nsIURI
* baseURI
);
247 nsresult
SetDefaultPort(int32_t aNewDefaultPort
);
248 nsresult
SetFile(nsIFile
* file
);
250 nsresult
SetFileNameInternal(const nsACString
& input
);
251 nsresult
SetFileBaseNameInternal(const nsACString
& input
);
252 nsresult
SetFileExtensionInternal(const nsACString
& input
);
254 int32_t Port() { return mPort
== -1 ? mDefaultPort
: mPort
; }
256 void ReplacePortInSpec(int32_t aNewPort
);
258 void InvalidateCache(bool invalidateCachedFile
= true);
260 bool ValidIPv6orHostname(const char* host
, uint32_t length
);
261 static bool IsValidOfBase(unsigned char c
, const uint32_t base
);
262 nsresult
NormalizeIDN(const nsCString
& host
, nsCString
& result
);
263 nsresult
CheckIfHostIsAscii();
264 void CoalescePath(netCoalesceFlags coalesceFlag
, char* path
);
266 uint32_t AppendSegmentToBuf(char*, uint32_t, const char*,
267 const URLSegment
& input
, URLSegment
& output
,
268 const nsCString
* esc
= nullptr,
269 bool useEsc
= false, int32_t* diff
= nullptr);
270 uint32_t AppendToBuf(char*, uint32_t, const char*, uint32_t);
272 nsresult
BuildNormalizedSpec(const char* spec
, const Encoding
* encoding
);
273 nsresult
SetSpecWithEncoding(const nsACString
& input
,
274 const Encoding
* encoding
);
276 bool SegmentIs(const URLSegment
& seg
, const char* val
,
277 bool ignoreCase
= false);
278 bool SegmentIs(const char* spec
, const URLSegment
& seg
, const char* val
,
279 bool ignoreCase
= false);
280 bool SegmentIs(const URLSegment
& seg1
, const char* val
,
281 const URLSegment
& seg2
, bool ignoreCase
= false);
283 int32_t ReplaceSegment(uint32_t pos
, uint32_t len
, const char* val
,
285 int32_t ReplaceSegment(uint32_t pos
, uint32_t len
, const nsACString
& val
);
287 nsresult
ParseURL(const char* spec
, int32_t specLen
);
288 nsresult
ParsePath(const char* spec
, uint32_t pathPos
, int32_t pathLen
= -1);
290 char* AppendToSubstring(uint32_t pos
, int32_t len
, const char* tail
);
292 // dependent substring helpers
293 nsDependentCSubstring
Segment(uint32_t pos
, int32_t len
); // see below
294 nsDependentCSubstring
Segment(const URLSegment
& s
) {
295 return Segment(s
.mPos
, s
.mLen
);
298 // dependent substring getters
299 nsDependentCSubstring
Prepath(); // see below
300 nsDependentCSubstring
Scheme() { return Segment(mScheme
); }
301 nsDependentCSubstring
Userpass(bool includeDelim
= false); // see below
302 nsDependentCSubstring
Username() { return Segment(mUsername
); }
303 nsDependentCSubstring
Password() { return Segment(mPassword
); }
304 nsDependentCSubstring
Hostport(); // see below
305 nsDependentCSubstring
Host(); // see below
306 nsDependentCSubstring
Path() { return Segment(mPath
); }
307 nsDependentCSubstring
Filepath() { return Segment(mFilepath
); }
308 nsDependentCSubstring
Directory() { return Segment(mDirectory
); }
309 nsDependentCSubstring
Filename(); // see below
310 nsDependentCSubstring
Basename() { return Segment(mBasename
); }
311 nsDependentCSubstring
Extension() { return Segment(mExtension
); }
312 nsDependentCSubstring
Query() { return Segment(mQuery
); }
313 nsDependentCSubstring
Ref() { return Segment(mRef
); }
315 // shift the URLSegments to the right by diff
316 void ShiftFromAuthority(int32_t diff
);
317 void ShiftFromUsername(int32_t diff
);
318 void ShiftFromPassword(int32_t diff
);
319 void ShiftFromHost(int32_t diff
);
320 void ShiftFromPath(int32_t diff
);
321 void ShiftFromFilepath(int32_t diff
);
322 void ShiftFromDirectory(int32_t diff
);
323 void ShiftFromBasename(int32_t diff
);
324 void ShiftFromExtension(int32_t diff
);
325 void ShiftFromQuery(int32_t diff
);
326 void ShiftFromRef(int32_t diff
);
328 // fastload helper functions
329 nsresult
ReadSegment(nsIBinaryInputStream
*, URLSegment
&);
330 nsresult
WriteSegment(nsIBinaryOutputStream
*, const URLSegment
&);
332 void FindHostLimit(nsACString::const_iterator
& aStart
,
333 nsACString::const_iterator
& aEnd
);
335 // Asserts that the URL has sane values
338 // Checks if the URL has a valid representation.
341 // mSpec contains the normalized version of the URL spec (UTF-8 encoded).
343 int32_t mDefaultPort
{-1};
346 // url parts (relative to mSpec)
348 URLSegment mAuthority
;
349 URLSegment mUsername
;
350 URLSegment mPassword
;
353 URLSegment mFilepath
;
354 URLSegment mDirectory
;
355 URLSegment mBasename
;
356 URLSegment mExtension
;
360 nsCOMPtr
<nsIURLParser
> mParser
;
362 // mFile is protected so subclasses can access it directly
364 nsCOMPtr
<nsIFile
> mFile
; // cached result for nsIFileURL::GetFile
367 // cached result for nsIURI::GetDisplayHost
368 nsCString mDisplayHost
;
370 enum { eEncoding_Unknown
, eEncoding_ASCII
, eEncoding_UTF8
};
372 uint32_t mURLType
: 2; // nsIStandardURL::URLTYPE_xxx
373 uint32_t mSupportsFileURL
: 1; // QI to nsIFileURL?
374 uint32_t mCheckedIfHostA
: 1; // If set to true, it means either that
375 // mDisplayHost has a been initialized, or
376 // that the hostname is not punycode
379 static StaticRefPtr
<nsIIDNService
> gIDN
;
380 static const char gHostLimitDigits
[];
383 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
384 void PrintSpec() const { printf(" %s\n", mSpec
.get()); }
388 // We make this implementation a template so that we can avoid writing
389 // the same code for SubstitutingURL (which extends nsStandardURL)
391 class TemplatedMutator
: public nsIURIMutator
,
392 public BaseURIMutator
<T
>,
393 public nsIStandardURLMutator
,
394 public nsIURLMutator
,
395 public nsIFileURLMutator
,
396 public nsISerializable
{
397 NS_FORWARD_SAFE_NSIURISETTERS_RET(BaseURIMutator
<T
>::mURI
)
399 [[nodiscard
]] NS_IMETHOD
Deserialize(
400 const mozilla::ipc::URIParams
& aParams
) override
{
401 return BaseURIMutator
<T
>::InitFromIPCParams(aParams
);
405 Write(nsIObjectOutputStream
* aOutputStream
) override
{
406 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
407 return NS_ERROR_NOT_IMPLEMENTED
;
410 [[nodiscard
]] NS_IMETHOD
Read(nsIObjectInputStream
* aStream
) override
{
411 return BaseURIMutator
<T
>::InitFromInputStream(aStream
);
414 [[nodiscard
]] NS_IMETHOD
Finalize(nsIURI
** aURI
) override
{
415 BaseURIMutator
<T
>::mURI
.forget(aURI
);
419 [[nodiscard
]] NS_IMETHOD
SetSpec(const nsACString
& aSpec
,
420 nsIURIMutator
** aMutator
) override
{
422 nsCOMPtr
<nsIURIMutator
> mutator
= this;
423 mutator
.forget(aMutator
);
425 return BaseURIMutator
<T
>::InitFromSpec(aSpec
);
428 [[nodiscard
]] NS_IMETHOD
Init(uint32_t aURLType
, int32_t aDefaultPort
,
429 const nsACString
& aSpec
, const char* aCharset
,
431 nsIURIMutator
** aMutator
) override
{
433 nsCOMPtr
<nsIURIMutator
> mutator
= this;
434 mutator
.forget(aMutator
);
437 if (BaseURIMutator
<T
>::mURI
) {
438 // We don't need a new URI object if we already have one
439 BaseURIMutator
<T
>::mURI
.swap(uri
);
444 uri
->Init(aURLType
, aDefaultPort
, aSpec
, aCharset
, aBaseURI
);
448 BaseURIMutator
<T
>::mURI
= std::move(uri
);
452 [[nodiscard
]] NS_IMETHODIMP
SetDefaultPort(
453 int32_t aNewDefaultPort
, nsIURIMutator
** aMutator
) override
{
454 if (!BaseURIMutator
<T
>::mURI
) {
455 return NS_ERROR_NULL_POINTER
;
458 nsCOMPtr
<nsIURIMutator
> mutator
= this;
459 mutator
.forget(aMutator
);
461 return BaseURIMutator
<T
>::mURI
->SetDefaultPort(aNewDefaultPort
);
464 [[nodiscard
]] NS_IMETHOD
SetFileName(const nsACString
& aFileName
,
465 nsIURIMutator
** aMutator
) override
{
466 if (!BaseURIMutator
<T
>::mURI
) {
467 return NS_ERROR_NULL_POINTER
;
470 nsCOMPtr
<nsIURIMutator
> mutator
= this;
471 mutator
.forget(aMutator
);
473 return BaseURIMutator
<T
>::mURI
->SetFileNameInternal(aFileName
);
476 [[nodiscard
]] NS_IMETHOD
SetFileBaseName(
477 const nsACString
& aFileBaseName
, nsIURIMutator
** aMutator
) override
{
478 if (!BaseURIMutator
<T
>::mURI
) {
479 return NS_ERROR_NULL_POINTER
;
482 nsCOMPtr
<nsIURIMutator
> mutator
= this;
483 mutator
.forget(aMutator
);
485 return BaseURIMutator
<T
>::mURI
->SetFileBaseNameInternal(aFileBaseName
);
488 [[nodiscard
]] NS_IMETHOD
SetFileExtension(
489 const nsACString
& aFileExtension
, nsIURIMutator
** aMutator
) override
{
490 if (!BaseURIMutator
<T
>::mURI
) {
491 return NS_ERROR_NULL_POINTER
;
494 nsCOMPtr
<nsIURIMutator
> mutator
= this;
495 mutator
.forget(aMutator
);
497 return BaseURIMutator
<T
>::mURI
->SetFileExtensionInternal(aFileExtension
);
500 T
* Create() override
{ return new T(mMarkedFileURL
); }
502 [[nodiscard
]] NS_IMETHOD
MarkFileURL() override
{
503 mMarkedFileURL
= true;
507 [[nodiscard
]] NS_IMETHOD
SetFile(nsIFile
* aFile
) override
{
509 if (BaseURIMutator
<T
>::mURI
) {
510 // We don't need a new URI object if we already have one
511 BaseURIMutator
<T
>::mURI
.swap(uri
);
513 uri
= new T(/* aSupportsFileURL = */ true);
516 nsresult rv
= uri
->SetFile(aFile
);
520 BaseURIMutator
<T
>::mURI
.swap(uri
);
524 explicit TemplatedMutator() = default;
527 virtual ~TemplatedMutator() = default;
529 bool mMarkedFileURL
= false;
534 class Mutator final
: public TemplatedMutator
<nsStandardURL
> {
537 explicit Mutator() = default;
540 virtual ~Mutator() = default;
543 friend BaseURIMutator
<nsStandardURL
>;
546 #define NS_THIS_STANDARDURL_IMPL_CID \
547 { /* b8e3e97b-1ccd-4b45-af5a-79596770f5d7 */ \
548 0xb8e3e97b, 0x1ccd, 0x4b45, { \
549 0xaf, 0x5a, 0x79, 0x59, 0x67, 0x70, 0xf5, 0xd7 \
553 //-----------------------------------------------------------------------------
554 // Dependent substring getters
555 //-----------------------------------------------------------------------------
557 inline nsDependentCSubstring
nsStandardURL::Segment(uint32_t pos
, int32_t len
) {
562 return Substring(mSpec
, pos
, uint32_t(len
));
565 inline nsDependentCSubstring
nsStandardURL::Prepath() {
567 if (mAuthority
.mLen
>= 0) len
= mAuthority
.mPos
+ mAuthority
.mLen
;
568 return Substring(mSpec
, 0, len
);
571 inline nsDependentCSubstring
nsStandardURL::Userpass(bool includeDelim
) {
572 uint32_t pos
= 0, len
= 0;
573 if (mUsername
.mLen
> 0 || mPassword
.mLen
> 0) {
574 if (mUsername
.mLen
> 0) {
575 pos
= mUsername
.mPos
;
576 len
= mUsername
.mLen
;
577 if (mPassword
.mLen
>= 0) {
578 len
+= (mPassword
.mLen
+ 1);
581 pos
= mPassword
.mPos
- 1;
582 len
= mPassword
.mLen
+ 1;
585 if (includeDelim
) len
++;
587 return Substring(mSpec
, pos
, len
);
590 inline nsDependentCSubstring
nsStandardURL::Hostport() {
591 uint32_t pos
= 0, len
= 0;
592 if (mAuthority
.mLen
> 0) {
594 len
= mAuthority
.mPos
+ mAuthority
.mLen
- pos
;
596 return Substring(mSpec
, pos
, len
);
599 inline nsDependentCSubstring
nsStandardURL::Host() {
600 uint32_t pos
= 0, len
= 0;
601 if (mHost
.mLen
> 0) {
604 if (mSpec
.CharAt(pos
) == '[' && mSpec
.CharAt(pos
+ len
- 1) == ']') {
609 return Substring(mSpec
, pos
, len
);
612 inline nsDependentCSubstring
nsStandardURL::Filename() {
613 uint32_t pos
= 0, len
= 0;
614 // if there is no basename, then there can be no extension
615 if (mBasename
.mLen
> 0) {
616 pos
= mBasename
.mPos
;
617 len
= mBasename
.mLen
;
618 if (mExtension
.mLen
>= 0) len
+= (mExtension
.mLen
+ 1);
620 return Substring(mSpec
, pos
, len
);
624 } // namespace mozilla
626 #endif // nsStandardURL_h__