Bug 1452278 - Part 1 - Make macOS nsOSHelperAppService::GetFromTypeAndExtension() not call OS MIME API's in content r=bzbarsky,froydnj
authorHaik Aftandilian <haftandilian@mozilla.com>
Wed, 27 Mar 2019 22:49:33 +0000
changeset 466483 7d40ca9aee44642578636744d9f44d298031ff8c
parent 466482 2b0b4377cc2bf358d49240bdf0b776276c760849
child 466484 09efa187c2cdd1d49a1726f7188858a412851cc5
push id35768
push useropoprus@mozilla.com
push dateThu, 28 Mar 2019 09:55:54 +0000
treeherdermozilla-central@c045dd97faf2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, froydnj
bugs1452278
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1452278 - Part 1 - Make macOS nsOSHelperAppService::GetFromTypeAndExtension() not call OS MIME API's in content r=bzbarsky,froydnj Add a new nsExternalHelperAppService derived class named nsOSHelperAppServiceChild to be used for the MIME service, external helper app service, and external protocol service interfaces in child processes. nsOSHelperAppServiceChild overrides some methods used to get MIME and external protocol handler information from the OS and implements these methods by remoting the calls to the parent process. This is necessary because, on Mac, querying the OS for helper application info from sandboxed content processes is unreliable and has buggy side effects. For now, only use the new class on Mac. Android and unix file changes r+ by gcp. Windows files changes r+ by bobowen. Sync messages review r+ by nfroyd. MozReview-Commit-ID: 63BiS6VCxfn Differential Revision: https://phabricator.services.mozilla.com/D15620
docshell/build/components.conf
ipc/ipdl/sync-messages.ini
netwerk/mime/nsIMIMEService.idl
uriloader/exthandler/ContentHandlerService.cpp
uriloader/exthandler/HandlerServiceParent.cpp
uriloader/exthandler/HandlerServiceParent.h
uriloader/exthandler/PHandlerService.ipdl
uriloader/exthandler/android/nsOSHelperAppService.cpp
uriloader/exthandler/android/nsOSHelperAppService.h
uriloader/exthandler/mac/nsOSHelperAppService.h
uriloader/exthandler/mac/nsOSHelperAppService.mm
uriloader/exthandler/moz.build
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsIHandlerService.idl
uriloader/exthandler/nsMIMEInfoChild.h
uriloader/exthandler/nsOSHelperAppServiceChild.cpp
uriloader/exthandler/nsOSHelperAppServiceChild.h
uriloader/exthandler/uikit/nsOSHelperAppService.h
uriloader/exthandler/uikit/nsOSHelperAppService.mm
uriloader/exthandler/unix/nsOSHelperAppService.cpp
uriloader/exthandler/unix/nsOSHelperAppService.h
uriloader/exthandler/win/nsOSHelperAppService.cpp
uriloader/exthandler/win/nsOSHelperAppService.h
--- a/docshell/build/components.conf
+++ b/docshell/build/components.conf
@@ -66,18 +66,19 @@ Classes = [
     },
     {
         'cid': '{a7f800e0-4306-11d4-98d0-001083010e9b}',
         'contract_ids': [
             '@mozilla.org/mime;1',
             '@mozilla.org/uriloader/external-helper-app-service;1',
             '@mozilla.org/uriloader/external-protocol-service;1',
         ],
-        'type': 'nsOSHelperAppService',
-        'headers': ['nsOSHelperAppService.h'],
+        'type': 'nsExternalHelperAppService',
+        'constructor': 'nsExternalHelperAppService::GetSingleton',
+        'headers': ['nsExternalHelperAppService.h'],
         'init_method': 'Init',
         'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
     },
     {
         'cid': '{397b43f3-1470-4542-8a40-c718f7753563}',
         'contract_ids': ['@mozilla.org/network/childProcessChannelListener;1'],
         'singleton': True,
         'type': 'mozilla::dom::ChildProcessChannelListener',
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -1044,20 +1044,26 @@ description =
 [PHal::GetWakeLockInfo]
 description =
 [PHal::LockScreenOrientation]
 description =
 [PPrinting::SavePrintSettings]
 description =
 [PHandlerService::FillHandlerInfo]
 description =
+[PHandlerService::GetMIMEInfoFromOS]
+description = Lets unprivileged child processes synchronously get MIME type/handler information from the OS
+[PHandlerService::ExistsForProtocolOS]
+description = bug 1382323
 [PHandlerService::ExistsForProtocol]
-description = bug 1382323
+description =
 [PHandlerService::Exists]
 description =
 [PHandlerService::GetTypeFromExtension]
 description =
+[PHandlerService::GetApplicationDescription]
+description = Lets unprivileged child processes synchronously get a description of the app that handles a given protocol scheme
 [PLayerTransaction::ShutdownSync]
 description = bug 1363126
 [PClientSource::WorkerSyncPing]
 description = Synchronous ping allowing worker thread to confirm actor is created. Necessary to avoid racing with ClientHandle actors on main thread.
 [PRemoteSandboxBroker::LaunchApp]
 description = Synchronous launch of a child process that in turn launches and sandboxes another process. Called on a dedicated thread and targets a dedicated process, so this shouldn't block anything.
--- a/netwerk/mime/nsIMIMEService.idl
+++ b/netwerk/mime/nsIMIMEService.idl
@@ -67,9 +67,34 @@ interface nsIMIMEService : nsISupports {
     /**
      * Given a Type/Extension combination, returns the default extension
      * for this type. This may be identical to the passed-in extension.
      *
      * @param aMIMEType The Type to get information on. Must not be empty.
      * @param aFileExt  File Extension. Can be empty.
      */
     AUTF8String getPrimaryExtension(in ACString aMIMEType, in AUTF8String aFileExt);
+
+    /*
+     * Returns an nsIMIMEInfo for the provided MIME type and extension
+     * obtained from an OS lookup. If no handler is found for the type and
+     * extension, returns a generic nsIMIMEInfo object. The MIME type and
+     * extension can be the empty string. When the type and extension don't
+     * map to the same handler, the semantics/resolution are platform
+     * specific. See the platform implementations for details.
+     *
+     * @param aType           The MIME type to get handler information for.
+     * @param aFileExtension  The filename extension to use either alone
+     *                        or with the MIME type to get handler information
+     *                        for. UTF-8 encoded.
+     * @param [out] aFound    Out param indicating whether a MIMEInfo could
+     *                        be found for the provided type and/or extension.
+     *                        Set to false when neither extension nor the MIME
+     *                        type are mapped to a handler.
+     * @return                A nsIMIMEInfo object. This function must return
+     *                        a MIMEInfo object if it can allocate one. The
+     *                        only justifiable reason for not returning one is
+     *                        an out-of-memory error.
+     */
+    nsIMIMEInfo getMIMEInfoFromOS(in ACString aType,
+                                  in ACString aFileExtension,
+                                  out boolean aFound);
 };
--- a/uriloader/exthandler/ContentHandlerService.cpp
+++ b/uriloader/exthandler/ContentHandlerService.cpp
@@ -161,16 +161,35 @@ NS_IMETHODIMP ContentHandlerService::Fil
   HandlerInfo info, returnedInfo;
   nsIHandlerInfoToHandlerInfo(aHandlerInfo, &info);
   mHandlerServiceChild->SendFillHandlerInfo(info, nsCString(aOverrideType),
                                             &returnedInfo);
   CopyHanderInfoTonsIHandlerInfo(returnedInfo, aHandlerInfo);
   return NS_OK;
 }
 
+NS_IMETHODIMP ContentHandlerService::GetMIMEInfoFromOS(
+    nsIHandlerInfo* aHandlerInfo, const nsACString& aMIMEType,
+    const nsACString& aExtension, bool* aFound) {
+  nsresult rv = NS_ERROR_FAILURE;
+  HandlerInfo returnedInfo;
+  if (!mHandlerServiceChild->SendGetMIMEInfoFromOS(nsCString(aMIMEType),
+                                                   nsCString(aExtension), &rv,
+                                                   &returnedInfo, aFound)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  CopyHanderInfoTonsIHandlerInfo(returnedInfo, aHandlerInfo);
+  return NS_OK;
+}
+
 NS_IMETHODIMP ContentHandlerService::Store(nsIHandlerInfo* aHandlerInfo) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP ContentHandlerService::Exists(nsIHandlerInfo* aHandlerInfo,
                                             bool* _retval) {
   HandlerInfo info;
   nsIHandlerInfoToHandlerInfo(aHandlerInfo, &info);
@@ -178,16 +197,26 @@ NS_IMETHODIMP ContentHandlerService::Exi
   return NS_OK;
 }
 
 NS_IMETHODIMP ContentHandlerService::Remove(nsIHandlerInfo* aHandlerInfo) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+ContentHandlerService::ExistsForProtocolOS(const nsACString& aProtocolScheme,
+                                           bool* aRetval) {
+  if (!mHandlerServiceChild->SendExistsForProtocolOS(nsCString(aProtocolScheme),
+                                                     aRetval)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 ContentHandlerService::ExistsForProtocol(const nsACString& aProtocolScheme,
                                          bool* aRetval) {
   if (!mHandlerServiceChild->SendExistsForProtocol(nsCString(aProtocolScheme),
                                                    aRetval)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
@@ -203,10 +232,20 @@ NS_IMETHODIMP ContentHandlerService::Get
   mHandlerServiceChild->SendGetTypeFromExtension(nsCString(aFileExtension),
                                                  &type);
   _retval.Assign(type);
   mExtToTypeMap.Put(nsCString(aFileExtension), new nsCString(type));
 
   return NS_OK;
 }
 
+NS_IMETHODIMP ContentHandlerService::GetApplicationDescription(
+    const nsACString& aProtocolScheme, nsAString& aRetVal) {
+  nsresult rv = NS_ERROR_FAILURE;
+  nsAutoCString scheme(aProtocolScheme);
+  nsAutoString desc;
+  mHandlerServiceChild->SendGetApplicationDescription(scheme, &rv, &desc);
+  aRetVal.Assign(desc);
+  return rv;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/uriloader/exthandler/HandlerServiceParent.cpp
+++ b/uriloader/exthandler/HandlerServiceParent.cpp
@@ -1,8 +1,9 @@
+#include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/Logging.h"
 #include "HandlerServiceParent.h"
 #include "nsIHandlerService.h"
 #include "nsIMIMEInfo.h"
 #include "ContentHandlerService.h"
 #include "nsStringEnumerator.h"
 #ifdef MOZ_WIDGET_GTK
 #  include "unix/nsGNOMERegistry.h"
@@ -233,37 +234,107 @@ mozilla::ipc::IPCResult HandlerServicePa
   nsCOMPtr<nsIHandlerInfo> info(WrapHandlerInfo(aHandlerInfoData));
   nsCOMPtr<nsIHandlerService> handlerSvc =
       do_GetService(NS_HANDLERSERVICE_CONTRACTID);
   handlerSvc->FillHandlerInfo(info, aOverrideType);
   ContentHandlerService::nsIHandlerInfoToHandlerInfo(info, handlerInfoData);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult HandlerServiceParent::RecvGetMIMEInfoFromOS(
+    const nsCString& aMIMEType, const nsCString& aExtension, nsresult* aRv,
+    HandlerInfo* aHandlerInfoData, bool* aFound) {
+  *aFound = false;
+  nsCOMPtr<nsIMIMEService> mimeService =
+      do_GetService(NS_MIMESERVICE_CONTRACTID, aRv);
+  if (NS_WARN_IF(NS_FAILED(*aRv))) {
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsIMIMEInfo> 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<nsIHandlerInfo> info(WrapHandlerInfo(aHandlerInfo));
   nsCOMPtr<nsIHandlerService> handlerSvc =
       do_GetService(NS_HANDLERSERVICE_CONTRACTID);
   handlerSvc->Exists(info, exists);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocol(
+mozilla::ipc::IPCResult HandlerServiceParent::RecvExistsForProtocolOS(
     const nsCString& aProtocolScheme, bool* aHandlerExists) {
 #ifdef MOZ_WIDGET_GTK
   // Check the GNOME registry for a protocol handler
   *aHandlerExists = nsGNOMERegistry::HandlerExists(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 nsCString& aProtocolScheme, bool* aHandlerExists) {
+#if defined(XP_MACOSX)
+  // Check the datastore and fallback to an OS check.
+  // ExternalProcotolHandlerExists() does the fallback.
+  nsresult rv;
+  nsCOMPtr<nsIExternalProtocolService> protoSvc =
+      do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    *aHandlerExists = false;
+    return IPC_OK();
+  }
+  rv = protoSvc->ExternalProtocolHandlerExists(aProtocolScheme.get(),
+                                               aHandlerExists);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    *aHandlerExists = false;
+  }
+#else
+  MOZ_RELEASE_ASSERT(false, "No implementation on this platform.");
+  *aHandlerExists = false;
+#endif
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult HandlerServiceParent::RecvGetTypeFromExtension(
     const nsCString& aFileExtension, nsCString* type) {
+  nsresult rv;
   nsCOMPtr<nsIHandlerService> handlerSvc =
-      do_GetService(NS_HANDLERSERVICE_CONTRACTID);
-  handlerSvc->GetTypeFromExtension(aFileExtension, *type);
+      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 nsCString& aScheme, nsresult* aRv, nsString* aDescription) {
+  nsCOMPtr<nsIExternalProtocolService> 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) {}
--- a/uriloader/exthandler/HandlerServiceParent.h
+++ b/uriloader/exthandler/HandlerServiceParent.h
@@ -8,24 +8,37 @@ class nsIHandlerApp;
 
 class HandlerServiceParent final : public mozilla::dom::PHandlerServiceParent {
  public:
   HandlerServiceParent();
   NS_INLINE_DECL_REFCOUNTING(HandlerServiceParent)
 
  private:
   virtual ~HandlerServiceParent();
-  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  virtual mozilla::ipc::IPCResult RecvFillHandlerInfo(
+  mozilla::ipc::IPCResult RecvFillHandlerInfo(
       const HandlerInfo& aHandlerInfoData, const nsCString& aOverrideType,
       HandlerInfo* handlerInfoData) override;
-  virtual mozilla::ipc::IPCResult RecvExists(const HandlerInfo& aHandlerInfo,
-                                             bool* exits) override;
 
-  virtual mozilla::ipc::IPCResult RecvGetTypeFromExtension(
+  mozilla::ipc::IPCResult RecvGetMIMEInfoFromOS(const nsCString& aMIMEType,
+                                                const nsCString& aExtension,
+                                                nsresult* aRv,
+                                                HandlerInfo* aHandlerInfoData,
+                                                bool* aFound) override;
+
+  mozilla::ipc::IPCResult RecvExists(const HandlerInfo& aHandlerInfo,
+                                     bool* exists) override;
+
+  mozilla::ipc::IPCResult RecvGetTypeFromExtension(
       const nsCString& aFileExtension, nsCString* type) override;
 
-  virtual mozilla::ipc::IPCResult RecvExistsForProtocol(
+  mozilla::ipc::IPCResult RecvExistsForProtocolOS(
       const nsCString& aProtocolScheme, bool* aHandlerExists) override;
+
+  mozilla::ipc::IPCResult RecvExistsForProtocol(
+      const nsCString& aProtocolScheme, bool* aHandlerExists) override;
+
+  mozilla::ipc::IPCResult RecvGetApplicationDescription(
+      const nsCString& aScheme, nsresult* aRv, nsString* aDescription) override;
 };
 
 #endif
--- a/uriloader/exthandler/PHandlerService.ipdl
+++ b/uriloader/exthandler/PHandlerService.ipdl
@@ -26,20 +26,36 @@ struct HandlerInfo {
 sync protocol PHandlerService
 {
   manager PContent;
 
 parent:
   sync FillHandlerInfo(HandlerInfo aHandlerInfoData,
                        nsCString aOverrideType)
       returns (HandlerInfo handlerInfoData);
+
+  /*
+   * Check if an OS handler exists for the given protocol scheme.
+   */
+  sync ExistsForProtocolOS(nsCString aProtocolScheme)
+      returns (bool exists);
+
+  /*
+   * Check if a handler exists for the given protocol scheme. Check
+   * the datastore first and then fallback to an OS handler check.
+   */
   sync ExistsForProtocol(nsCString aProtocolScheme)
       returns (bool exists);
+
   sync Exists(HandlerInfo aHandlerInfo)
       returns (bool exists);
   sync GetTypeFromExtension(nsCString aFileExtension)
       returns (nsCString type);
+  sync GetMIMEInfoFromOS(nsCString aMIMEType, nsCString aExtension)
+      returns (nsresult rv, HandlerInfo handlerInfoData, bool found);
+  sync GetApplicationDescription(nsCString aScheme)
+      returns (nsresult rv, nsString description);
   async __delete__();
 };
 
 
 } // namespace dom
 } // namespace mozilla
--- a/uriloader/exthandler/android/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/android/nsOSHelperAppService.cpp
@@ -6,32 +6,35 @@
 #include "nsOSHelperAppService.h"
 #include "nsMIMEInfoAndroid.h"
 #include "AndroidBridge.h"
 
 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService() {}
 
 nsOSHelperAppService::~nsOSHelperAppService() {}
 
-already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(
-    const nsACString& aMIMEType, const nsACString& aFileExt, bool* aFound) {
+nsresult nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                                                 const nsACString& aFileExt,
+                                                 bool* aFound,
+                                                 nsIMIMEInfo** aMIMEInfo) {
   RefPtr<nsMIMEInfoAndroid> mimeInfo;
   *aFound = false;
   if (!aMIMEType.IsEmpty())
     *aFound = nsMIMEInfoAndroid::GetMimeInfoForMimeType(
         aMIMEType, getter_AddRefs(mimeInfo));
   if (!*aFound)
     *aFound = nsMIMEInfoAndroid::GetMimeInfoForFileExt(
         aFileExt, getter_AddRefs(mimeInfo));
 
   // Code that calls this requires an object regardless if the OS has
   // something for us, so we return the empty object.
   if (!*aFound) mimeInfo = new nsMIMEInfoAndroid(aMIMEType);
 
-  return mimeInfo.forget();
+  mimeInfo.forget(aMIMEInfo);
+  return NS_OK;
 }
 
 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char* aScheme,
                                                        bool* aExists) {
   *aExists = mozilla::AndroidBridge::Bridge()->GetHandlersForURL(
       NS_ConvertUTF8toUTF16(aScheme));
   return NS_OK;
 }
--- a/uriloader/exthandler/android/nsOSHelperAppService.h
+++ b/uriloader/exthandler/android/nsOSHelperAppService.h
@@ -9,25 +9,26 @@
 #include "nsCExternalHandlerService.h"
 #include "nsExternalHelperAppService.h"
 
 class nsOSHelperAppService : public nsExternalHelperAppService {
  public:
   nsOSHelperAppService();
   virtual ~nsOSHelperAppService();
 
-  virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(
-      const nsACString& aMIMEType, const nsACString& aFileExt, bool* aFound);
+  nsresult GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                             const nsACString& aFileExt, bool* aFound,
+                             nsIMIMEInfo** aMIMEInfo) override;
 
-  virtual MOZ_MUST_USE nsresult OSProtocolHandlerExists(const char* aScheme,
-                                                        bool* aExists);
+  MOZ_MUST_USE nsresult OSProtocolHandlerExists(const char* aScheme,
+                                                bool* aExists) override;
 
   NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                           bool* found,
-                                          nsIHandlerInfo** _retval);
+                                          nsIHandlerInfo** _retval) override;
 
   static nsIHandlerApp* CreateAndroidHandlerApp(
       const nsAString& aName, const nsAString& aDescription,
       const nsAString& aPackageName, const nsAString& aClassName,
       const nsACString& aMimeType, const nsAString& aAction = EmptyString());
 };
 
 #endif /* nsOSHelperAppService_h */
--- a/uriloader/exthandler/mac/nsOSHelperAppService.h
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.h
@@ -21,37 +21,30 @@ class nsIMimeInfo;
 class nsOSHelperAppService : public nsExternalHelperAppService {
  public:
   virtual ~nsOSHelperAppService();
 
   // override nsIExternalProtocolService methods
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme,
                                        nsAString& _retval) override;
 
-  // method overrides --> used to hook the mime service into internet config....
-  NS_IMETHOD GetFromTypeAndExtension(const nsACString& aType,
-                                     const nsACString& aFileExt,
-                                     nsIMIMEInfo** aMIMEInfo) override;
-  already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
-                                                  const nsACString& aFileExt,
-                                                  bool* aFound) override;
+  nsresult GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                             const nsACString& aFileExt, bool* aFound,
+                             nsIMIMEInfo** aMIMEInfo) override;
+
   NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                           bool* found,
                                           nsIHandlerInfo** _retval) override;
 
-  // override so we can have a child process sandbox-friendly implementation
-  bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
-                                     nsACString& aMIMEType) override;
-
   // GetFileTokenForPath must be implemented by each platform.
   // platformAppPath --> a platform specific path to an application that we got
   //                     out of the rdf data source. This can be a mac file
   //                     spec, a unix path or a windows path depending on the
   //                     platform
   // aFile --> an nsIFile representation of that platform application path.
-  virtual MOZ_MUST_USE nsresult GetFileTokenForPath(
-      const char16_t* platformAppPath, nsIFile** aFile) override;
+  MOZ_MUST_USE nsresult GetFileTokenForPath(const char16_t* platformAppPath,
+                                            nsIFile** aFile) override;
 
   MOZ_MUST_USE nsresult OSProtocolHandlerExists(const char* aScheme,
                                                 bool* aHandlerExists) override;
 };
 
 #endif  // nsOSHelperAppService_h__
--- a/uriloader/exthandler/mac/nsOSHelperAppService.mm
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.mm
@@ -53,18 +53,18 @@ using mozilla::LogLevel;
 - (NSArray *)extensionsForMIMEType:(NSString *)aString;
 @end
 
 nsOSHelperAppService::~nsOSHelperAppService() {}
 
 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char *aProtocolScheme,
                                                        bool *aHandlerExists) {
   // CFStringCreateWithBytes() can fail even if we're not out of memory --
-  // for example if the 'bytes' parameter is something very wierd (like "~"
-  // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+  // for example if the 'bytes' parameter is something very weird (like
+  // "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
   // specified in the 'encoding' parameter.  See bug 548719.
   CFStringRef schemeString =
       ::CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)aProtocolScheme,
                                 strlen(aProtocolScheme), kCFStringEncodingUTF8, false);
   if (schemeString) {
     // LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
     // for aProtocolScheme when it's never been explicitly set (using
     // LSSetDefaultHandlerForURLScheme()).  For example, Safari is the default
@@ -192,22 +192,16 @@ nsresult nsOSHelperAppService::GetFileTo
   *aFile = localFile;
   NS_IF_ADDREF(*aFile);
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
-NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString &aType,
-                                                            const nsACString &aFileExt,
-                                                            nsIMIMEInfo **aMIMEInfo) {
-  return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
-}
-
 // Returns the MIME types an application bundle explicitly claims to handle.
 // Returns NULL if aAppRef doesn't explicitly claim to handle any MIME types.
 // If the return value is non-NULL, the caller is responsible for freeing it.
 // This isn't necessarily the same as the MIME types the application bundle
 // is registered to handle in the Launch Services database.  (For example
 // the Preview application is normally registered to handle the application/pdf
 // MIME type, even though it doesn't explicitly claim to handle *any* MIME
 // types in its Info.plist.  This is probably because Preview does explicitly
@@ -270,22 +264,21 @@ static CFArrayRef GetMIMETypesHandledByA
   ::CFRelease(infoDict);
   if (!::CFArrayGetCount(mimeTypes)) {
     ::CFRelease(mimeTypes);
     mimeTypes = NULL;
   }
   return mimeTypes;
 }
 
-// aMIMEType and aFileExt might not match,  If they don't we set *aFound to
-// false and return a minimal nsIMIMEInfo structure.
-already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString &aMIMEType,
-                                                                      const nsACString &aFileExt,
-                                                                      bool *aFound) {
+nsresult nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString &aMIMEType,
+                                                 const nsACString &aFileExt, bool *aFound,
+                                                 nsIMIMEInfo **aMIMEInfo) {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
+  MOZ_ASSERT(XRE_IsParentProcess());
 
   *aFound = false;
 
   const nsCString &flatType = PromiseFlatCString(aMIMEType);
   const nsCString &flatExt = PromiseFlatCString(aFileExt);
 
   MOZ_LOG(mLog, LogLevel::Debug,
           ("Mac: HelperAppService lookup for type '%s' ext '%s'\n", flatType.get(), flatExt.get()));
@@ -305,36 +298,36 @@ already_AddRefed<nsIMIMEInfo> nsOSHelper
   FSRef extAppFSRef;
 
   CFStringRef cfMIMEType = NULL;
 
   if (!aMIMEType.IsEmpty()) {
     typeIsOctetStream = aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM);
     CFURLRef appURL = NULL;
     // CFStringCreateWithCString() can fail even if we're not out of memory --
-    // for example if the 'cStr' parameter is something very weird (like "~"
-    // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+    // for example if the 'cStr' parameter is something very weird (like
+    // "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
     // specified in the 'encoding' parameter.  See bug 548719.
     cfMIMEType = ::CFStringCreateWithCString(NULL, flatType.get(), kCFStringEncodingUTF8);
     if (cfMIMEType) {
       err = ::LSCopyApplicationForMIMEType(cfMIMEType, kLSRolesAll, &appURL);
       if ((err == noErr) && appURL && ::CFURLGetFSRef(appURL, &typeAppFSRef)) {
         haveAppForType = true;
         MOZ_LOG(mLog, LogLevel::Debug,
                 ("LSCopyApplicationForMIMEType found a default application\n"));
       }
       if (appURL) {
         ::CFRelease(appURL);
       }
     }
   }
   if (!aFileExt.IsEmpty()) {
     // CFStringCreateWithCString() can fail even if we're not out of memory --
-    // for example if the 'cStr' parameter is something very wierd (like "~"
-    // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+    // for example if the 'cStr' parameter is something very weird (like
+    // "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
     // specified in the 'encoding' parameter.  See bug 548719.
     CFStringRef cfExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8);
     if (cfExt) {
       err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, cfExt, kLSRolesAll,
                                       &extAppFSRef, nullptr);
       if (err == noErr) {
         haveAppForExt = true;
         MOZ_LOG(mLog, LogLevel::Debug, ("LSGetApplicationForInfo found a default application\n"));
@@ -440,20 +433,21 @@ already_AddRefed<nsIMIMEInfo> nsOSHelper
       // Otherwise set the MIME type to a reasonable fallback.
       mimeInfoMac->SetMIMEType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
     }
   }
 
   if (typeAppIsDefault || extAppIsDefault) {
     if (haveAppForExt) mimeInfoMac->AppendExtension(aFileExt);
 
-    nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
-    if (!app) {
+    nsresult rv;
+    nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+    if (NS_FAILED(rv)) {
       [localPool release];
-      return nullptr;
+      return rv;
     }
 
     CFStringRef cfAppName = NULL;
     if (typeAppIsDefault) {
       app->InitWithFSRef(&typeAppFSRef);
       ::LSCopyItemAttribute((const FSRef *)&typeAppFSRef, kLSRolesAll, kLSItemDisplayName,
                             (CFTypeRef *)&cfAppName);
     } else {
@@ -511,19 +505,20 @@ already_AddRefed<nsIMIMEInfo> nsOSHelper
       }
       ::CFRelease(cfType);
     }
   }
 
   MOZ_LOG(mLog, LogLevel::Debug, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound));
 
   [localPool release];
-  return mimeInfoMac.forget();
+  mimeInfoMac.forget(aMIMEInfo);
+  return NS_OK;
 
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP
 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, bool *found,
                                                    nsIHandlerInfo **_retval) {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), found);
@@ -545,36 +540,8 @@ nsOSHelperAppService::GetProtocolHandler
     nsAutoString desc;
     rv = GetApplicationDescription(aScheme, desc);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetApplicationDescription failed");
     handlerInfo->SetDefaultDescription(desc);
   }
 
   return NS_OK;
 }
-
-/*
- * Override GetMIMETypeFromOSForExtension() so that we can proxy requests for
- * the MIME type to the parent when we're executing in the child process. If
- * we're in the parent process, query the OS directly.
- */
-bool nsOSHelperAppService::GetMIMETypeFromOSForExtension(const nsACString &aExtension,
-                                                         nsACString &aMIMEType) {
-  if (XRE_IsParentProcess()) {
-    return nsExternalHelperAppService::GetMIMETypeFromOSForExtension(aExtension, aMIMEType);
-  }
-
-  nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
-  if (NS_WARN_IF(!handlerSvc)) {
-    return false;
-  }
-
-  nsresult rv = handlerSvc->GetTypeFromExtension(aExtension, aMIMEType);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  if (aMIMEType.IsEmpty()) {
-    return false;
-  }
-
-  return true;
-}
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -31,16 +31,18 @@ else:
 
 EXPORTS += [
     osdir + '/nsOSHelperAppService.h'
 ]
 
 EXPORTS += [
     'ContentHandlerService.h',
     'nsExternalHelperAppService.h',
+    'nsMIMEInfoChild.h',
+    'nsOSHelperAppServiceChild.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [ '%s/%s' % (osdir, f) for f in [
         'nsExternalURLHandlerService.h',
     ]]
 
 EXPORTS += [
@@ -56,16 +58,17 @@ UNIFIED_SOURCES += [
     'ContentHandlerService.cpp',
     'ExternalHelperAppChild.cpp',
     'ExternalHelperAppParent.cpp',
     'HandlerServiceParent.cpp',
     'nsExternalHelperAppService.cpp',
     'nsExternalProtocolHandler.cpp',
     'nsLocalHandlerApp.cpp',
     'nsMIMEInfoImpl.cpp',
+    'nsOSHelperAppServiceChild.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'mac/nsLocalHandlerAppMac.mm',
         'mac/nsMIMEInfoMac.mm',
         'mac/nsOSHelperAppService.mm',
     ]
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -38,16 +38,18 @@
 #include "nsReadableUtils.h"
 #include "nsIRequest.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsIMutableArray.h"
 #include "nsIRedirectHistoryEntry.h"
+#include "nsOSHelperAppService.h"
+#include "nsOSHelperAppServiceChild.h"
 
 // used to access our datastore of user-configured helper applications
 #include "nsIHandlerService.h"
 #include "nsIMIMEInfo.h"
 #include "nsIRefreshURI.h"  // XXX needed to redirect according to Refresh: URI
 #include "nsIDocumentLoader.h"  // XXX needed to get orig. channel and assoc. refresh uri
 #include "nsIHelperAppLauncherDialog.h"
 #include "nsIContentDispatchChooser.h"
@@ -100,16 +102,17 @@
 #ifdef XP_WIN
 #  include "nsWindowsHelpers.h"
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #  include "FennecJNIWrappers.h"
 #endif
 
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ipc/URIUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 // Download Folder location constants
 #define NS_PREF_DOWNLOAD_DIR "browser.download.dir"
@@ -568,16 +571,42 @@ static const nsExtraMimeTypeEntry extraM
  */
 static const nsDefaultMimeTypeEntry nonDecodableExtensions[] = {
     {APPLICATION_GZIP, "gz"},
     {APPLICATION_GZIP, "tgz"},
     {APPLICATION_ZIP, "zip"},
     {APPLICATION_COMPRESS, "z"},
     {APPLICATION_GZIP, "svgz"}};
 
+static StaticRefPtr<nsExternalHelperAppService> sExtHelperAppSvcSingleton;
+
+/**
+ * On Mac child processes, return an nsOSHelperAppServiceChild for remoting
+ * OS calls to the parent process. On all other platforms use
+ * nsOSHelperAppService.
+ */
+/* static */
+already_AddRefed<nsExternalHelperAppService>
+nsExternalHelperAppService::GetSingleton() {
+  if (!sExtHelperAppSvcSingleton) {
+#ifdef XP_MACOSX
+    if (XRE_IsParentProcess()) {
+      sExtHelperAppSvcSingleton = new nsOSHelperAppService();
+    } else {
+      sExtHelperAppSvcSingleton = new nsOSHelperAppServiceChild();
+    }
+#else
+    sExtHelperAppSvcSingleton = new nsOSHelperAppService();
+#endif /* XP_MACOSX */
+    ClearOnShutdown(&sExtHelperAppSvcSingleton);
+  }
+
+  return do_AddRef(sExtHelperAppSvcSingleton);
+}
+
 NS_IMPL_ISUPPORTS(nsExternalHelperAppService, nsIExternalHelperAppService,
                   nsPIExternalAppLauncher, nsIExternalProtocolService,
                   nsIMIMEService, nsIObserver, nsISupportsWeakReference)
 
 nsExternalHelperAppService::nsExternalHelperAppService() {}
 nsresult nsExternalHelperAppService::Init() {
   // Add an observer for profile change
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -2478,25 +2507,28 @@ NS_IMETHODIMP nsExternalHelperAppService
     if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE;
   }
 
   // We promise to only send lower case mime types to the OS
   ToLowerCase(typeToUse);
 
   // (1) Ask the OS for a mime info
   bool found;
-  *_retval = GetMIMEInfoFromOS(typeToUse, aFileExt, &found).take();
+  nsresult rv = GetMIMEInfoFromOS(typeToUse, aFileExt, &found, _retval);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   LOG(("OS gave back 0x%p - found: %i\n", *_retval, found));
   // If we got no mimeinfo, something went wrong. Probably lack of memory.
   if (!*_retval) return NS_ERROR_OUT_OF_MEMORY;
 
   // (2) Now, let's see if we can find something in our datastore
   // This will not overwrite the OS information that interests us
   // (i.e. default application, default app. description)
-  nsresult rv;
   nsCOMPtr<nsIHandlerService> handlerSvc =
       do_GetService(NS_HANDLERSERVICE_CONTRACTID);
   if (handlerSvc) {
     bool hasHandler = false;
     (void)handlerSvc->Exists(*_retval, &hasHandler);
     if (hasHandler) {
       rv = handlerSvc->FillHandlerInfo(*_retval, EmptyCString());
       LOG(("Data source: Via type: retval 0x%08" PRIx32 "\n",
@@ -2791,12 +2823,22 @@ bool nsExternalHelperAppService::GetType
   }
 
   return false;
 }
 
 bool nsExternalHelperAppService::GetMIMETypeFromOSForExtension(
     const nsACString& aExtension, nsACString& aMIMEType) {
   bool found = false;
-  nsCOMPtr<nsIMIMEInfo> mimeInfo =
-      GetMIMEInfoFromOS(EmptyCString(), aExtension, &found);
-  return found && mimeInfo && NS_SUCCEEDED(mimeInfo->GetMIMEType(aMIMEType));
+  nsCOMPtr<nsIMIMEInfo> mimeInfo;
+  nsresult rv = GetMIMEInfoFromOS(EmptyCString(), aExtension, &found,
+                                  getter_AddRefs(mimeInfo));
+  return NS_SUCCEEDED(rv) && found && mimeInfo &&
+         NS_SUCCEEDED(mimeInfo->GetMIMEType(aMIMEType));
 }
+
+nsresult nsExternalHelperAppService::GetMIMEInfoFromOS(
+    const nsACString& aMIMEType, const nsACString& aFileExt, bool* aFound,
+    nsIMIMEInfo** aMIMEInfo) {
+  *aMIMEInfo = nullptr;
+  *aFound = false;
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -63,63 +63,45 @@ class nsExternalHelperAppService : publi
 
   /**
    * Initializes internal state. Will be called automatically when
    * this service is first instantiated.
    */
   MOZ_MUST_USE nsresult Init();
 
   /**
-   * Given a mimetype and an extension, looks up a mime info from the OS.
-   * The mime type is given preference. This function follows the same rules
-   * as nsIMIMEService::GetFromTypeAndExtension.
-   * This is supposed to be overridden by the platform-specific
-   * nsOSHelperAppService!
-   * @param aFileExt The file extension; may be empty. UTF-8 encoded.
-   * @param [out] aFound
-   *        Should be set to true if the os has a mapping, to
-   *        false otherwise. Must not be null.
-   * @return A MIMEInfo. This function must return a MIMEInfo object if it
-   *         can allocate one.  The only justifiable reason for not
-   *         returning one is an out-of-memory error.
-   *         If null, the value of aFound is unspecified.
-   */
-  virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(
-      const nsACString& aMIMEType, const nsACString& aFileExt,
-      bool* aFound) = 0;
-
-  /**
    * Given a string identifying an application, create an nsIFile representing
    * it. This function should look in $PATH for the application.
    * The base class implementation will first try to interpret platformAppPath
    * as an absolute path, and if that fails it will look for a file next to the
    * mozilla executable. Subclasses can override this method if they want a
    * different behaviour.
    * @param platformAppPath A platform specific path to an application that we
    *                        got out of the rdf data source. This can be a mac
    *                        file spec, a unix path or a windows path depending
    *                        on the platform
    * @param aFile           [out] An nsIFile representation of that platform
    *                        application path.
    */
   virtual nsresult GetFileTokenForPath(const char16_t* platformAppPath,
                                        nsIFile** aFile);
 
-  virtual nsresult OSProtocolHandlerExists(const char* aScheme,
-                                           bool* aExists) = 0;
+  NS_IMETHOD OSProtocolHandlerExists(const char* aScheme, bool* aExists) = 0;
 
   /**
    * Given an extension, get a MIME type string. If not overridden by
    * the OS-specific nsOSHelperAppService, will call into GetMIMEInfoFromOS
    * with an empty mimetype.
    * @return true if we successfully found a mimetype.
    */
   virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
                                              nsACString& aMIMEType);
 
+  static already_AddRefed<nsExternalHelperAppService> GetSingleton();
+
  protected:
   virtual ~nsExternalHelperAppService();
 
   /**
    * Searches the "extra" array of MIMEInfo objects for an object
    * with a specific type. If found, it will modify the passed-in
    * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
    * will be untouched.
--- a/uriloader/exthandler/nsIHandlerService.idl
+++ b/uriloader/exthandler/nsIHandlerService.idl
@@ -124,10 +124,39 @@ interface nsIHandlerService : nsISupport
   /**
    * Whether or not there is a handler known to the OS for the
    * specified protocol type.
    *
    * @param aProtocolScheme scheme to check for support
    *
    * @returns whether or not a handler exists
    */
+  boolean existsForProtocolOS(in ACString aProtocolScheme);
+
+  /**
+   * Whether or not there is a handler in the datastore or OS for
+   * the specified protocol type. If there is no handler in the datastore,
+   * falls back to a check for an OS handler.
+   *
+   * @param aProtocolScheme scheme to check for support
+   *
+   * @returns whether or not a handler exists
+   */
   boolean existsForProtocol(in ACString aProtocolScheme);
+
+  /*
+   * Fill in a handler info object using information from the OS, taking into
+   * account the MIME type and file extension. When the OS handler
+   * for the MIME type and extension match, |aFound| is returned as true. If
+   * either the MIME type or extension is the empty string and a handler is
+   * found, |aFound| is returned as true.
+   */
+  void getMIMEInfoFromOS(in nsIHandlerInfo aHandlerInfo,
+                         in ACString aMIMEType,
+                         in ACString aExtension,
+                         out bool aFound);
+
+  /*
+   * Get a description for the application responsible for handling
+   * the provided protocol.
+   */
+  AString getApplicationDescription(in ACString aProtocolScheme);
 };
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsMIMEInfoChild.h
@@ -0,0 +1,49 @@
+/* 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 nsMIMEInfoChild_h
+#define nsMIMEInfoChild_h
+
+#include "nsMIMEInfoImpl.h"
+
+/*
+ * A platform-generic nsMIMEInfo implementation to be used in child process
+ * generic code that needs a MIMEInfo with limited functionality.
+ */
+class nsChildProcessMIMEInfo : public nsMIMEInfoImpl {
+ public:
+  explicit nsChildProcessMIMEInfo(const char* aMIMEType = "")
+      : nsMIMEInfoImpl(aMIMEType) {}
+
+  explicit nsChildProcessMIMEInfo(const nsACString& aMIMEType)
+      : nsMIMEInfoImpl(aMIMEType) {}
+
+  nsChildProcessMIMEInfo(const nsACString& aType, HandlerClass aClass)
+      : nsMIMEInfoImpl(aType, aClass) {}
+
+  NS_IMETHOD LaunchWithFile(nsIFile* aFile) override {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  };
+
+ protected:
+  virtual MOZ_MUST_USE nsresult LoadUriInternal(nsIURI* aURI) override {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  };
+
+#ifdef DEBUG
+  virtual MOZ_MUST_USE nsresult LaunchDefaultWithFile(nsIFile* aFile) override {
+    return NS_ERROR_UNEXPECTED;
+  }
+#endif
+  static MOZ_MUST_USE nsresult OpenApplicationWithURI(nsIFile* aApplication,
+                                                      const nsCString& aURI) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription) override {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  };
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsOSHelperAppServiceChild.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 3; 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 <sys/types.h>
+#include <sys/stat.h>
+#include "mozilla/Logging.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "nsOSHelperAppServiceChild.h"
+#include "nsMIMEInfoChild.h"
+#include "nsISupports.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIURL.h"
+#include "nsIFile.h"
+#include "nsIHandlerService.h"
+#include "nsMimeTypes.h"
+#include "nsMIMEInfoImpl.h"
+#include "nsIStringBundle.h"
+#include "nsIPromptService.h"
+#include "nsMemory.h"
+#include "nsCRT.h"
+#include "nsEmbedCID.h"
+
+#undef LOG
+#define LOG(args) \
+  MOZ_LOG(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info, args)
+#undef LOG_ERR
+#define LOG_ERR(args) \
+  MOZ_LOG(nsExternalHelperAppService::mLog, mozilla::LogLevel::Error, args)
+#undef LOG_ENABLED
+#define LOG_ENABLED() \
+  MOZ_LOG_TEST(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info)
+
+nsresult nsOSHelperAppServiceChild::ExternalProtocolHandlerExists(
+    const char* aProtocolScheme, bool* aHandlerExists) {
+  nsresult rv;
+  nsCOMPtr<nsIHandlerService> handlerSvc =
+      do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    LOG_ERR(("nsOSHelperAppServiceChild error: no handler service"));
+    return rv;
+  }
+
+  nsAutoCString scheme(aProtocolScheme);
+  *aHandlerExists = false;
+  rv = handlerSvc->ExistsForProtocol(scheme, aHandlerExists);
+  LOG(
+      ("nsOSHelperAppServiceChild::OSProtocolHandlerExists(): "
+       "protocol: %s, result: %" PRId32,
+       aProtocolScheme, static_cast<uint32_t>(rv)));
+  mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+}
+
+nsresult nsOSHelperAppServiceChild::OSProtocolHandlerExists(const char* aScheme,
+                                                            bool* aExists) {
+  // Use ExternalProtocolHandlerExists() which includes the
+  // OS-level check and remotes the call to the parent process.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsOSHelperAppServiceChild::GetApplicationDescription(const nsACString& aScheme,
+                                                     nsAString& aRetVal) {
+  nsresult rv;
+  nsCOMPtr<nsIHandlerService> handlerSvc =
+      do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    LOG_ERR(("nsOSHelperAppServiceChild error: no handler service"));
+    return rv;
+  }
+
+  rv = handlerSvc->GetApplicationDescription(aScheme, aRetVal);
+  LOG(
+      ("nsOSHelperAppServiceChild::GetApplicationDescription(): "
+       "scheme: %s, result: %" PRId32 ", description: %s",
+       PromiseFlatCString(aScheme).get(), static_cast<uint32_t>(rv),
+       NS_ConvertUTF16toUTF8(aRetVal).get()));
+  mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+}
+
+NS_IMETHODIMP
+nsOSHelperAppServiceChild::GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                                             const nsACString& aFileExt,
+                                             bool* aFound,
+                                             nsIMIMEInfo** aMIMEInfo) {
+  RefPtr<nsChildProcessMIMEInfo> mimeInfo =
+      new nsChildProcessMIMEInfo(aMIMEType);
+
+  nsCOMPtr<nsIHandlerService> handlerSvc =
+      do_GetService(NS_HANDLERSERVICE_CONTRACTID);
+  if (handlerSvc) {
+    nsresult rv =
+        handlerSvc->GetMIMEInfoFromOS(mimeInfo, aMIMEType, aFileExt, aFound);
+    LOG(
+        ("nsOSHelperAppServiceChild::GetMIMEInfoFromOS(): "
+         "MIME type: %s, extension: %s, result: %" PRId32,
+         PromiseFlatCString(aMIMEType).get(),
+         PromiseFlatCString(aFileExt).get(), static_cast<uint32_t>(rv)));
+    mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  } else {
+    LOG_ERR(("nsOSHelperAppServiceChild error: no handler service"));
+    *aFound = false;
+  }
+
+  mimeInfo.forget(aMIMEInfo);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOSHelperAppServiceChild::GetProtocolHandlerInfoFromOS(
+    const nsACString& aScheme, bool* aFound, nsIHandlerInfo** aRetVal) {
+  MOZ_ASSERT(!aScheme.IsEmpty(), "No scheme was specified!");
+
+  nsresult rv =
+      OSProtocolHandlerExists(PromiseFlatCString(aScheme).get(), aFound);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsChildProcessMIMEInfo* handlerInfo =
+      new nsChildProcessMIMEInfo(aScheme, nsMIMEInfoBase::eProtocolInfo);
+  NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
+  NS_ADDREF(*aRetVal = handlerInfo);
+
+  if (!*aFound) {
+    // Code that calls this requires an object no matter whether the OS has
+    // something for us, so we return the empty object.
+    return NS_OK;
+  }
+
+  nsAutoString description;
+  rv = GetApplicationDescription(aScheme, description);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetApplicationDescription failed");
+  handlerInfo->SetDefaultDescription(description);
+  return NS_OK;
+}
+
+nsresult nsOSHelperAppServiceChild::GetFileTokenForPath(
+    const char16_t* platformAppPath, nsIFile** aFile) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsOSHelperAppServiceChild.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 3; 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/. */
+
+#ifndef nsOSHelperAppServiceChild_h__
+#define nsOSHelperAppServiceChild_h__
+
+#include "nsExternalHelperAppService.h"
+
+class nsIMIMEInfo;
+
+/*
+ * Provides a generic implementation of the nsExternalHelperAppService
+ * platform-specific methods by remoting calls to the parent process.
+ * Only provides implementations for the methods needed in unprivileged
+ * child processes.
+ */
+class nsOSHelperAppServiceChild : public nsExternalHelperAppService {
+ public:
+  nsOSHelperAppServiceChild() : nsExternalHelperAppService(){};
+  virtual ~nsOSHelperAppServiceChild() = default;
+
+  NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
+                                          bool* found,
+                                          nsIHandlerInfo** _retval) override;
+
+  nsresult GetFileTokenForPath(const char16_t* platformAppPath,
+                               nsIFile** aFile) override;
+
+  NS_IMETHOD ExternalProtocolHandlerExists(const char* aProtocolScheme,
+                                           bool* aHandlerExists) override;
+
+  NS_IMETHOD OSProtocolHandlerExists(const char* aScheme,
+                                     bool* aExists) override;
+
+  NS_IMETHOD GetApplicationDescription(const nsACString& aScheme,
+                                       nsAString& aRetVal) override;
+
+  NS_IMETHOD GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                               const nsACString& aFileExt, bool* aFound,
+                               nsIMIMEInfo** aMIMEInfo) override;
+};
+
+#endif  // nsOSHelperAppServiceChild_h__
--- a/uriloader/exthandler/uikit/nsOSHelperAppService.h
+++ b/uriloader/exthandler/uikit/nsOSHelperAppService.h
@@ -24,28 +24,29 @@ class nsOSHelperAppService final : publi
   // override nsIExternalProtocolService methods
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme,
                                        nsAString& _retval);
 
   // method overrides --> used to hook the mime service into internet config....
   NS_IMETHOD GetFromTypeAndExtension(const nsACString& aType,
                                      const nsACString& aFileExt,
                                      nsIMIMEInfo** aMIMEInfo);
-  already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
-                                                  const nsACString& aFileExt,
-                                                  bool* aFound);
+  NS_IMETHOD GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                               const nsACString& aFileExt, bool* aFound,
+                               nsIMIMEInfo** aMIMEInfo) override;
   NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                           bool* found,
                                           nsIHandlerInfo** _retval);
 
   // GetFileTokenForPath must be implemented by each platform.
   // platformAppPath --> a platform specific path to an application that we got
   //                     out of the rdf data source. This can be a mac file
   //                     spec, a unix path or a windows path depending on the
   //                     platform
   // aFile --> an nsIFile representation of that platform application path.
   virtual nsresult GetFileTokenForPath(const char16_t* platformAppPath,
                                        nsIFile** aFile);
 
-  nsresult OSProtocolHandlerExists(const char* aScheme, bool* aHandlerExists);
+  nsresult OSProtocolHandlerExists(const char* aScheme,
+                                   bool* aHandlerExists) override;
 };
 
 #endif  // nsOSHelperAppService_h__
--- a/uriloader/exthandler/uikit/nsOSHelperAppService.mm
+++ b/uriloader/exthandler/uikit/nsOSHelperAppService.mm
@@ -27,21 +27,22 @@ nsresult nsOSHelperAppService::GetFileTo
 }
 
 NS_IMETHODIMP
 nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt,
                                               nsIMIMEInfo** aMIMEInfo) {
   return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
 }
 
-already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
-                                                                      const nsACString& aFileExt,
-                                                                      bool* aFound) {
+NS_IMETHODIMP nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                                                      const nsACString& aFileExt, bool* aFound,
+                                                      nsIMIMEInfo** aMIMEInfo) {
+  *aMIMEInfo = nullptr;
   *aFound = false;
-  return nullptr;
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString& aScheme, bool* found,
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const char* aScheme, bool* found,
                                                    nsIHandlerInfo** _retval) {
   *found = false;
   return NS_OK;
 }
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -1034,18 +1034,18 @@ nsresult nsOSHelperAppService::OSProtoco
 #else
     *aHandlerExists = false;
 #endif
   } else {
     *aHandlerExists = false;
     nsCOMPtr<nsIHandlerService> handlerSvc =
         do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv) && handlerSvc) {
-      rv = handlerSvc->ExistsForProtocol(nsCString(aProtocolScheme),
-                                         aHandlerExists);
+      rv = handlerSvc->ExistsForProtocolOS(nsCString(aProtocolScheme),
+                                           aHandlerExists);
     }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(
     const nsACString& aScheme, nsAString& _retval) {
@@ -1319,62 +1319,70 @@ already_AddRefed<nsMIMEInfoBase> nsOSHel
     mimeInfo->SetDefaultDescription(handler);
   } else {
     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
   }
 
   return mimeInfo.forget();
 }
 
-already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(
-    const nsACString& aType, const nsACString& aFileExt, bool* aFound) {
+nsresult nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
+                                                 const nsACString& aFileExt,
+                                                 bool* aFound,
+                                                 nsIMIMEInfo** aMIMEInfo) {
   *aFound = true;
   RefPtr<nsMIMEInfoBase> retval;
   // Fallback to lookup by extension when generic 'application/octet-stream'
   // content type is received.
   if (!aType.EqualsLiteral(APPLICATION_OCTET_STREAM)) {
     retval = GetFromType(PromiseFlatCString(aType));
   }
   bool hasDefault = false;
   if (retval) retval->GetHasDefaultHandler(&hasDefault);
   if (!retval || !hasDefault) {
     RefPtr<nsMIMEInfoBase> miByExt =
         GetFromExtension(PromiseFlatCString(aFileExt));
     // If we had no extension match, but a type match, use that
-    if (!miByExt && retval) return retval.forget();
+    if (!miByExt && retval) {
+      retval.forget(aMIMEInfo);
+      return NS_OK;
+    }
     // If we had an extension match but no type match, set the mimetype and use
     // it
     if (!retval && miByExt) {
       if (!aType.IsEmpty()) miByExt->SetMIMEType(aType);
       miByExt.swap(retval);
 
-      return retval.forget();
+      retval.forget(aMIMEInfo);
+      return NS_OK;
     }
     // If we got nothing, make a new mimeinfo
     if (!retval) {
       *aFound = false;
       retval = new nsMIMEInfoUnix(aType);
       if (retval) {
         if (!aFileExt.IsEmpty()) retval->AppendExtension(aFileExt);
       }
 
-      return retval.forget();
+      retval.forget(aMIMEInfo);
+      return NS_OK;
     }
 
     // Copy the attributes of retval (mimeinfo from type) onto miByExt, to
     // return it
     // but reset to just collected mDefaultAppDescription (from ext)
     nsAutoString byExtDefault;
     miByExt->GetDefaultDescription(byExtDefault);
     retval->SetDefaultDescription(byExtDefault);
     retval->CopyBasicDataTo(miByExt);
 
     miByExt.swap(retval);
   }
-  return retval.forget();
+  retval.forget(aMIMEInfo);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                                    bool* found,
                                                    nsIHandlerInfo** _retval) {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
--- a/uriloader/exthandler/unix/nsOSHelperAppService.h
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.h
@@ -19,19 +19,19 @@
 class nsILineInputStream;
 class nsMIMEInfoBase;
 
 class nsOSHelperAppService : public nsExternalHelperAppService {
  public:
   virtual ~nsOSHelperAppService();
 
   // method overrides for mime.types and mime.info look up steps
-  already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMimeType,
-                                                  const nsACString& aFileExt,
-                                                  bool* aFound) override;
+  NS_IMETHOD GetMIMEInfoFromOS(const nsACString& aMimeType,
+                               const nsACString& aFileExt, bool* aFound,
+                               nsIMIMEInfo** aMIMEInfo) override;
   NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                           bool* found,
                                           nsIHandlerInfo** _retval) override;
 
   // override nsIExternalProtocolService methods
   nsresult OSProtocolHandlerExists(const char* aProtocolScheme,
                                    bool* aHandlerExists) override;
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme,
--- a/uriloader/exthandler/win/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp
@@ -371,18 +371,20 @@ already_AddRefed<nsMIMEInfoWin> nsOSHelp
   mimeInfo->SetDefaultApplicationHandler(defaultApplication);
 
   // Grab the general description
   GetMIMEInfoFromRegistry(appInfo, mimeInfo);
 
   return mimeInfo.forget();
 }
 
-already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(
-    const nsACString& aMIMEType, const nsACString& aFileExt, bool* aFound) {
+NS_IMETHODIMP
+nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                                        const nsACString& aFileExt,
+                                        bool* aFound, nsIMIMEInfo** aMIMEInfo) {
   *aFound = true;
 
   const nsCString& flatType = PromiseFlatCString(aMIMEType);
   const nsCString& flatExt = PromiseFlatCString(aFileExt);
 
   nsAutoString fileExtension;
   /* XXX The Equals is a gross hack to wallpaper over the most common Win32
    * extension issues caused by the fix for bug 116938.  See bug
@@ -425,38 +427,44 @@ already_AddRefed<nsIMIMEInfo> nsOSHelper
       mi->ExtensionExists(aFileExt, &extExist);
       if (!extExist) mi->AppendExtension(aFileExt);
     }
   }
   if (!mi || !hasDefault) {
     RefPtr<nsMIMEInfoWin> miByExt =
         GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get());
     LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get()));
-    if (!miByExt && mi) return mi.forget();
+    if (!miByExt && mi) {
+      mi.forget(aMIMEInfo);
+      return NS_OK;
+    }
     if (miByExt && !mi) {
-      return miByExt.forget();
+      miByExt.forget(aMIMEInfo);
+      return NS_OK;
     }
     if (!miByExt && !mi) {
       *aFound = false;
       mi = new nsMIMEInfoWin(flatType);
       if (!aFileExt.IsEmpty()) {
         mi->AppendExtension(aFileExt);
       }
 
-      return mi.forget();
+      mi.forget(aMIMEInfo);
+      return NS_OK;
     }
 
     // if we get here, mi has no default app. copy from extension lookup.
     nsCOMPtr<nsIFile> defaultApp;
     nsAutoString desc;
     miByExt->GetDefaultDescription(desc);
 
     mi->SetDefaultDescription(desc);
   }
-  return mi.forget();
+  mi.forget(aMIMEInfo);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                                    bool* found,
                                                    nsIHandlerInfo** _retval) {
   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
 
--- a/uriloader/exthandler/win/nsOSHelperAppService.h
+++ b/uriloader/exthandler/win/nsOSHelperAppService.h
@@ -26,26 +26,26 @@ class nsMIMEInfoWin;
 class nsIMIMEInfo;
 
 class nsOSHelperAppService : public nsExternalHelperAppService {
  public:
   nsOSHelperAppService();
   virtual ~nsOSHelperAppService();
 
   // override nsIExternalProtocolService methods
-  nsresult OSProtocolHandlerExists(const char* aProtocolScheme,
-                                   bool* aHandlerExists);
+  NS_IMETHOD OSProtocolHandlerExists(const char* aProtocolScheme,
+                                     bool* aHandlerExists) override;
   nsresult LoadUriInternal(nsIURI* aURL);
   NS_IMETHOD GetApplicationDescription(const nsACString& aScheme,
                                        nsAString& _retval) override;
 
   // method overrides for windows registry look up steps....
-  already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
-                                                  const nsACString& aFileExt,
-                                                  bool* aFound);
+  NS_IMETHOD GetMIMEInfoFromOS(const nsACString& aMIMEType,
+                               const nsACString& aFileExt, bool* aFound,
+                               nsIMIMEInfo** aMIMEInfo) override;
   NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                           bool* found,
                                           nsIHandlerInfo** _retval);
   virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
                                              nsACString& aMIMEType) override;
 
   /** Get the string value of a registry value and store it in result.
    * @return true on success, false on failure