/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/ipc/ProtocolUtils.h" #include "mozilla/Logging.h" #include "HandlerServiceParent.h" #include "nsIHandlerService.h" #include "nsIMIMEInfo.h" #include "nsIMIMEService.h" #include "ContentHandlerService.h" #include "nsIExternalProtocolService.h" #include "nsStringEnumerator.h" #include "nsIMutableArray.h" #include "nsCExternalHandlerService.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #ifdef MOZ_WIDGET_GTK # include "unix/nsGNOMERegistry.h" #endif using mozilla::dom::ContentHandlerService; using mozilla::dom::HandlerApp; using mozilla::dom::HandlerInfo; using mozilla::dom::RemoteHandlerApp; namespace { class ProxyHandlerInfo final : public nsIHandlerInfo { public: explicit ProxyHandlerInfo(const HandlerInfo& aHandlerInfo); NS_DECL_ISUPPORTS; NS_DECL_NSIHANDLERINFO; nsTArray& Extensions() { return mHandlerInfo.extensions(); } protected: ~ProxyHandlerInfo() {} HandlerInfo mHandlerInfo; nsHandlerInfoAction mPrefAction; nsCOMPtr mPossibleApps; }; NS_IMPL_ISUPPORTS(ProxyHandlerInfo, nsIHandlerInfo) ProxyHandlerInfo::ProxyHandlerInfo(const HandlerInfo& aHandlerInfo) : mHandlerInfo(aHandlerInfo), mPrefAction(nsIHandlerInfo::alwaysAsk), mPossibleApps(do_CreateInstance(NS_ARRAY_CONTRACTID)) { for (auto& happ : aHandlerInfo.possibleApplicationHandlers()) { mPossibleApps->AppendElement(new RemoteHandlerApp(happ)); } } /* readonly attribute ACString type; */ NS_IMETHODIMP ProxyHandlerInfo::GetType(nsACString& aType) { aType.Assign(mHandlerInfo.type()); return NS_OK; } /* attribute AString description; */ NS_IMETHODIMP ProxyHandlerInfo::GetDescription(nsAString& aDescription) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP ProxyHandlerInfo::SetDescription(const nsAString& aDescription) { return NS_ERROR_NOT_IMPLEMENTED; } /* attribute nsIHandlerApp preferredApplicationHandler; */ NS_IMETHODIMP ProxyHandlerInfo::GetPreferredApplicationHandler( nsIHandlerApp** aPreferredApplicationHandler) { *aPreferredApplicationHandler = new RemoteHandlerApp(mHandlerInfo.preferredApplicationHandler()); NS_IF_ADDREF(*aPreferredApplicationHandler); return NS_OK; } NS_IMETHODIMP ProxyHandlerInfo::SetPreferredApplicationHandler( nsIHandlerApp* aApp) { nsString name; nsString detailedDescription; if (aApp) { aApp->GetName(name); aApp->GetDetailedDescription(detailedDescription); } mHandlerInfo.preferredApplicationHandler() = HandlerApp(name, detailedDescription); return NS_OK; } /* readonly attribute nsIMutableArray possibleApplicationHandlers; */ NS_IMETHODIMP ProxyHandlerInfo::GetPossibleApplicationHandlers( nsIMutableArray** aPossibleApplicationHandlers) { *aPossibleApplicationHandlers = mPossibleApps; NS_IF_ADDREF(*aPossibleApplicationHandlers); return NS_OK; } /* readonly attribute boolean hasDefaultHandler; */ NS_IMETHODIMP ProxyHandlerInfo::GetHasDefaultHandler(bool* aHasDefaultHandler) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute AString defaultDescription; */ NS_IMETHODIMP ProxyHandlerInfo::GetDefaultDescription( nsAString& aDefaultDescription) { return NS_ERROR_NOT_IMPLEMENTED; } /* void launchWithURI (in nsIURI aURI, [optional] in BrowsingContext aBrowsingContext); */ NS_IMETHODIMP ProxyHandlerInfo::LaunchWithURI( nsIURI* aURI, mozilla::dom::BrowsingContext* aBrowsingContext) { return NS_ERROR_NOT_IMPLEMENTED; } /* attribute ProxyHandlerInfoAction preferredAction; */ NS_IMETHODIMP ProxyHandlerInfo::GetPreferredAction( nsHandlerInfoAction* aPreferredAction) { *aPreferredAction = mPrefAction; return NS_OK; } NS_IMETHODIMP ProxyHandlerInfo::SetPreferredAction( nsHandlerInfoAction aPreferredAction) { mHandlerInfo.preferredAction() = aPreferredAction; mPrefAction = aPreferredAction; return NS_OK; } /* attribute boolean alwaysAskBeforeHandling; */ NS_IMETHODIMP ProxyHandlerInfo::GetAlwaysAskBeforeHandling( bool* aAlwaysAskBeforeHandling) { *aAlwaysAskBeforeHandling = mHandlerInfo.alwaysAskBeforeHandling(); return NS_OK; } NS_IMETHODIMP ProxyHandlerInfo::SetAlwaysAskBeforeHandling( bool aAlwaysAskBeforeHandling) { mHandlerInfo.alwaysAskBeforeHandling() = aAlwaysAskBeforeHandling; return NS_OK; } class ProxyMIMEInfo : public nsIMIMEInfo { public: NS_DECL_ISUPPORTS NS_DECL_NSIMIMEINFO NS_FORWARD_NSIHANDLERINFO(mProxyHandlerInfo->); explicit ProxyMIMEInfo(const HandlerInfo& aHandlerInfo) : mProxyHandlerInfo(new ProxyHandlerInfo(aHandlerInfo)) {} private: virtual ~ProxyMIMEInfo() {} RefPtr mProxyHandlerInfo; protected: /* additional members */ }; NS_IMPL_ISUPPORTS(ProxyMIMEInfo, nsIMIMEInfo, nsIHandlerInfo) /* nsIUTF8StringEnumerator getFileExtensions (); */ NS_IMETHODIMP ProxyMIMEInfo::GetFileExtensions( nsIUTF8StringEnumerator** _retval) { return NS_NewUTF8StringEnumerator(_retval, &mProxyHandlerInfo->Extensions(), this); } /* void setFileExtensions (in AUTF8String aExtensions); */ NS_IMETHODIMP ProxyMIMEInfo::SetFileExtensions(const nsACString& aExtensions) { return NS_ERROR_NOT_IMPLEMENTED; } /* boolean extensionExists (in AUTF8String aExtension); */ NS_IMETHODIMP ProxyMIMEInfo::ExtensionExists(const nsACString& aExtension, bool* _retval) { *_retval = mProxyHandlerInfo->Extensions().Contains( aExtension, nsCaseInsensitiveCStringArrayComparator()); return NS_OK; } /* void appendExtension (in AUTF8String aExtension); */ NS_IMETHODIMP ProxyMIMEInfo::AppendExtension(const nsACString& aExtension) { if (!aExtension.IsEmpty() && !mProxyHandlerInfo->Extensions().Contains( aExtension, nsCaseInsensitiveCStringArrayComparator())) { mProxyHandlerInfo->Extensions().AppendElement(aExtension); } return NS_OK; } /* attribute AUTF8String primaryExtension; */ NS_IMETHODIMP ProxyMIMEInfo::GetPrimaryExtension( nsACString& aPrimaryExtension) { const auto& extensions = mProxyHandlerInfo->Extensions(); if (extensions.IsEmpty()) { aPrimaryExtension.Truncate(); return NS_ERROR_FAILURE; } aPrimaryExtension = extensions[0]; return NS_OK; } NS_IMETHODIMP ProxyMIMEInfo::SetPrimaryExtension( const nsACString& aPrimaryExtension) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute ACString MIMEType; */ NS_IMETHODIMP ProxyMIMEInfo::GetMIMEType(nsACString& aMIMEType) { return NS_ERROR_NOT_IMPLEMENTED; } /* boolean equals (in nsIMIMEInfo aMIMEInfo); */ NS_IMETHODIMP ProxyMIMEInfo::Equals(nsIMIMEInfo* aMIMEInfo, bool* _retval) { return NS_ERROR_NOT_IMPLEMENTED; } /* readonly attribute nsIArray possibleLocalHandlers; */ NS_IMETHODIMP ProxyMIMEInfo::GetPossibleLocalHandlers( nsIArray** aPossibleLocalHandlers) { return NS_ERROR_NOT_IMPLEMENTED; } /* void launchWithFile (in nsIFile aFile); */ NS_IMETHODIMP ProxyMIMEInfo::LaunchWithFile(nsIFile* aFile) { return NS_ERROR_NOT_IMPLEMENTED; } /* boolean isCurrentAppOSDefault(); */ NS_IMETHODIMP ProxyMIMEInfo::IsCurrentAppOSDefault(bool* _retval) { return NS_ERROR_NOT_IMPLEMENTED; } static already_AddRefed WrapHandlerInfo( const HandlerInfo& aHandlerInfo) { nsCOMPtr info; if (aHandlerInfo.isMIMEInfo()) { info = new ProxyMIMEInfo(aHandlerInfo); } else { info = new ProxyHandlerInfo(aHandlerInfo); } return info.forget(); } } // anonymous namespace HandlerServiceParent::HandlerServiceParent() {} HandlerServiceParent::~HandlerServiceParent() {} mozilla::ipc::IPCResult HandlerServiceParent::RecvFillHandlerInfo( const HandlerInfo& aHandlerInfoData, const nsACString& aOverrideType, HandlerInfo* handlerInfoData) { nsCOMPtr info(WrapHandlerInfo(aHandlerInfoData)); nsCOMPtr handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID); handlerSvc->FillHandlerInfo(info, aOverrideType); ContentHandlerService::nsIHandlerInfoToHandlerInfo(info, handlerInfoData); return IPC_OK(); } mozilla::ipc::IPCResult HandlerServiceParent::RecvGetMIMEInfoFromOS( const nsACString& aMIMEType, const nsACString& aExtension, nsresult* aRv, HandlerInfo* aHandlerInfoData, bool* aFound) { *aFound = false; if (aMIMEType.Length() > MAX_MIMETYPE_LENGTH || aExtension.Length() > MAX_EXT_LENGTH) { *aRv = NS_OK; return IPC_OK(); } nsCOMPtr mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID, aRv); if (NS_WARN_IF(NS_FAILED(*aRv))) { return IPC_OK(); } nsCOMPtr mimeInfo; *aRv = mimeService->GetMIMEInfoFromOS(aMIMEType, aExtension, aFound, getter_AddRefs(mimeInfo)); if (NS_WARN_IF(NS_FAILED(*aRv))) { return IPC_OK(); } if (mimeInfo) { ContentHandlerService::nsIHandlerInfoToHandlerInfo(mimeInfo, aHandlerInfoData); } return IPC_OK(); } mozilla::ipc::IPCResult HandlerServiceParent::RecvExists( const HandlerInfo& aHandlerInfo, bool* exists) { nsCOMPtr info(WrapHandlerInfo(aHandlerInfo)); nsCOMPtr handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID); handlerSvc->Exists(info, exists); return IPC_OK(); } mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS( const nsACString& aProtocolScheme, bool* aHandlerExists) { if (aProtocolScheme.Length() > MAX_SCHEME_LENGTH) { *aHandlerExists = false; return IPC_OK(); } #ifdef MOZ_WIDGET_GTK // Check the GNOME registry for a protocol handler *aHandlerExists = nsGNOMERegistry::HandlerExists(PromiseFlatCString(aProtocolScheme).get()); #else *aHandlerExists = false; #endif return IPC_OK(); } /* * Check if a handler exists for the provided protocol. Check the datastore * first and then fallback to checking the OS for a handler. */ mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocol( const nsACString& aProtocolScheme, bool* aHandlerExists) { if (aProtocolScheme.Length() > MAX_SCHEME_LENGTH) { *aHandlerExists = false; return IPC_OK(); } // Check the datastore and fallback to an OS check. // ExternalProcotolHandlerExists() does the fallback. nsresult rv; nsCOMPtr protoSvc = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { *aHandlerExists = false; return IPC_OK(); } rv = protoSvc->ExternalProtocolHandlerExists( PromiseFlatCString(aProtocolScheme).get(), aHandlerExists); if (NS_WARN_IF(NS_FAILED(rv))) { *aHandlerExists = false; } return IPC_OK(); } mozilla::ipc::IPCResult HandlerServiceParent::RecvGetTypeFromExtension( const nsACString& aFileExtension, nsCString* type) { if (aFileExtension.Length() > MAX_EXT_LENGTH) { return IPC_OK(); } nsresult rv; nsCOMPtr handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return IPC_OK(); } rv = handlerSvc->GetTypeFromExtension(aFileExtension, *type); mozilla::Unused << NS_WARN_IF(NS_FAILED(rv)); return IPC_OK(); } mozilla::ipc::IPCResult HandlerServiceParent::RecvGetApplicationDescription( const nsACString& aScheme, nsresult* aRv, nsString* aDescription) { if (aScheme.Length() > MAX_SCHEME_LENGTH) { *aRv = NS_ERROR_NOT_AVAILABLE; return IPC_OK(); } nsCOMPtr protoSvc = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID); NS_ASSERTION(protoSvc, "No Helper App Service!"); *aRv = protoSvc->GetApplicationDescription(aScheme, *aDescription); return IPC_OK(); } void HandlerServiceParent::ActorDestroy(ActorDestroyReason aWhy) {}