summaryrefslogtreecommitdiffstats
path: root/netwerk/base/mozurl/MozURL.h
blob: ddd91c1b0c3954bcbc3c2f64add0d4a0293e172f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozURL_h__
#define mozURL_h__

#include "mozilla/net/MozURL_ffi.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"

namespace mozilla {
namespace net {

// This class provides a thread-safe, immutable URL parser.
// As long as there is RefPtr to the object, you may use it on any thread.
// The constructor is private. One can instantiate the object by
// calling the Init() method as such:
//
// RefPtr<MozURL> url;
// nsAutoCString href("http://example.com/path?query#ref");
// nsresult rv = MozURL::Init(getter_AddRefs(url), href);
// if (NS_SUCCEEDED(rv)) { /* use url */ }
//
// When changing the URL is needed, you need to call the Mutate() method.
// This gives you a Mutator object, on which you can perform setter operations.
// Calling Finalize() on the Mutator will result in a new MozURL and a status
// code. If any of the setter operations failed, it will be reflected in the
// status code, and a null MozURL.
//
// Note: In the case of a domain name containing non-ascii characters,
// GetSpec and GetHostname will return the IDNA(punycode) version of the host.
// Also note that for now, MozURL only supports the UTF-8 charset.
//
// Implementor Note: This type is only a holder for methods in C++, and does not
// reflect the actual layout of the type.
class MozURL final {
 public:
  static nsresult Init(MozURL** aURL, const nsACString& aSpec,
                       const MozURL* aBaseURL = nullptr) {
    return mozurl_new(aURL, &aSpec, aBaseURL);
  }

  nsDependentCSubstring Spec() const { return mozurl_spec(this); }
  nsDependentCSubstring Scheme() const { return mozurl_scheme(this); }
  nsDependentCSubstring Username() const { return mozurl_username(this); }
  nsDependentCSubstring Password() const { return mozurl_password(this); }
  // Will return the hostname of URL. If the hostname is an IPv6 address,
  // it will be enclosed in square brackets, such as `[::1]`
  nsDependentCSubstring Host() const { return mozurl_host(this); }
  // Will return the port number, if specified, or -1
  int32_t Port() const { return mozurl_port(this); }
  int32_t RealPort() const { return mozurl_real_port(this); }
  // If the URL's port number is equal to the default port, will only return the
  // hostname, otherwise it will return a string of the form `{host}:{port}`
  // See: https://url.spec.whatwg.org/#default-port
  nsDependentCSubstring HostPort() const { return mozurl_host_port(this); }
  nsDependentCSubstring FilePath() const { return mozurl_filepath(this); }
  nsDependentCSubstring Path() const { return mozurl_path(this); }
  nsDependentCSubstring Query() const { return mozurl_query(this); }
  nsDependentCSubstring Ref() const { return mozurl_fragment(this); }
  bool HasFragment() const { return mozurl_has_fragment(this); }
  nsDependentCSubstring Directory() const { return mozurl_directory(this); }
  nsDependentCSubstring PrePath() const { return mozurl_prepath(this); }
  nsDependentCSubstring SpecNoRef() const { return mozurl_spec_no_ref(this); }

  // This matches the definition of origins and base domains in nsIPrincipal for
  // almost all URIs (some rare file:// URIs don't match and it would be hard to
  // fix them). It definitely matches nsIPrincipal for URIs used in quota
  // manager and there are checks in quota manager and its clients that prevent
  // different definitions (see QuotaManager::IsPrincipalInfoValid).
  // See also TestMozURL.cpp which enumerates a huge pile of URIs and checks
  // that origin and base domain definitions are in sync.
  void Origin(nsACString& aOrigin) const { mozurl_origin(this, &aOrigin); }
  nsresult BaseDomain(nsACString& aBaseDomain) const {
    return mozurl_base_domain(this, &aBaseDomain);
  }

  nsresult GetCommonBase(const MozURL* aOther, MozURL** aCommon) const {
    return mozurl_common_base(this, aOther, aCommon);
  }
  nsresult GetRelative(const MozURL* aOther, nsACString* aRelative) const {
    return mozurl_relative(this, aOther, aRelative);
  }

  size_t SizeOf() { return mozurl_sizeof(this); }

  class Mutator {
   public:
    // Calling this method will result in the creation of a new MozURL that
    // adopts the mutator's mURL.
    // If any of the setters failed with an error code, that error code will be
    // returned here. It will also return an error code if Finalize is called
    // more than once on the Mutator.
    nsresult Finalize(MozURL** aURL) {
      nsresult rv = GetStatus();
      if (NS_SUCCEEDED(rv)) {
        mURL.forget(aURL);
      } else {
        *aURL = nullptr;
      }
      return rv;
    }

    // These setter methods will return a reference to `this` so that you may
    // chain setter operations as such:
    //
    // RefPtr<MozURL> url2;
    // nsresult rv = url->Mutate().SetHostname("newhost"_ns)
    //                            .SetFilePath("new/file/path"_ns)
    //                            .Finalize(getter_AddRefs(url2));
    // if (NS_SUCCEEDED(rv)) { /* use url2 */ }
    Mutator& SetScheme(const nsACString& aScheme) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_scheme(mURL, &aScheme);
      }
      return *this;
    }
    Mutator& SetUsername(const nsACString& aUser) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_username(mURL, &aUser);
      }
      return *this;
    }
    Mutator& SetPassword(const nsACString& aPassword) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_password(mURL, &aPassword);
      }
      return *this;
    }
    Mutator& SetHostname(const nsACString& aHost) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_hostname(mURL, &aHost);
      }
      return *this;
    }
    Mutator& SetHostPort(const nsACString& aHostPort) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_host_port(mURL, &aHostPort);
      }
      return *this;
    }
    Mutator& SetFilePath(const nsACString& aPath) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_pathname(mURL, &aPath);
      }
      return *this;
    }
    Mutator& SetQuery(const nsACString& aQuery) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_query(mURL, &aQuery);
      }
      return *this;
    }
    Mutator& SetRef(const nsACString& aRef) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_fragment(mURL, &aRef);
      }
      return *this;
    }
    Mutator& SetPort(int32_t aPort) {
      if (NS_SUCCEEDED(GetStatus())) {
        mStatus = mozurl_set_port_no(mURL, aPort);
      }
      return *this;
    }

    // This method returns the status code of the setter operations.
    // If any of the setters failed, it will return the code of the first error
    // that occured. If none of the setters failed, it will return NS_OK.
    // This method is useful to avoid doing expensive operations when the result
    // would not be used because an error occurred. For example:
    //
    // RefPtr<MozURL> url2;
    // MozURL::Mutator mut = url->Mutate();
    // mut.SetScheme("!@#$"); // this would fail
    // if (NS_SUCCEDED(mut.GetStatus())) {
    //   nsAutoCString host(ExpensiveComputing());
    //   rv = mut.SetHostname(host).Finalize(getter_AddRefs(url2));
    // }
    // if (NS_SUCCEEDED(rv)) { /* use url2 */ }
    nsresult GetStatus() { return mURL ? mStatus : NS_ERROR_NOT_AVAILABLE; }

    static Result<Mutator, nsresult> FromSpec(
        const nsACString& aSpec, const MozURL* aBaseURL = nullptr) {
      Mutator m = Mutator(aSpec, aBaseURL);
      if (m.mURL) {
        MOZ_ASSERT(NS_SUCCEEDED(m.mStatus));
        return m;
      }

      MOZ_ASSERT(NS_FAILED(m.mStatus));
      return Err(m.mStatus);
    }

   private:
    explicit Mutator(MozURL* aUrl) : mStatus(NS_OK) {
      mozurl_clone(aUrl, getter_AddRefs(mURL));
    }

    // This is only used to create a mutator from a string without cloning it
    // so we avoid a pointless copy in FromSpec. It is important that we
    // check the value of mURL afterwards.
    explicit Mutator(const nsACString& aSpec,
                     const MozURL* aBaseURL = nullptr) {
      mStatus = mozurl_new(getter_AddRefs(mURL), &aSpec, aBaseURL);
    }
    RefPtr<MozURL> mURL;
    nsresult mStatus;
    friend class MozURL;
  };

  Mutator Mutate() { return Mutator(this); }

  // AddRef and Release are non-virtual on this type, and always call into rust.
  void AddRef() { mozurl_addref(this); }
  void Release() { mozurl_release(this); }

 private:
  // Make it a compile time error for C++ code to ever create, destruct, or copy
  // MozURL objects. All of these operations will be performed by rust.
  MozURL();  /* never defined */
  ~MozURL(); /* never defined */
  MozURL(const MozURL&) = delete;
  MozURL& operator=(const MozURL&) = delete;
};

}  // namespace net
}  // namespace mozilla

#endif  // mozURL_h__