nsStandardURL.h (20629B)
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/. */ 5 6 #ifndef nsStandardURL_h__ 7 #define nsStandardURL_h__ 8 9 #include <bitset> 10 11 #include "nsString.h" 12 #include "nsISerializable.h" 13 #include "nsIFileURL.h" 14 #include "nsIStandardURL.h" 15 #include "mozilla/Encoding.h" 16 #include "nsCOMPtr.h" 17 #include "nsURLHelper.h" 18 #include "mozilla/Atomics.h" 19 #include "mozilla/LinkedList.h" 20 #include "nsISensitiveInfoHiddenURI.h" 21 #include "nsIURIMutator.h" 22 23 #ifdef NS_BUILD_REFCNT_LOGGING 24 # define DEBUG_DUMP_URLS_AT_SHUTDOWN 25 #endif 26 27 class nsIBinaryInputStream; 28 class nsIBinaryOutputStream; 29 class nsIIDNService; 30 class nsIPrefBranch; 31 class nsIFile; 32 class nsIURLParser; 33 34 namespace mozilla { 35 class Encoding; 36 namespace net { 37 38 template <typename T> 39 class URLSegmentNumber { 40 T mData{0}; 41 bool mParity{false}; 42 43 public: 44 URLSegmentNumber() = default; 45 explicit URLSegmentNumber(T data) : mData(data) { 46 mParity = CalculateParity(); 47 } 48 bool operator==(URLSegmentNumber value) const { return mData == value.mData; } 49 bool operator!=(URLSegmentNumber value) const { return mData != value.mData; } 50 bool operator>(URLSegmentNumber value) const { return mData > value.mData; } 51 URLSegmentNumber operator+(int32_t value) const { 52 return URLSegmentNumber(mData + value); 53 } 54 URLSegmentNumber operator+(uint32_t value) const { 55 return URLSegmentNumber(mData + value); 56 } 57 URLSegmentNumber operator-(int32_t value) const { 58 return URLSegmentNumber(mData - value); 59 } 60 URLSegmentNumber operator-(uint32_t value) const { 61 return URLSegmentNumber(mData - value); 62 } 63 URLSegmentNumber operator+=(URLSegmentNumber value) { 64 mData += value.mData; 65 mParity = CalculateParity(); 66 return *this; 67 } 68 URLSegmentNumber operator+=(T value) { 69 mData += value; 70 mParity = CalculateParity(); 71 return *this; 72 } 73 URLSegmentNumber operator-=(URLSegmentNumber value) { 74 mData -= value.mData; 75 mParity = CalculateParity(); 76 return *this; 77 } 78 URLSegmentNumber operator-=(T value) { 79 mData -= value; 80 mParity = CalculateParity(); 81 return *this; 82 } 83 operator T() const { return mData; } 84 URLSegmentNumber& operator=(T value) { 85 mData = value; 86 mParity = CalculateParity(); 87 return *this; 88 } 89 URLSegmentNumber& operator++() { 90 ++mData; 91 mParity = CalculateParity(); 92 return *this; 93 } 94 URLSegmentNumber operator++(int) { 95 URLSegmentNumber value = *this; 96 *this += 1; 97 return value; 98 } 99 bool CalculateParity() const { 100 std::bitset<32> bits((uint32_t)mData); 101 return bits.count() % 2 == 0 ? false : true; 102 } 103 bool Parity() const { return mParity; } 104 }; 105 106 //----------------------------------------------------------------------------- 107 // standard URL implementation 108 //----------------------------------------------------------------------------- 109 110 class nsStandardURL : public nsIFileURL, 111 public nsIStandardURL, 112 public nsISerializable, 113 public nsISensitiveInfoHiddenURI 114 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN 115 , 116 public LinkedListElement<nsStandardURL> 117 #endif 118 { 119 protected: 120 virtual ~nsStandardURL(); 121 explicit nsStandardURL(bool aSupportsFileURL = false, bool aTrackURL = true); 122 123 public: 124 NS_DECL_THREADSAFE_ISUPPORTS 125 NS_DECL_NSIURI 126 NS_DECL_NSIURL 127 NS_DECL_NSIFILEURL 128 NS_DECL_NSISTANDARDURL 129 NS_DECL_NSISERIALIZABLE 130 NS_DECL_NSISENSITIVEINFOHIDDENURI 131 132 static void InitGlobalObjects(); 133 static void ShutdownGlobalObjects(); 134 135 // 136 // location and length of an url segment relative to mSpec 137 // 138 struct URLSegment { 139 #ifdef EARLY_BETA_OR_EARLIER 140 URLSegmentNumber<uint32_t> mPos{0}; 141 URLSegmentNumber<int32_t> mLen{-1}; 142 #else 143 uint32_t mPos{0}; 144 int32_t mLen{-1}; 145 #endif 146 147 URLSegment() = default; 148 URLSegment(uint32_t pos, int32_t len) : mPos(pos), mLen(len) {} 149 URLSegment(const URLSegment& aCopy) = default; 150 void Reset() { 151 mPos = 0; 152 mLen = -1; 153 } 154 // Merge another segment following this one to it if they're contiguous 155 // Assumes we have something like "foo;bar" where this object is 'foo' and 156 // right is 'bar'. 157 void Merge(const nsCString& spec, const char separator, 158 const URLSegment& right) { 159 if (mLen >= 0 && *(spec.get() + mPos + mLen) == separator && 160 mPos + mLen + 1 == right.mPos) { 161 mLen += 1 + right.mLen; 162 } 163 } 164 }; 165 166 public: 167 // 168 // URL segment encoder : performs charset conversion and URL escaping. 169 // 170 class nsSegmentEncoder { 171 public: 172 explicit nsSegmentEncoder(const Encoding* encoding = nullptr); 173 174 // Encode the given segment if necessary, and return the length of 175 // the encoded segment. The encoded segment is appended to |aOut| 176 // if and only if encoding is required. 177 int32_t EncodeSegmentCount(const char* str, const URLSegment& aSeg, 178 int16_t mask, nsCString& aOut, bool& appended, 179 uint32_t extraLen = 0); 180 181 // Encode the given string if necessary, and return a reference to 182 // the encoded string. Returns a reference to |result| if encoding 183 // is required. Otherwise, a reference to |str| is returned. 184 const nsACString& EncodeSegment(const nsACString& str, int16_t mask, 185 nsCString& result); 186 187 private: 188 const Encoding* mEncoding; 189 }; 190 friend class nsSegmentEncoder; 191 192 static nsIIDNService* GetIDNService(); 193 194 protected: 195 // enum used in a few places to specify how .ref attribute should be handled 196 enum RefHandlingEnum { eIgnoreRef, eHonorRef, eReplaceRef }; 197 198 // Helper to share code between Equals and EqualsExceptRef 199 // NOTE: *not* virtual, because no one needs to override this so far... 200 nsresult EqualsInternal(nsIURI* unknownOther, RefHandlingEnum refHandlingMode, 201 bool* result); 202 203 virtual nsStandardURL* StartClone(); 204 205 // Helper to share code between Clone methods. 206 nsresult CloneInternal(RefHandlingEnum aRefHandlingMode, 207 const nsACString& aNewRef, nsIURI** aClone); 208 // Helper method that copies member variables from the source StandardURL 209 // if copyCached = true, it will also copy mFile and mDisplayHost 210 nsresult CopyMembers(nsStandardURL* source, RefHandlingEnum mode, 211 const nsACString& newRef, bool copyCached = false); 212 213 // Helper for subclass implementation of GetFile(). Subclasses that map 214 // URIs to files in a special way should implement this method. It should 215 // ensure that our mFile is initialized, if it's possible. 216 // returns NS_ERROR_NO_INTERFACE if the url does not map to a file 217 virtual nsresult EnsureFile(); 218 219 virtual nsresult Clone(nsIURI** aURI); 220 virtual nsresult SetSpecInternal(const nsACString& input); 221 virtual nsresult SetScheme(const nsACString& input); 222 virtual nsresult SetUserPass(const nsACString& input); 223 virtual nsresult SetUsername(const nsACString& input); 224 virtual nsresult SetPassword(const nsACString& input); 225 virtual nsresult SetHostPort(const nsACString& aValue); 226 virtual nsresult SetHost(const nsACString& input); 227 virtual nsresult SetPort(int32_t port); 228 virtual nsresult SetPathQueryRef(const nsACString& input); 229 virtual nsresult SetRef(const nsACString& input); 230 virtual nsresult SetFilePath(const nsACString& input); 231 virtual nsresult SetQuery(const nsACString& input); 232 virtual nsresult SetQueryWithEncoding(const nsACString& input, 233 const Encoding* encoding); 234 bool Deserialize(const mozilla::ipc::URIParams&); 235 nsresult ReadPrivate(nsIObjectInputStream* stream); 236 237 private: 238 nsresult Init(uint32_t urlType, int32_t defaultPort, const nsACString& spec, 239 const char* charset, nsIURI* baseURI); 240 nsresult SetDefaultPort(int32_t aNewDefaultPort); 241 nsresult SetFile(nsIFile* file); 242 243 nsresult SetFileNameInternal(const nsACString& input); 244 nsresult SetFileBaseNameInternal(const nsACString& input); 245 nsresult SetFileExtensionInternal(const nsACString& input); 246 247 int32_t Port() { return mPort == -1 ? mDefaultPort : mPort; } 248 249 void ReplacePortInSpec(int32_t aNewPort); 250 void Clear(); 251 void InvalidateCache(bool invalidateCachedFile = true); 252 253 static bool IsValidOfBase(unsigned char c, const uint32_t base); 254 nsresult NormalizeIDN(const nsACString& aHost, nsACString& aResult); 255 nsresult CheckIfHostIsAscii(); 256 void CoalescePath(char* path); 257 258 uint32_t AppendSegmentToBuf(char*, uint32_t, const char*, 259 const URLSegment& input, URLSegment& output, 260 const nsCString* esc = nullptr, 261 bool useEsc = false, int32_t* diff = nullptr); 262 uint32_t AppendToBuf(char*, uint32_t, const char*, uint32_t); 263 264 nsresult BuildNormalizedSpec(const char* spec, const Encoding* encoding); 265 nsresult SetSpecWithEncoding(const nsACString& input, 266 const Encoding* encoding); 267 268 bool SegmentIs(const URLSegment& seg, const char* val, 269 bool ignoreCase = false); 270 bool SegmentIs(const char* spec, const URLSegment& seg, const char* val, 271 bool ignoreCase = false); 272 bool SegmentIs(const URLSegment& seg1, const char* val, 273 const URLSegment& seg2, bool ignoreCase = false); 274 275 int32_t ReplaceSegment(uint32_t pos, uint32_t len, const char* val, 276 uint32_t valLen); 277 int32_t ReplaceSegment(uint32_t pos, uint32_t len, const nsACString& val); 278 279 nsresult ParseURL(const char* spec, int32_t specLen); 280 nsresult ParsePath(const char* spec, uint32_t pathPos, int32_t pathLen = -1); 281 282 char* AppendToSubstring(uint32_t pos, int32_t len, const char* tail); 283 284 // dependent substring helpers 285 nsDependentCSubstring Segment(uint32_t pos, int32_t len); // see below 286 nsDependentCSubstring Segment(const URLSegment& s) { 287 return Segment(s.mPos, s.mLen); 288 } 289 290 // dependent substring getters 291 nsDependentCSubstring Prepath(); // see below 292 nsDependentCSubstring Scheme() { return Segment(mScheme); } 293 nsDependentCSubstring Userpass(bool includeDelim = false); // see below 294 nsDependentCSubstring Username() { return Segment(mUsername); } 295 nsDependentCSubstring Password() { return Segment(mPassword); } 296 nsDependentCSubstring Hostport(); // see below 297 nsDependentCSubstring Host(); // see below 298 nsDependentCSubstring Path() { return Segment(mPath); } 299 nsDependentCSubstring Filepath() { return Segment(mFilepath); } 300 nsDependentCSubstring Directory() { return Segment(mDirectory); } 301 nsDependentCSubstring Filename(); // see below 302 nsDependentCSubstring Basename() { return Segment(mBasename); } 303 nsDependentCSubstring Extension() { return Segment(mExtension); } 304 nsDependentCSubstring Query() { return Segment(mQuery); } 305 nsDependentCSubstring Ref() { return Segment(mRef); } 306 307 // shift the URLSegments to the right by diff 308 void ShiftFromAuthority(int32_t diff); 309 void ShiftFromUsername(int32_t diff); 310 void ShiftFromPassword(int32_t diff); 311 void ShiftFromHost(int32_t diff); 312 void ShiftFromPath(int32_t diff); 313 void ShiftFromFilepath(int32_t diff); 314 void ShiftFromDirectory(int32_t diff); 315 void ShiftFromBasename(int32_t diff); 316 void ShiftFromExtension(int32_t diff); 317 void ShiftFromQuery(int32_t diff); 318 void ShiftFromRef(int32_t diff); 319 320 // fastload helper functions 321 nsresult ReadSegment(nsIBinaryInputStream*, URLSegment&); 322 nsresult WriteSegment(nsIBinaryOutputStream*, const URLSegment&); 323 324 void FindHostLimit(nsACString::const_iterator& aStart, 325 nsACString::const_iterator& aEnd); 326 327 // Asserts that the URL has sane values 328 void SanityCheck(); 329 330 // Checks if the URL has a valid representation. 331 bool IsValid(); 332 333 // This value will only be updated on the main thread once. 334 static Atomic<bool, Relaxed> gInitialized; 335 336 // mSpec contains the normalized version of the URL spec (UTF-8 encoded). 337 nsCString mSpec; 338 int32_t mDefaultPort{-1}; 339 int32_t mPort{-1}; 340 341 // url parts (relative to mSpec) 342 URLSegment mScheme; 343 URLSegment mAuthority; 344 URLSegment mUsername; 345 URLSegment mPassword; 346 URLSegment mHost; 347 URLSegment mPath; 348 URLSegment mFilepath; 349 URLSegment mDirectory; 350 URLSegment mBasename; 351 URLSegment mExtension; 352 URLSegment mQuery; 353 URLSegment mRef; 354 355 nsCOMPtr<nsIURLParser> mParser; 356 357 // mFile is protected so subclasses can access it directly 358 protected: 359 nsCOMPtr<nsIFile> mFile; // cached result for nsIFileURL::GetFile 360 361 private: 362 // cached result for nsIURI::GetDisplayHost 363 nsCString mDisplayHost; 364 365 enum { eEncoding_Unknown, eEncoding_ASCII, eEncoding_UTF8 }; 366 367 uint32_t mURLType : 2; // nsIStandardURL::URLTYPE_xxx 368 uint32_t mSupportsFileURL : 1; // QI to nsIFileURL? 369 uint32_t mCheckedIfHostA : 1; // If set to true, it means either that 370 // mDisplayHost has a been initialized, or 371 // that the hostname is not punycode 372 373 // global objects. 374 static StaticRefPtr<nsIIDNService> gIDN; 375 static const char gHostLimitDigits[]; 376 377 public: 378 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN 379 void PrintSpec() const { printf(" %s\n", mSpec.get()); } 380 #endif 381 382 public: 383 // We make this implementation a template so that we can avoid writing 384 // the same code for SubstitutingURL (which extends nsStandardURL) 385 template <class T> 386 class TemplatedMutator : public nsIURIMutator, 387 public BaseURIMutator<T>, 388 public nsIStandardURLMutator, 389 public nsIURLMutator, 390 public nsIFileURLMutator, 391 public nsISerializable { 392 NS_FORWARD_SAFE_NSIURISETTERS_RET(BaseURIMutator<T>::mURI) 393 394 [[nodiscard]] NS_IMETHOD Deserialize( 395 const mozilla::ipc::URIParams& aParams) override { 396 return BaseURIMutator<T>::InitFromIPCParams(aParams); 397 } 398 399 NS_IMETHOD 400 Write(nsIObjectOutputStream* aOutputStream) override { 401 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); 402 return NS_ERROR_NOT_IMPLEMENTED; 403 } 404 405 [[nodiscard]] NS_IMETHOD Read(nsIObjectInputStream* aStream) override { 406 return BaseURIMutator<T>::InitFromInputStream(aStream); 407 } 408 409 [[nodiscard]] NS_IMETHOD Finalize(nsIURI** aURI) override { 410 BaseURIMutator<T>::mURI.forget(aURI); 411 return NS_OK; 412 } 413 414 [[nodiscard]] NS_IMETHOD SetSpec(const nsACString& aSpec, 415 nsIURIMutator** aMutator) override { 416 if (aMutator) { 417 nsCOMPtr<nsIURIMutator> mutator = this; 418 mutator.forget(aMutator); 419 } 420 return BaseURIMutator<T>::InitFromSpec(aSpec); 421 } 422 423 [[nodiscard]] NS_IMETHOD Init(uint32_t aURLType, int32_t aDefaultPort, 424 const nsACString& aSpec, const char* aCharset, 425 nsIURI* aBaseURI, 426 nsIURIMutator** aMutator) override { 427 if (aMutator) { 428 nsCOMPtr<nsIURIMutator> mutator = this; 429 mutator.forget(aMutator); 430 } 431 RefPtr<T> uri; 432 if (BaseURIMutator<T>::mURI) { 433 // We don't need a new URI object if we already have one 434 BaseURIMutator<T>::mURI.swap(uri); 435 } else { 436 uri = Create(); 437 } 438 nsresult rv = 439 uri->Init(aURLType, aDefaultPort, aSpec, aCharset, aBaseURI); 440 if (NS_FAILED(rv)) { 441 return rv; 442 } 443 BaseURIMutator<T>::mURI = std::move(uri); 444 return NS_OK; 445 } 446 447 [[nodiscard]] NS_IMETHODIMP SetDefaultPort( 448 int32_t aNewDefaultPort, nsIURIMutator** aMutator) override { 449 if (!BaseURIMutator<T>::mURI) { 450 return NS_ERROR_NULL_POINTER; 451 } 452 if (aMutator) { 453 nsCOMPtr<nsIURIMutator> mutator = this; 454 mutator.forget(aMutator); 455 } 456 return BaseURIMutator<T>::mURI->SetDefaultPort(aNewDefaultPort); 457 } 458 459 [[nodiscard]] NS_IMETHOD SetFileName(const nsACString& aFileName, 460 nsIURIMutator** aMutator) override { 461 if (!BaseURIMutator<T>::mURI) { 462 return NS_ERROR_NULL_POINTER; 463 } 464 if (aMutator) { 465 nsCOMPtr<nsIURIMutator> mutator = this; 466 mutator.forget(aMutator); 467 } 468 return BaseURIMutator<T>::mURI->SetFileNameInternal(aFileName); 469 } 470 471 [[nodiscard]] NS_IMETHOD SetFileBaseName( 472 const nsACString& aFileBaseName, nsIURIMutator** aMutator) override { 473 if (!BaseURIMutator<T>::mURI) { 474 return NS_ERROR_NULL_POINTER; 475 } 476 if (aMutator) { 477 nsCOMPtr<nsIURIMutator> mutator = this; 478 mutator.forget(aMutator); 479 } 480 return BaseURIMutator<T>::mURI->SetFileBaseNameInternal(aFileBaseName); 481 } 482 483 [[nodiscard]] NS_IMETHOD SetFileExtension( 484 const nsACString& aFileExtension, nsIURIMutator** aMutator) override { 485 if (!BaseURIMutator<T>::mURI) { 486 return NS_ERROR_NULL_POINTER; 487 } 488 if (aMutator) { 489 nsCOMPtr<nsIURIMutator> mutator = this; 490 mutator.forget(aMutator); 491 } 492 return BaseURIMutator<T>::mURI->SetFileExtensionInternal(aFileExtension); 493 } 494 495 T* Create() override { return new T(mMarkedFileURL); } 496 497 [[nodiscard]] NS_IMETHOD MarkFileURL() override { 498 mMarkedFileURL = true; 499 return NS_OK; 500 } 501 502 [[nodiscard]] NS_IMETHOD SetFile(nsIFile* aFile) override { 503 RefPtr<T> uri; 504 if (BaseURIMutator<T>::mURI) { 505 // We don't need a new URI object if we already have one 506 BaseURIMutator<T>::mURI.swap(uri); 507 } else { 508 uri = new T(/* aSupportsFileURL = */ true); 509 } 510 511 nsresult rv = uri->SetFile(aFile); 512 if (NS_FAILED(rv)) { 513 return rv; 514 } 515 BaseURIMutator<T>::mURI.swap(uri); 516 return NS_OK; 517 } 518 519 explicit TemplatedMutator() = default; 520 521 private: 522 virtual ~TemplatedMutator() = default; 523 524 bool mMarkedFileURL = false; 525 526 friend T; 527 }; 528 529 class Mutator final : public TemplatedMutator<nsStandardURL> { 530 NS_DECL_ISUPPORTS 531 public: 532 explicit Mutator() = default; 533 534 private: 535 virtual ~Mutator() = default; 536 }; 537 538 friend BaseURIMutator<nsStandardURL>; 539 }; 540 541 #define NS_THIS_STANDARDURL_IMPL_CID \ 542 {/* b8e3e97b-1ccd-4b45-af5a-79596770f5d7 */ \ 543 0xb8e3e97b, \ 544 0x1ccd, \ 545 0x4b45, \ 546 {0xaf, 0x5a, 0x79, 0x59, 0x67, 0x70, 0xf5, 0xd7}} 547 548 //----------------------------------------------------------------------------- 549 // Dependent substring getters 550 //----------------------------------------------------------------------------- 551 552 inline nsDependentCSubstring nsStandardURL::Segment(uint32_t pos, int32_t len) { 553 if (len < 0) { 554 pos = 0; 555 len = 0; 556 } 557 return Substring(mSpec, pos, uint32_t(len)); 558 } 559 560 inline nsDependentCSubstring nsStandardURL::Prepath() { 561 uint32_t len = 0; 562 if (mAuthority.mLen >= 0) len = mAuthority.mPos + mAuthority.mLen; 563 return Substring(mSpec, 0, len); 564 } 565 566 inline nsDependentCSubstring nsStandardURL::Userpass(bool includeDelim) { 567 uint32_t pos = 0, len = 0; 568 if (mUsername.mLen > 0 || mPassword.mLen > 0) { 569 if (mUsername.mLen > 0) { 570 pos = mUsername.mPos; 571 len = mUsername.mLen; 572 if (mPassword.mLen >= 0) { 573 len += (mPassword.mLen + 1); 574 } 575 } else { 576 pos = mPassword.mPos - 1; 577 len = mPassword.mLen + 1; 578 } 579 580 if (includeDelim) len++; 581 } 582 return Substring(mSpec, pos, len); 583 } 584 585 inline nsDependentCSubstring nsStandardURL::Hostport() { 586 uint32_t pos = 0, len = 0; 587 if (mAuthority.mLen > 0) { 588 pos = mHost.mPos; 589 len = mAuthority.mPos + mAuthority.mLen - pos; 590 } 591 return Substring(mSpec, pos, len); 592 } 593 594 inline nsDependentCSubstring nsStandardURL::Host() { 595 uint32_t pos = 0, len = 0; 596 if (mHost.mLen > 0) { 597 pos = mHost.mPos; 598 len = mHost.mLen; 599 if (mSpec.CharAt(pos) == '[' && mSpec.CharAt(pos + len - 1) == ']') { 600 pos++; 601 len -= 2; 602 } 603 } 604 return Substring(mSpec, pos, len); 605 } 606 607 inline nsDependentCSubstring nsStandardURL::Filename() { 608 uint32_t pos = 0, len = 0; 609 // if there is no basename, then there can be no extension 610 if (mBasename.mLen > 0) { 611 pos = mBasename.mPos; 612 len = mBasename.mLen; 613 if (mExtension.mLen >= 0) len += (mExtension.mLen + 1); 614 } 615 return Substring(mSpec, pos, len); 616 } 617 618 } // namespace net 619 } // namespace mozilla 620 621 #endif // nsStandardURL_h__