1/* -*- Mode: C++; tab-width: 2; 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#include "nsISupports.idl"
7interface nsIURI;
8interface nsIObjectInputStream;
9interface nsIURIMutator;
10
11%{C++
12#include "nsString.h"
13#include "nsCOMPtr.h"
14#include <functional>
15
16#undef SetPort  // XXX Windows!
17
18namespace mozilla {
19class Encoding;
20}
21
22namespace mozilla {
23namespace ipc {
24class URIParams;
25} // namespace ipc
26} // namespace mozilla
27
28template <class T>
29class BaseURIMutator
30{
31// This is the base class that can be extended by implementors of nsIURIMutator
32// in order to avoid code duplication
33// Class type T should be the type of the class that implements nsIURI
34protected:
35  MOZ_MUST_USE nsresult InitFromURI(T* aURI)
36  {
37    nsCOMPtr<nsIURI> clone;
38    nsresult rv = aURI->Clone(getter_AddRefs(clone));
39    if (NS_FAILED(rv)) {
40      return rv;
41    }
42    mURI = static_cast<T*>(clone.get());
43    return NS_OK;
44  }
45
46  MOZ_MUST_USE nsresult InitFromInputStream(nsIObjectInputStream* aStream)
47  {
48    RefPtr<T> uri = new T();
49    nsresult rv = uri->Read(aStream);
50    if (NS_FAILED(rv)) {
51      return rv;
52    }
53    mURI = uri.forget();
54    return NS_OK;
55  }
56
57  MOZ_MUST_USE nsresult InitFromIPCParams(const mozilla::ipc::URIParams& aParams)
58  {
59    RefPtr<T> uri = new T();
60    bool ret = uri->Deserialize(aParams);
61    if (!ret) {
62      return NS_ERROR_FAILURE;
63    }
64    mURI = uri.forget();
65    return NS_OK;
66  }
67
68  MOZ_MUST_USE nsresult InitFromSpec(const nsACString& aSpec)
69  {
70    nsresult rv = NS_OK;
71    RefPtr<T> uri;
72    if (mURI) {
73      // This only works because all other Init methods create a new object
74      mURI.swap(uri);
75    } else {
76      uri = new T();
77    }
78
79    rv = uri->SetSpecInternal(aSpec);
80    if (NS_FAILED(rv)) {
81      return rv;
82    }
83    mURI = uri.forget();
84    return NS_OK;
85  }
86
87  RefPtr<T> mURI;
88};
89
90// Since most implementations of nsIURIMutator would extend BaseURIMutator,
91// some methods would have the same implementation. We provide a useful macro
92// to avoid code duplication.
93#define NS_DEFINE_NSIMUTATOR_COMMON                                           \
94  MOZ_MUST_USE NS_IMETHOD                                                     \
95  Deserialize(const mozilla::ipc::URIParams& aParams) override                \
96  {                                                                           \
97    return InitFromIPCParams(aParams);                                        \
98  }                                                                           \
99                                                                              \
100  MOZ_MUST_USE NS_IMETHOD                                                     \
101  Read(nsIObjectInputStream* aStream) override                                \
102  {                                                                           \
103    return InitFromInputStream(aStream);                                      \
104  }                                                                           \
105                                                                              \
106  MOZ_MUST_USE NS_IMETHOD                                                     \
107  Finalize(nsIURI** aURI) override                                            \
108  {                                                                           \
109    mURI.forget(aURI); return NS_OK;                                          \
110  }                                                                           \
111                                                                              \
112  MOZ_MUST_USE NS_IMETHOD                                                     \
113  SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override         \
114  {                                                                           \
115    if (aMutator) NS_ADDREF(*aMutator = this);                                \
116    return InitFromSpec(aSpec);                                               \
117  }                                                                           \
118%}
119
120[ptr] native Encoding(const mozilla::Encoding);
121[ref] native const_URIParams_ref(const mozilla::ipc::URIParams);
122
123[scriptable, builtinclass, uuid(1fc53257-898b-4c5e-b69c-05bc84b4cd8f)]
124interface nsIURISetSpec : nsISupports
125{
126  /**
127   * This setter is different from all other setters because it may be used to
128   * initialize the object. We define it separately allowing mutator implementors
129   * to define it separately, while the rest of the setters may be simply
130   * forwarded to the mutable URI.
131   */
132  [must_use] nsIURIMutator setSpec(in AUTF8String aSpec);
133};
134
135/**
136 * These methods allow the mutator to change various parts of the URI.
137 * They return the same nsIURIMutator so that we may chain setter operations:
138 * Example:
139 * let newURI = uri.mutate()
140 *                 .setSpec("http://example.com")
141 *                 .setQuery("hello")
142 *                 .finalize();
143 */
144[scriptable, builtinclass, uuid(5403a6ec-99d7-405e-8b45-9f805bbdfcef)]
145interface nsIURISetters : nsIURISetSpec
146{
147  /**
148   * Setting the scheme outside of a protocol handler implementation is highly
149   * discouraged since that will generally lead to incorrect results.
150   */
151  [must_use] nsIURIMutator setScheme(in AUTF8String aScheme);
152  [must_use] nsIURIMutator setUserPass(in AUTF8String aUserPass);
153  [must_use] nsIURIMutator setUsername(in AUTF8String aUsername);
154  [must_use] nsIURIMutator setPassword(in AUTF8String aPassword);
155
156  /**
157   * If you setHostPort to a value that only has a host part, the port
158   * will not be reset. To reset the port set it to -1 beforehand.
159   * If setting the host succeeds, this method will return NS_OK, even if
160   * setting the port fails (error in parsing the port, or value out of range)
161   */
162  [must_use] nsIURIMutator setHostPort(in AUTF8String aHostPort);
163  [must_use] nsIURIMutator setHost(in AUTF8String aHost);
164  [must_use] nsIURIMutator setPort(in long aPort);
165  [must_use] nsIURIMutator setPathQueryRef(in AUTF8String aPathQueryRef);
166  [must_use] nsIURIMutator setRef(in AUTF8String aRef);
167  [must_use] nsIURIMutator setFilePath(in AUTF8String aFilePath);
168  [must_use] nsIURIMutator setQuery(in AUTF8String aQuery);
169  [must_use, noscript] nsIURIMutator setQueryWithEncoding(in AUTF8String query, in Encoding encoding);
170};
171
172%{C++
173
174// Using this macro instead of NS_FORWARD_SAFE_NSIURISETTERS makes chaining
175// setter operations possible.
176#define NS_FORWARD_SAFE_NSIURISETTERS_RET(_to)                                \
177  MOZ_MUST_USE NS_IMETHOD                                                     \
178  SetScheme(const nsACString& aScheme, nsIURIMutator** aMutator) override     \
179  {                                                                           \
180    if (aMutator) NS_ADDREF(*aMutator = this);                                \
181    return !_to ? NS_ERROR_NULL_POINTER : _to->SetScheme(aScheme);            \
182  }                                                                           \
183  MOZ_MUST_USE NS_IMETHOD                                                     \
184  SetUserPass(const nsACString& aUserPass, nsIURIMutator** aMutator) override \
185  {                                                                           \
186    if (aMutator) NS_ADDREF(*aMutator = this);                                \
187    return !_to ? NS_ERROR_NULL_POINTER : _to->SetUserPass(aUserPass);        \
188  }                                                                           \
189  MOZ_MUST_USE NS_IMETHOD                                                     \
190  SetUsername(const nsACString& aUsername, nsIURIMutator** aMutator) override \
191  {                                                                           \
192    if (aMutator) NS_ADDREF(*aMutator = this);                                \
193    return !_to ? NS_ERROR_NULL_POINTER : _to->SetUsername(aUsername);        \
194  }                                                                           \
195  MOZ_MUST_USE NS_IMETHOD                                                     \
196  SetPassword(const nsACString& aPassword, nsIURIMutator** aMutator) override \
197  {                                                                           \
198    if (aMutator) NS_ADDREF(*aMutator = this);                                \
199    return !_to ? NS_ERROR_NULL_POINTER : _to->SetPassword(aPassword);        \
200  }                                                                           \
201  MOZ_MUST_USE NS_IMETHOD                                                     \
202  SetHostPort(const nsACString& aHostPort, nsIURIMutator** aMutator) override \
203  {                                                                           \
204    if (aMutator) NS_ADDREF(*aMutator = this);                                \
205    return !_to ? NS_ERROR_NULL_POINTER : _to->SetHostPort(aHostPort);        \
206  }                                                                           \
207  MOZ_MUST_USE NS_IMETHOD                                                     \
208  SetHost(const nsACString& aHost, nsIURIMutator** aMutator) override         \
209  {                                                                           \
210    if (aMutator) NS_ADDREF(*aMutator = this);                                \
211    return !_to ? NS_ERROR_NULL_POINTER : _to->SetHost(aHost);                \
212  }                                                                           \
213  MOZ_MUST_USE NS_IMETHOD                                                     \
214  SetPort(int32_t aPort, nsIURIMutator** aMutator) override                   \
215  {                                                                           \
216    if (aMutator) NS_ADDREF(*aMutator = this);                                \
217    return !_to ? NS_ERROR_NULL_POINTER : _to->SetPort(aPort);                \
218  }                                                                           \
219  MOZ_MUST_USE NS_IMETHOD                                                     \
220  SetPathQueryRef(const nsACString& aPathQueryRef, nsIURIMutator** aMutator) override \
221  {                                                                           \
222    if (aMutator) NS_ADDREF(*aMutator = this);                                \
223    return !_to ? NS_ERROR_NULL_POINTER : _to->SetPathQueryRef(aPathQueryRef); \
224  }                                                                           \
225  MOZ_MUST_USE NS_IMETHOD                                                     \
226  SetRef(const nsACString& aRef, nsIURIMutator** aMutator) override           \
227  {                                                                           \
228    if (aMutator) NS_ADDREF(*aMutator = this);                                \
229    return !_to ? NS_ERROR_NULL_POINTER : _to->SetRef(aRef);                  \
230  }                                                                           \
231  MOZ_MUST_USE NS_IMETHOD                                                     \
232  SetFilePath(const nsACString& aFilePath, nsIURIMutator** aMutator) override \
233  {                                                                           \
234    if (aMutator) NS_ADDREF(*aMutator = this);                                \
235    return !_to ? NS_ERROR_NULL_POINTER : _to->SetFilePath(aFilePath);        \
236  }                                                                           \
237  MOZ_MUST_USE NS_IMETHOD                                                     \
238  SetQuery(const nsACString& aQuery, nsIURIMutator** aMutator) override       \
239  {                                                                           \
240    if (aMutator) NS_ADDREF(*aMutator = this);                                \
241    return !_to ? NS_ERROR_NULL_POINTER : _to->SetQuery(aQuery);              \
242  }                                                                           \
243  MOZ_MUST_USE NS_IMETHOD                                                     \
244  SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding, nsIURIMutator** aMutator) override \
245  {                                                                           \
246    if (aMutator) NS_ADDREF(*aMutator = this);                                \
247    return !_to ? NS_ERROR_NULL_POINTER : _to->SetQueryWithEncoding(query, encoding); \
248  }                                                                           \
249
250%}
251
252[scriptable, builtinclass, uuid(4d1f3103-1c44-4dcd-b717-5d22a697a7d9)]
253interface nsIURIMutator : nsIURISetters
254{
255  /**
256   * Initializes the URI by reading from the input stream.
257   * The input stream must contain the serialization of the same object type.
258   * See nsISerializable.
259   */
260  [must_use]
261  void read(in nsIObjectInputStream aInputStream);
262
263  /**
264   * Initalizes the URI by reading IPC URIParams.
265   * See nsIIPCSerializableURI.
266   */
267  [noscript, notxpcom, must_use]
268  nsresult deserialize(in const_URIParams_ref aParams);
269
270  /**
271   * Finishes changing or constructing the URI and returns an immutable URI.
272   */
273  [must_use]
274  nsIURI finalize();
275};
276
277%{C++
278
279// This templated struct is used to extract the class type of the method
280// passed to NS_MutatorMethod.
281template <typename Method>
282struct nsMethodTypeTraits;
283
284template <class C, typename R, typename... As>
285struct nsMethodTypeTraits<R(C::*)(As...)>
286{
287  typedef C class_type;
288};
289
290#ifdef NS_HAVE_STDCALL
291template <class C, typename R, typename... As>
292struct nsMethodTypeTraits<R(__stdcall C::*)(As...)>
293{
294  typedef C class_type;
295};
296#endif
297
298// This helper returns a std::function that will be applied on the
299// nsIURIMutator. The type of `Interface` will be deduced from the method type.
300// aMethod will be called on the target object if it successfully QIs to
301// `Interface`, and the arguments will be passed to the call.
302template <typename Method, typename... Args>
303const std::function<nsresult(nsIURIMutator*)>
304NS_MutatorMethod(Method aMethod, Args ...aArgs)
305{
306  // Capture arguments by value, otherwise we crash.
307  return [=](nsIURIMutator* aMutator) {
308    typedef typename nsMethodTypeTraits<Method>::class_type Interface;
309    nsresult rv;
310    nsCOMPtr<Interface> target = do_QueryInterface(aMutator, &rv);
311    NS_ENSURE_SUCCESS(rv, rv);
312    rv = (target->*aMethod)(aArgs...);
313    NS_ENSURE_SUCCESS(rv, rv);
314    return NS_OK;
315  };
316}
317
318// This class provides a useful helper that allows chaining of setter operations
319class MOZ_STACK_CLASS NS_MutateURI
320{
321public:
322  explicit NS_MutateURI(nsIURI* aURI);
323  explicit NS_MutateURI(const char * aContractID);
324
325  explicit NS_MutateURI(nsIURIMutator* m)
326  {
327    mStatus = m ? NS_OK : NS_ERROR_NULL_POINTER;
328    mMutator = m;
329    NS_ENSURE_SUCCESS_VOID(mStatus);
330  }
331
332  NS_MutateURI& SetSpec(const nsACString& aSpec)
333  {
334    if (NS_FAILED(mStatus)) {
335      return *this;
336    }
337    mStatus = mMutator->SetSpec(aSpec, nullptr);
338    NS_ENSURE_SUCCESS(mStatus, *this);
339    return *this;
340  }
341  NS_MutateURI& SetScheme(const nsACString& aScheme)
342  {
343    if (NS_FAILED(mStatus)) {
344      return *this;
345    }
346    mStatus = mMutator->SetScheme(aScheme, nullptr);
347    NS_ENSURE_SUCCESS(mStatus, *this);
348    return *this;
349  }
350  NS_MutateURI& SetUserPass(const nsACString& aUserPass)
351  {
352    if (NS_FAILED(mStatus)) {
353      return *this;
354    }
355    mStatus = mMutator->SetUserPass(aUserPass, nullptr);
356    NS_ENSURE_SUCCESS(mStatus, *this);
357    return *this;
358  }
359  NS_MutateURI& SetUsername(const nsACString& aUsername)
360  {
361    if (NS_FAILED(mStatus)) {
362      return *this;
363    }
364    mStatus = mMutator->SetUsername(aUsername, nullptr);
365    NS_ENSURE_SUCCESS(mStatus, *this);
366    return *this;
367  }
368  NS_MutateURI& SetPassword(const nsACString& aPassword)
369  {
370    if (NS_FAILED(mStatus)) {
371      return *this;
372    }
373    mStatus = mMutator->SetPassword(aPassword, nullptr);
374    NS_ENSURE_SUCCESS(mStatus, *this);
375    return *this;
376  }
377  NS_MutateURI& SetHostPort(const nsACString& aHostPort)
378  {
379    if (NS_FAILED(mStatus)) {
380      return *this;
381    }
382    mStatus = mMutator->SetHostPort(aHostPort, nullptr);
383    NS_ENSURE_SUCCESS(mStatus, *this);
384    return *this;
385  }
386  NS_MutateURI& SetHost(const nsACString& aHost)
387  {
388    if (NS_FAILED(mStatus)) {
389      return *this;
390    }
391    mStatus = mMutator->SetHost(aHost, nullptr);
392    NS_ENSURE_SUCCESS(mStatus, *this);
393    return *this;
394  }
395  NS_MutateURI& SetPort(int32_t aPort)
396  {
397    if (NS_FAILED(mStatus)) {
398      return *this;
399    }
400    mStatus = mMutator->SetPort(aPort, nullptr);
401    NS_ENSURE_SUCCESS(mStatus, *this);
402    return *this;
403  }
404  NS_MutateURI& SetPathQueryRef(const nsACString& aPathQueryRef)
405  {
406    if (NS_FAILED(mStatus)) {
407      return *this;
408    }
409    mStatus = mMutator->SetPathQueryRef(aPathQueryRef, nullptr);
410    NS_ENSURE_SUCCESS(mStatus, *this);
411    return *this;
412  }
413  NS_MutateURI& SetRef(const nsACString& aRef)
414  {
415    if (NS_FAILED(mStatus)) {
416      return *this;
417    }
418    mStatus = mMutator->SetRef(aRef, nullptr);
419    NS_ENSURE_SUCCESS(mStatus, *this);
420    return *this;
421  }
422  NS_MutateURI& SetFilePath(const nsACString& aFilePath)
423  {
424    if (NS_FAILED(mStatus)) {
425      return *this;
426    }
427    mStatus = mMutator->SetFilePath(aFilePath, nullptr);
428    NS_ENSURE_SUCCESS(mStatus, *this);
429    return *this;
430  }
431  NS_MutateURI& SetQuery(const nsACString& aQuery)
432  {
433    if (NS_FAILED(mStatus)) {
434      return *this;
435    }
436    mStatus = mMutator->SetQuery(aQuery, nullptr);
437    NS_ENSURE_SUCCESS(mStatus, *this);
438    return *this;
439  }
440  NS_MutateURI& SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding)
441  {
442    if (NS_FAILED(mStatus)) {
443      return *this;
444    }
445    mStatus = mMutator->SetQueryWithEncoding(query, encoding, nullptr);
446    NS_ENSURE_SUCCESS(mStatus, *this);
447    return *this;
448  }
449
450  /**
451   * This method allows consumers to call the methods declared in other
452   * interfaces implemented by the mutator object.
453   *
454   * Example:
455   * nsCOMPtr<nsIURI> uri;
456   * nsresult rv = NS_MutateURI(new URIClass::Mutator())
457   *                 .SetSpec(aSpec)
458   *                 .Apply(NS_MutatorMethod(&SomeInterface::Method, arg1, arg2))
459   *                 .Finalize(uri);
460   *
461   * If mMutator does not implement SomeInterface, do_QueryInterface will fail
462   * and the method will not be called.
463   * If aMethod does not exist, or if there is a mismatch between argument
464   * types, or the number of arguments, then there will be a compile error.
465   */
466  NS_MutateURI& Apply(const std::function<nsresult(nsIURIMutator*)>& aFunction)
467  {
468    if (NS_FAILED(mStatus)) {
469      return *this;
470    }
471    mStatus = aFunction(mMutator);
472    NS_ENSURE_SUCCESS(mStatus, *this);
473    return *this;
474  }
475
476  template <class C>
477  MOZ_MUST_USE nsresult Finalize(nsCOMPtr<C>& aURI)
478  {
479    NS_ENSURE_SUCCESS(mStatus, mStatus);
480
481    nsCOMPtr<nsIURI> uri;
482    mStatus = mMutator->Finalize(getter_AddRefs(uri));
483    NS_ENSURE_SUCCESS(mStatus, mStatus);
484
485    aURI = do_QueryInterface(uri, &mStatus);
486    NS_ENSURE_SUCCESS(mStatus, mStatus);
487
488    mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
489    return NS_OK;
490  }
491
492  // Overload for nsIURI to avoid query interface.
493  MOZ_MUST_USE nsresult Finalize(nsCOMPtr<nsIURI>& aURI)
494  {
495    NS_ENSURE_SUCCESS(mStatus, mStatus);
496    mStatus = mMutator->Finalize(getter_AddRefs(aURI));
497    NS_ENSURE_SUCCESS(mStatus, mStatus);
498
499    mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
500    return NS_OK;
501  }
502
503  template <class C>
504  MOZ_MUST_USE nsresult Finalize(C** aURI)
505  {
506    NS_ENSURE_SUCCESS(mStatus, mStatus);
507
508    nsCOMPtr<nsIURI> uri;
509    mStatus = mMutator->Finalize(getter_AddRefs(uri));
510    NS_ENSURE_SUCCESS(mStatus, mStatus);
511
512    nsCOMPtr<C> result = do_QueryInterface(uri, &mStatus);
513    NS_ENSURE_SUCCESS(mStatus, mStatus);
514
515    result.forget(aURI);
516    mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
517    return NS_OK;
518  }
519
520  MOZ_MUST_USE nsresult Finalize(nsIURI** aURI)
521  {
522    NS_ENSURE_SUCCESS(mStatus, mStatus);
523    mStatus = mMutator->Finalize(aURI);
524    NS_ENSURE_SUCCESS(mStatus, mStatus);
525
526    mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
527    return NS_OK;
528  }
529
530  nsresult GetStatus() { return mStatus; }
531private:
532  nsresult mStatus;
533  nsCOMPtr<nsIURIMutator> mMutator;
534};
535
536%}
537