diff options
Diffstat (limited to 'netwerk/base/nsIURIMutator.idl')
-rw-r--r-- | netwerk/base/nsIURIMutator.idl | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/netwerk/base/nsIURIMutator.idl b/netwerk/base/nsIURIMutator.idl new file mode 100644 index 0000000000..c3afa21075 --- /dev/null +++ b/netwerk/base/nsIURIMutator.idl @@ -0,0 +1,557 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "nsISupports.idl" +interface nsIURI; +interface nsIObjectInputStream; +interface nsIURIMutator; + +%{C++ +#include "nsString.h" +#include "nsCOMPtr.h" +#include <functional> + +#undef SetPort // XXX Windows! + +namespace mozilla { +class Encoding; +} + +namespace mozilla { +namespace ipc { +class URIParams; +} // namespace ipc +} // namespace mozilla + +template <class T> +class BaseURIMutator +{ +// This is the base class that can be extended by implementors of nsIURIMutator +// in order to avoid code duplication +// Class type T should be the type of the class that implements nsIURI +protected: + virtual T* Create() + { + return new T(); + } + + [[nodiscard]] nsresult InitFromURI(T* aURI) + { + nsCOMPtr<nsIURI> clone; + nsresult rv = aURI->Clone(getter_AddRefs(clone)); + if (NS_FAILED(rv)) { + return rv; + } + mURI = static_cast<T*>(clone.get()); + return NS_OK; + } + + [[nodiscard]] nsresult InitFromInputStream(nsIObjectInputStream* aStream) + { + RefPtr<T> uri = Create(); + nsresult rv = uri->ReadPrivate(aStream); + if (NS_FAILED(rv)) { + return rv; + } + mURI = std::move(uri); + return NS_OK; + } + + [[nodiscard]] nsresult InitFromIPCParams(const mozilla::ipc::URIParams& aParams) + { + RefPtr<T> uri = Create(); + bool ret = uri->Deserialize(aParams); + if (!ret) { + return NS_ERROR_FAILURE; + } + mURI = std::move(uri); + return NS_OK; + } + + [[nodiscard]] nsresult InitFromSpec(const nsACString& aSpec) + { + nsresult rv = NS_OK; + RefPtr<T> uri; + if (mURI) { + // This only works because all other Init methods create a new object + mURI.swap(uri); + } else { + uri = Create(); + } + + rv = uri->SetSpecInternal(aSpec); + if (NS_FAILED(rv)) { + return rv; + } + mURI = std::move(uri); + return NS_OK; + } + + RefPtr<T> mURI; +}; + +// Since most implementations of nsIURIMutator would extend BaseURIMutator, +// some methods would have the same implementation. We provide a useful macro +// to avoid code duplication. +#define NS_DEFINE_NSIMUTATOR_COMMON \ + [[nodiscard]] NS_IMETHOD \ + Deserialize(const mozilla::ipc::URIParams& aParams) override \ + { \ + return InitFromIPCParams(aParams); \ + } \ + \ + [[nodiscard]] NS_IMETHOD \ + Finalize(nsIURI** aURI) override \ + { \ + mURI.forget(aURI); return NS_OK; \ + } \ + \ + [[nodiscard]] NS_IMETHOD \ + SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return InitFromSpec(aSpec); \ + } \ + +// Implements AddRef, Release and QueryInterface for the mutator +#define NS_IMPL_NSIURIMUTATOR_ISUPPORTS(aClass, ...) \ + NS_IMPL_ADDREF(aClass) \ + NS_IMPL_RELEASE(aClass) \ + NS_IMPL_NSIURIMUTATOR_QUERY_INTERFACE(aClass, __VA_ARGS__) \ + +// The list of interfaces is queried and an AddRef-ed pointer is returned if +// there is a match. Otherwise, we call QueryInterface on mURI and return. +// The reason for this specialized QueryInterface implementation is that we +// we want to be able to instantiate the mutator for a given CID of a +// nsIURI implementation, call nsISerializable.Read() on the mutator to +// deserialize the URI then QueryInterface the mutator to an nsIURI interface. +// See bug 1442239. +// If you QueryInterface a mutator to an interface of the URI +// implementation this is similar to calling Finalize. +#define NS_IMPL_NSIURIMUTATOR_QUERY_INTERFACE(aClass, ...) \ + static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \ + "Need more arguments"); \ + NS_INTERFACE_MAP_BEGIN(aClass) \ + nsCOMPtr<nsIURI> uri; \ + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIMutator) \ + MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \ + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { \ + foundInterface = nullptr; \ + } else \ + if (mURI && \ + NS_SUCCEEDED(mURI->QueryInterface(aIID, getter_AddRefs(uri)))) { \ + mURI = nullptr; \ + foundInterface = uri.get(); \ + } else \ + NS_INTERFACE_MAP_END \ + +%} + +[ptr] native Encoding(const mozilla::Encoding); +[ref] native const_URIParams_ref(const mozilla::ipc::URIParams); + +[scriptable, builtinclass, uuid(1fc53257-898b-4c5e-b69c-05bc84b4cd8f)] +interface nsIURISetSpec : nsISupports +{ + /** + * This setter is different from all other setters because it may be used to + * initialize the object. We define it separately allowing mutator implementors + * to define it separately, while the rest of the setters may be simply + * forwarded to the mutable URI. + */ + [must_use] nsIURIMutator setSpec(in AUTF8String aSpec); +}; + +/** + * These methods allow the mutator to change various parts of the URI. + * They return the same nsIURIMutator so that we may chain setter operations: + * Example: + * let newURI = uri.mutate() + * .setSpec("http://example.com") + * .setQuery("hello") + * .finalize(); + */ +[scriptable, builtinclass, uuid(5403a6ec-99d7-405e-8b45-9f805bbdfcef)] +interface nsIURISetters : nsIURISetSpec +{ + /** + * Setting the scheme outside of a protocol handler implementation is highly + * discouraged since that will generally lead to incorrect results. + */ + [must_use] nsIURIMutator setScheme(in AUTF8String aScheme); + [must_use] nsIURIMutator setUserPass(in AUTF8String aUserPass); + [must_use] nsIURIMutator setUsername(in AUTF8String aUsername); + [must_use] nsIURIMutator setPassword(in AUTF8String aPassword); + + /** + * If you setHostPort to a value that only has a host part, the port + * will not be reset. To reset the port set it to -1 beforehand. + * If setting the host succeeds, this method will return NS_OK, even if + * setting the port fails (error in parsing the port, or value out of range) + */ + [must_use] nsIURIMutator setHostPort(in AUTF8String aHostPort); + [must_use] nsIURIMutator setHost(in AUTF8String aHost); + [must_use] nsIURIMutator setPort(in long aPort); + [must_use] nsIURIMutator setPathQueryRef(in AUTF8String aPathQueryRef); + [must_use] nsIURIMutator setRef(in AUTF8String aRef); + [must_use] nsIURIMutator setFilePath(in AUTF8String aFilePath); + [must_use] nsIURIMutator setQuery(in AUTF8String aQuery); + [must_use, noscript] nsIURIMutator setQueryWithEncoding(in AUTF8String query, in Encoding encoding); +}; + +%{C++ + +// Using this macro instead of NS_FORWARD_SAFE_NSIURISETTERS makes chaining +// setter operations possible. +#define NS_FORWARD_SAFE_NSIURISETTERS_RET(_to) \ + [[nodiscard]] NS_IMETHOD \ + SetScheme(const nsACString& aScheme, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetScheme(aScheme); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetUserPass(const nsACString& aUserPass, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetUserPass(aUserPass); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetUsername(const nsACString& aUsername, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetUsername(aUsername); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetPassword(const nsACString& aPassword, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetPassword(aPassword); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetHostPort(const nsACString& aHostPort, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetHostPort(aHostPort); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetHost(const nsACString& aHost, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetHost(aHost); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetPort(int32_t aPort, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetPort(aPort); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetPathQueryRef(const nsACString& aPathQueryRef, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetPathQueryRef(aPathQueryRef); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetRef(const nsACString& aRef, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetRef(aRef); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetFilePath(const nsACString& aFilePath, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetFilePath(aFilePath); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetQuery(const nsACString& aQuery, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetQuery(aQuery); \ + } \ + [[nodiscard]] NS_IMETHOD \ + SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding, nsIURIMutator** aMutator) override \ + { \ + if (aMutator) NS_ADDREF(*aMutator = this); \ + return !_to ? NS_ERROR_NULL_POINTER : _to->SetQueryWithEncoding(query, encoding); \ + } \ + +%} + +[scriptable, builtinclass, uuid(4d1f3103-1c44-4dcd-b717-5d22a697a7d9)] +interface nsIURIMutator : nsIURISetters +{ + /** + * Initalizes the URI by reading IPC URIParams. + * See nsIURI. + */ + [noscript, notxpcom, must_use] + nsresult deserialize(in const_URIParams_ref aParams); + + /** + * Finishes changing or constructing the URI and returns an immutable URI. + */ + [must_use] + nsIURI finalize(); +}; + +%{C++ + +// This templated struct is used to extract the class type of the method +// passed to NS_MutatorMethod. +template <typename Method> +struct nsMethodTypeTraits; + +template <class C, typename R, typename... As> +struct nsMethodTypeTraits<R(C::*)(As...)> +{ + typedef C class_type; +}; + +#ifdef NS_HAVE_STDCALL +template <class C, typename R, typename... As> +struct nsMethodTypeTraits<R(__stdcall C::*)(As...)> +{ + typedef C class_type; +}; +#endif + +// This helper returns a std::function that will be applied on the +// nsIURIMutator. The type of `Interface` will be deduced from the method type. +// aMethod will be called on the target object if it successfully QIs to +// `Interface`, and the arguments will be passed to the call. +template <typename Method, typename... Args> +const std::function<nsresult(nsIURIMutator*)> +NS_MutatorMethod(Method aMethod, Args ...aArgs) +{ + // Capture arguments by value, otherwise we crash. + return [=](nsIURIMutator* aMutator) { + typedef typename nsMethodTypeTraits<Method>::class_type Interface; + nsresult rv; + nsCOMPtr<Interface> target = do_QueryInterface(aMutator, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = (target->*aMethod)(aArgs...); + if (NS_FAILED(rv)) return rv; + return NS_OK; + }; +} + +// This class provides a useful helper that allows chaining of setter operations +class MOZ_STACK_CLASS NS_MutateURI +{ +public: + explicit NS_MutateURI(nsIURI* aURI); + explicit NS_MutateURI(const char * aContractID); + + explicit NS_MutateURI(nsIURIMutator* m) + { + mStatus = m ? NS_OK : NS_ERROR_NULL_POINTER; + mMutator = m; + NS_ENSURE_SUCCESS_VOID(mStatus); + } + + NS_MutateURI& SetSpec(const nsACString& aSpec) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetSpec(aSpec, nullptr); + return *this; + } + NS_MutateURI& SetScheme(const nsACString& aScheme) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetScheme(aScheme, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetUserPass(const nsACString& aUserPass) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetUserPass(aUserPass, nullptr); + return *this; + } + NS_MutateURI& SetUsername(const nsACString& aUsername) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetUsername(aUsername, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetPassword(const nsACString& aPassword) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetPassword(aPassword, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetHostPort(const nsACString& aHostPort) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetHostPort(aHostPort, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetHost(const nsACString& aHost) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetHost(aHost, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetPort(int32_t aPort) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetPort(aPort, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetPathQueryRef(const nsACString& aPathQueryRef) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetPathQueryRef(aPathQueryRef, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetRef(const nsACString& aRef) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetRef(aRef, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetFilePath(const nsACString& aFilePath) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetFilePath(aFilePath, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetQuery(const nsACString& aQuery) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetQuery(aQuery, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + NS_MutateURI& SetQueryWithEncoding(const nsACString& query, const mozilla::Encoding *encoding) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = mMutator->SetQueryWithEncoding(query, encoding, nullptr); + NS_ENSURE_SUCCESS(mStatus, *this); + return *this; + } + + /** + * This method allows consumers to call the methods declared in other + * interfaces implemented by the mutator object. + * + * Example: + * nsCOMPtr<nsIURI> uri; + * nsresult rv = NS_MutateURI(new URIClass::Mutator()) + * .SetSpec(aSpec) + * .Apply(NS_MutatorMethod(&SomeInterface::Method, arg1, arg2)) + * .Finalize(uri); + * + * If mMutator does not implement SomeInterface, do_QueryInterface will fail + * and the method will not be called. + * If aMethod does not exist, or if there is a mismatch between argument + * types, or the number of arguments, then there will be a compile error. + */ + NS_MutateURI& Apply(const std::function<nsresult(nsIURIMutator*)>& aFunction) + { + if (NS_FAILED(mStatus)) { + return *this; + } + mStatus = aFunction(mMutator); + return *this; + } + + template <class C> + [[nodiscard]] nsresult Finalize(nsCOMPtr<C>& aURI) + { + NS_ENSURE_SUCCESS(mStatus, mStatus); + + nsCOMPtr<nsIURI> uri; + mStatus = mMutator->Finalize(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + aURI = do_QueryInterface(uri, &mStatus); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. + return NS_OK; + } + + // Overload for nsIURI to avoid query interface. + [[nodiscard]] nsresult Finalize(nsCOMPtr<nsIURI>& aURI) + { + if (NS_FAILED(mStatus)) return mStatus; + mStatus = mMutator->Finalize(getter_AddRefs(aURI)); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. + return NS_OK; + } + + template <class C> + [[nodiscard]] nsresult Finalize(C** aURI) + { + NS_ENSURE_SUCCESS(mStatus, mStatus); + + nsCOMPtr<nsIURI> uri; + mStatus = mMutator->Finalize(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + nsCOMPtr<C> result = do_QueryInterface(uri, &mStatus); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + result.forget(aURI); + mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. + return NS_OK; + } + + [[nodiscard]] nsresult Finalize(nsIURI** aURI) + { + if (NS_FAILED(mStatus)) return mStatus; + mStatus = mMutator->Finalize(aURI); + NS_ENSURE_SUCCESS(mStatus, mStatus); + + mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail. + return NS_OK; + } + + nsresult GetStatus() { return mStatus; } +private: + nsresult mStatus; + nsCOMPtr<nsIURIMutator> mMutator; +}; + +%} |