1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef mozURL_h__ 6 #define mozURL_h__ 7 8 #include "mozilla/net/MozURL_ffi.h" 9 #include "mozilla/RefPtr.h" 10 #include "mozilla/Result.h" 11 12 namespace mozilla { 13 namespace net { 14 15 // This class provides a thread-safe, immutable URL parser. 16 // As long as there is RefPtr to the object, you may use it on any thread. 17 // The constructor is private. One can instantiate the object by 18 // calling the Init() method as such: 19 // 20 // RefPtr<MozURL> url; 21 // nsAutoCString href("http://example.com/path?query#ref"); 22 // nsresult rv = MozURL::Init(getter_AddRefs(url), href); 23 // if (NS_SUCCEEDED(rv)) { /* use url */ } 24 // 25 // When changing the URL is needed, you need to call the Mutate() method. 26 // This gives you a Mutator object, on which you can perform setter operations. 27 // Calling Finalize() on the Mutator will result in a new MozURL and a status 28 // code. If any of the setter operations failed, it will be reflected in the 29 // status code, and a null MozURL. 30 // 31 // Note: In the case of a domain name containing non-ascii characters, 32 // GetSpec and GetHostname will return the IDNA(punycode) version of the host. 33 // Also note that for now, MozURL only supports the UTF-8 charset. 34 // 35 // Implementor Note: This type is only a holder for methods in C++, and does not 36 // reflect the actual layout of the type. 37 class MozURL final { 38 public: 39 static nsresult Init(MozURL** aURL, const nsACString& aSpec, 40 const MozURL* aBaseURL = nullptr) { 41 return mozurl_new(aURL, &aSpec, aBaseURL); 42 } 43 Spec()44 nsDependentCSubstring Spec() const { return mozurl_spec(this); } Scheme()45 nsDependentCSubstring Scheme() const { return mozurl_scheme(this); } Username()46 nsDependentCSubstring Username() const { return mozurl_username(this); } Password()47 nsDependentCSubstring Password() const { return mozurl_password(this); } 48 // Will return the hostname of URL. If the hostname is an IPv6 address, 49 // it will be enclosed in square brackets, such as `[::1]` Host()50 nsDependentCSubstring Host() const { return mozurl_host(this); } 51 // Will return the port number, if specified, or -1 Port()52 int32_t Port() const { return mozurl_port(this); } RealPort()53 int32_t RealPort() const { return mozurl_real_port(this); } 54 // If the URL's port number is equal to the default port, will only return the 55 // hostname, otherwise it will return a string of the form `{host}:{port}` 56 // See: https://url.spec.whatwg.org/#default-port HostPort()57 nsDependentCSubstring HostPort() const { return mozurl_host_port(this); } FilePath()58 nsDependentCSubstring FilePath() const { return mozurl_filepath(this); } Path()59 nsDependentCSubstring Path() const { return mozurl_path(this); } Query()60 nsDependentCSubstring Query() const { return mozurl_query(this); } Ref()61 nsDependentCSubstring Ref() const { return mozurl_fragment(this); } HasFragment()62 bool HasFragment() const { return mozurl_has_fragment(this); } Directory()63 nsDependentCSubstring Directory() const { return mozurl_directory(this); } PrePath()64 nsDependentCSubstring PrePath() const { return mozurl_prepath(this); } SpecNoRef()65 nsDependentCSubstring SpecNoRef() const { return mozurl_spec_no_ref(this); } 66 67 // This matches the definition of origins and base domains in nsIPrincipal for 68 // almost all URIs (some rare file:// URIs don't match and it would be hard to 69 // fix them). It definitely matches nsIPrincipal for URIs used in quota 70 // manager and there are checks in quota manager and its clients that prevent 71 // different definitions (see QuotaManager::IsPrincipalInfoValid). 72 // See also TestMozURL.cpp which enumerates a huge pile of URIs and checks 73 // that origin and base domain definitions are in sync. Origin(nsACString & aOrigin)74 void Origin(nsACString& aOrigin) const { mozurl_origin(this, &aOrigin); } BaseDomain(nsACString & aBaseDomain)75 nsresult BaseDomain(nsACString& aBaseDomain) const { 76 return mozurl_base_domain(this, &aBaseDomain); 77 } 78 GetCommonBase(const MozURL * aOther,MozURL ** aCommon)79 nsresult GetCommonBase(const MozURL* aOther, MozURL** aCommon) const { 80 return mozurl_common_base(this, aOther, aCommon); 81 } GetRelative(const MozURL * aOther,nsACString * aRelative)82 nsresult GetRelative(const MozURL* aOther, nsACString* aRelative) const { 83 return mozurl_relative(this, aOther, aRelative); 84 } 85 SizeOf()86 size_t SizeOf() { return mozurl_sizeof(this); } 87 88 class Mutator { 89 public: 90 // Calling this method will result in the creation of a new MozURL that 91 // adopts the mutator's mURL. 92 // If any of the setters failed with an error code, that error code will be 93 // returned here. It will also return an error code if Finalize is called 94 // more than once on the Mutator. Finalize(MozURL ** aURL)95 nsresult Finalize(MozURL** aURL) { 96 nsresult rv = GetStatus(); 97 if (NS_SUCCEEDED(rv)) { 98 mURL.forget(aURL); 99 } else { 100 *aURL = nullptr; 101 } 102 return rv; 103 } 104 105 // These setter methods will return a reference to `this` so that you may 106 // chain setter operations as such: 107 // 108 // RefPtr<MozURL> url2; 109 // nsresult rv = url->Mutate().SetHostname("newhost"_ns) 110 // .SetFilePath("new/file/path"_ns) 111 // .Finalize(getter_AddRefs(url2)); 112 // if (NS_SUCCEEDED(rv)) { /* use url2 */ } SetScheme(const nsACString & aScheme)113 Mutator& SetScheme(const nsACString& aScheme) { 114 if (NS_SUCCEEDED(GetStatus())) { 115 mStatus = mozurl_set_scheme(mURL, &aScheme); 116 } 117 return *this; 118 } SetUsername(const nsACString & aUser)119 Mutator& SetUsername(const nsACString& aUser) { 120 if (NS_SUCCEEDED(GetStatus())) { 121 mStatus = mozurl_set_username(mURL, &aUser); 122 } 123 return *this; 124 } SetPassword(const nsACString & aPassword)125 Mutator& SetPassword(const nsACString& aPassword) { 126 if (NS_SUCCEEDED(GetStatus())) { 127 mStatus = mozurl_set_password(mURL, &aPassword); 128 } 129 return *this; 130 } SetHostname(const nsACString & aHost)131 Mutator& SetHostname(const nsACString& aHost) { 132 if (NS_SUCCEEDED(GetStatus())) { 133 mStatus = mozurl_set_hostname(mURL, &aHost); 134 } 135 return *this; 136 } SetHostPort(const nsACString & aHostPort)137 Mutator& SetHostPort(const nsACString& aHostPort) { 138 if (NS_SUCCEEDED(GetStatus())) { 139 mStatus = mozurl_set_host_port(mURL, &aHostPort); 140 } 141 return *this; 142 } SetFilePath(const nsACString & aPath)143 Mutator& SetFilePath(const nsACString& aPath) { 144 if (NS_SUCCEEDED(GetStatus())) { 145 mStatus = mozurl_set_pathname(mURL, &aPath); 146 } 147 return *this; 148 } SetQuery(const nsACString & aQuery)149 Mutator& SetQuery(const nsACString& aQuery) { 150 if (NS_SUCCEEDED(GetStatus())) { 151 mStatus = mozurl_set_query(mURL, &aQuery); 152 } 153 return *this; 154 } SetRef(const nsACString & aRef)155 Mutator& SetRef(const nsACString& aRef) { 156 if (NS_SUCCEEDED(GetStatus())) { 157 mStatus = mozurl_set_fragment(mURL, &aRef); 158 } 159 return *this; 160 } SetPort(int32_t aPort)161 Mutator& SetPort(int32_t aPort) { 162 if (NS_SUCCEEDED(GetStatus())) { 163 mStatus = mozurl_set_port_no(mURL, aPort); 164 } 165 return *this; 166 } 167 168 // This method returns the status code of the setter operations. 169 // If any of the setters failed, it will return the code of the first error 170 // that occured. If none of the setters failed, it will return NS_OK. 171 // This method is useful to avoid doing expensive operations when the result 172 // would not be used because an error occurred. For example: 173 // 174 // RefPtr<MozURL> url2; 175 // MozURL::Mutator mut = url->Mutate(); 176 // mut.SetScheme("!@#$"); // this would fail 177 // if (NS_SUCCEDED(mut.GetStatus())) { 178 // nsAutoCString host(ExpensiveComputing()); 179 // rv = mut.SetHostname(host).Finalize(getter_AddRefs(url2)); 180 // } 181 // if (NS_SUCCEEDED(rv)) { /* use url2 */ } GetStatus()182 nsresult GetStatus() { return mURL ? mStatus : NS_ERROR_NOT_AVAILABLE; } 183 184 static Result<Mutator, nsresult> FromSpec( 185 const nsACString& aSpec, const MozURL* aBaseURL = nullptr) { 186 Mutator m = Mutator(aSpec, aBaseURL); 187 if (m.mURL) { 188 MOZ_ASSERT(NS_SUCCEEDED(m.mStatus)); 189 return m; 190 } 191 192 MOZ_ASSERT(NS_FAILED(m.mStatus)); 193 return Err(m.mStatus); 194 } 195 196 private: Mutator(MozURL * aUrl)197 explicit Mutator(MozURL* aUrl) : mStatus(NS_OK) { 198 mozurl_clone(aUrl, getter_AddRefs(mURL)); 199 } 200 201 // This is only used to create a mutator from a string without cloning it 202 // so we avoid a pointless copy in FromSpec. It is important that we 203 // check the value of mURL afterwards. 204 explicit Mutator(const nsACString& aSpec, 205 const MozURL* aBaseURL = nullptr) { 206 mStatus = mozurl_new(getter_AddRefs(mURL), &aSpec, aBaseURL); 207 } 208 RefPtr<MozURL> mURL; 209 nsresult mStatus; 210 friend class MozURL; 211 }; 212 Mutate()213 Mutator Mutate() { return Mutator(this); } 214 215 // AddRef and Release are non-virtual on this type, and always call into rust. AddRef()216 nsrefcnt AddRef() { return mozurl_addref(this); } Release()217 nsrefcnt Release() { return mozurl_release(this); } 218 219 private: 220 // Make it a compile time error for C++ code to ever create, destruct, or copy 221 // MozURL objects. All of these operations will be performed by rust. 222 MozURL(); /* never defined */ 223 ~MozURL(); /* never defined */ 224 MozURL(const MozURL&) = delete; 225 MozURL& operator=(const MozURL&) = delete; 226 }; 227 228 } // namespace net 229 } // namespace mozilla 230 231 #endif // mozURL_h__ 232