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