Bug 924702 - Rewrite the app:// protocol handler in c++ r=jduell
authorFabrice Desré <fabrice@mozilla.com>
Wed, 23 Oct 2013 11:56:20 -0700
changeset 165642 0ab4e1cae4872ec7597db327369bb96b5d88c345
parent 165641 80fdb0fe0fab3d62d8db9e9499c51e6d65cf5801
child 165643 ae1c322dab45a0db0ca5c741e57c9d761635ced5
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs924702
milestone27.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 924702 - Rewrite the app:// protocol handler in c++ r=jduell
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
configure.in
dom/webidl/AppInfo.webidl
dom/webidl/DummyBinding.webidl
dom/webidl/moz.build
mobile/android/installer/package-manifest.in
netwerk/build/Makefile.in
netwerk/build/nsNetCID.h
netwerk/build/nsNetModule.cpp
netwerk/protocol/app/AppProtocolHandler.cpp
netwerk/protocol/app/AppProtocolHandler.h
netwerk/protocol/app/AppProtocolHandler.js
netwerk/protocol/app/AppProtocolHandler.manifest
netwerk/protocol/app/Makefile.in
netwerk/protocol/app/moz.build
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -538,19 +538,16 @@
 @BINPATH@/components/ActivityWrapper.js
 @BINPATH@/components/ActivityMessageConfigurator.js
 
 @BINPATH@/components/TCPSocket.js
 @BINPATH@/components/TCPServerSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
-@BINPATH@/components/AppProtocolHandler.js
-@BINPATH@/components/AppProtocolHandler.manifest
-
 @BINPATH@/components/Payment.js
 @BINPATH@/components/PaymentFlowInfo.js
 @BINPATH@/components/PaymentRequestInfo.js
 @BINPATH@/components/Payment.manifest
 
 ; InputMethod API
 @BINPATH@/components/MozKeyboard.js
 @BINPATH@/components/InputMethod.manifest
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -514,19 +514,16 @@
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
 @BINPATH@/components/PushServiceLauncher.js
 @BINPATH@/components/TCPSocket.js
 @BINPATH@/components/TCPServerSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
-@BINPATH@/components/AppProtocolHandler.js
-@BINPATH@/components/AppProtocolHandler.manifest
-
 @BINPATH@/components/Payment.js
 @BINPATH@/components/PaymentFlowInfo.js
 @BINPATH@/components/PaymentRequestInfo.js
 @BINPATH@/components/Payment.manifest
 
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/PeerConnection.js
 @BINPATH@/components/PeerConnection.manifest
--- a/configure.in
+++ b/configure.in
@@ -3981,17 +3981,17 @@ MOZ_UNIVERSALCHARDET=1
 MOZ_URL_CLASSIFIER=
 MOZ_XUL=1
 MOZ_ZIPWRITER=1
 NS_PRINTING=1
 MOZ_PDF_PRINTING=
 MOZ_DISABLE_CRYPTOLEGACY=
 NSS_DISABLE_DBM=
 NECKO_COOKIES=1
-NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
+NECKO_PROTOCOLS_DEFAULT="about app data file ftp http res viewsource websocket wyciwyg device"
 USE_ARM_KUSER=
 BUILD_CTYPES=1
 MOZ_USE_NATIVE_POPUP_WINDOWS=
 MOZ_ANDROID_HISTORY=
 MOZ_WEBSMS_BACKEND=
 MOZ_ANDROID_BEAM=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AppInfo.webidl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+/**
+  * This dictionnary holds the parameters supporting the app:// protocol.
+  */
+dictionary AppInfo
+{
+  DOMString path = "";
+  boolean   isCoreApp = false;
+};
--- a/dom/webidl/DummyBinding.webidl
+++ b/dom/webidl/DummyBinding.webidl
@@ -23,13 +23,14 @@ interface DummyInterface : EventTarget {
   void MmsParameters(optional MmsParameters arg);
   void MmsAttachment(optional MmsAttachment arg);
   void AsyncScrollEventDetail(optional AsyncScrollEventDetail arg);
   void OpenWindowEventDetail(optional OpenWindowEventDetail arg);
   void DOMWindowResizeEventDetail(optional DOMWindowResizeEventDetail arg);
   void WifiOptions(optional WifiCommandOptions arg1,
                    optional WifiResultOptions arg2);
   void AppNotificationServiceOptions(optional AppNotificationServiceOptions arg);
+  void AppInfo(optional AppInfo arg1);
 };
 
 interface DummyInterfaceWorkers {
   BlobPropertyBag blobBag();
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -14,16 +14,17 @@ PREPROCESSED_WEBIDL_FILES = [
     'Crypto.webidl',
     'Navigator.webidl',
 ]
 
 WEBIDL_FILES = [
     'AbstractWorker.webidl',
     'AnalyserNode.webidl',
     'AnimationEvent.webidl',
+    'AppInfo.webidl',
     'AppNotificationServiceOptions.webidl',
     'ArchiveReader.webidl',
     'ArchiveRequest.webidl',
     'Attr.webidl',
     'AudioBuffer.webidl',
     'AudioBufferSourceNode.webidl',
     'AudioContext.webidl',
     'AudioDestinationNode.webidl',
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -380,18 +380,16 @@
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
-@BINPATH@/components/AppProtocolHandler.js
-@BINPATH@/components/AppProtocolHandler.manifest
 
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
 
 @BINPATH@/components/SystemMessageInternal.js
 @BINPATH@/components/SystemMessageManager.js
 @BINPATH@/components/SystemMessageManager.manifest
 
--- a/netwerk/build/Makefile.in
+++ b/netwerk/build/Makefile.in
@@ -64,16 +64,17 @@ LOCAL_INCLUDES = \
   -I$(srcdir)/../base/src \
   -I$(srcdir)/../dns \
   -I$(srcdir)/../socket \
   -I$(srcdir)/../streamconv/src \
   -I$(srcdir)/../streamconv/converters \
   -I$(srcdir)/../mime \
   -I$(srcdir)/../cache \
   -I$(srcdir)/../protocol/about \
+  -I$(srcdir)/../protocol/app \
   -I../dns \
   $(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
     -I$(srcdir)/../protocol/$(d)) \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
     LOCAL_INCLUDES += -I$(srcdir)/../system/win32
 endif
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -598,16 +598,28 @@
 { /* fbc81170-1f69-11d3-9344-00104ba0fd40 */         \
     0xfbc81170,                                      \
     0x1f69,                                          \
     0x11d3,                                          \
     {0x93, 0x44, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
 }
 
 /******************************************************************************
+ * netwerk/protocol/app/ classes
+ */
+
+#define NS_APPPROTOCOLHANDLER_CID                    \
+{ /* {B6ED3030-9999-11d3-A178-0050041CAF44} */       \
+    0xb6ed3030,                                      \
+    0x9999,                                          \
+    0x11d3,                                          \
+    {0xa1, 0x78, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \
+}
+
+/******************************************************************************
  * netwerk/protocol/data/ classes
  */
 
 #define NS_DATAPROTOCOLHANDLER_CID                   \
 { /* {B6ED3030-6183-11d3-A178-0050041CAF44} */       \
     0xb6ed3030,                                      \
     0x6183,                                          \
     0x11d3,                                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -238,16 +238,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDig
 #endif // !NECKO_PROTOCOL_http
 
 #include "mozilla/net/Dashboard.h"
 namespace mozilla {
 namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
 }
 }
+#include "AppProtocolHandler.h"
 
 #ifdef NECKO_PROTOCOL_res
 // resource
 #include "nsResProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsResURL)
 #endif
 
@@ -767,16 +768,17 @@ NS_DEFINE_NAMED_CID(NS_ABOUT_CACHE_ENTRY
 #endif
 NS_DEFINE_NAMED_CID(NS_SOCKSSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_SOCKS4SOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_UDPSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_CACHESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHENAMESPACE_CID);
 NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHE_CID);
+NS_DEFINE_NAMED_CID(NS_APPPROTOCOLHANDLER_CID);
 #ifdef NECKO_COOKIES
 NS_DEFINE_NAMED_CID(NS_COOKIEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_COOKIESERVICE_CID);
 #endif
 #ifdef NECKO_WIFI
 NS_DEFINE_NAMED_CID(NS_WIFI_MONITOR_COMPONENT_CID);
 #endif
 #ifdef NECKO_PROTOCOL_data
@@ -905,16 +907,17 @@ static const mozilla::Module::CIDEntry k
 #endif
     { &kNS_SOCKSSOCKETPROVIDER_CID, false, nullptr, nsSOCKSSocketProvider::CreateV5 },
     { &kNS_SOCKS4SOCKETPROVIDER_CID, false, nullptr, nsSOCKSSocketProvider::CreateV4 },
     { &kNS_UDPSOCKETPROVIDER_CID, false, nullptr, nsUDPSocketProviderConstructor },
     { &kNS_CACHESERVICE_CID, false, nullptr, nsCacheService::Create },
     { &kNS_APPLICATIONCACHESERVICE_CID, false, nullptr, nsApplicationCacheServiceConstructor },
     { &kNS_APPLICATIONCACHENAMESPACE_CID, false, nullptr, nsApplicationCacheNamespaceConstructor },
     { &kNS_APPLICATIONCACHE_CID, false, nullptr, nsApplicationCacheConstructor },
+    { &kNS_APPPROTOCOLHANDLER_CID, false, nullptr, AppProtocolHandler::Create },
 #ifdef NECKO_COOKIES
     { &kNS_COOKIEMANAGER_CID, false, nullptr, nsICookieServiceConstructor },
     { &kNS_COOKIESERVICE_CID, false, nullptr, nsICookieServiceConstructor },
 #endif
 #ifdef NECKO_WIFI
     { &kNS_WIFI_MONITOR_COMPONENT_CID, false, nullptr, nsWifiMonitorConstructor },
 #endif
 #ifdef NECKO_PROTOCOL_data
@@ -1050,16 +1053,17 @@ static const mozilla::Module::ContractID
 #endif
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "socks", &kNS_SOCKSSOCKETPROVIDER_CID },
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "socks4", &kNS_SOCKS4SOCKETPROVIDER_CID },
     { NS_NETWORK_SOCKET_CONTRACTID_PREFIX "udp", &kNS_UDPSOCKETPROVIDER_CID },
     { NS_CACHESERVICE_CONTRACTID, &kNS_CACHESERVICE_CID },
     { NS_APPLICATIONCACHESERVICE_CONTRACTID, &kNS_APPLICATIONCACHESERVICE_CID },
     { NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &kNS_APPLICATIONCACHENAMESPACE_CID },
     { NS_APPLICATIONCACHE_CONTRACTID, &kNS_APPLICATIONCACHE_CID },
+    { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "app", &kNS_APPPROTOCOLHANDLER_CID },
 #ifdef NECKO_COOKIES
     { NS_COOKIEMANAGER_CONTRACTID, &kNS_COOKIEMANAGER_CID },
     { NS_COOKIESERVICE_CONTRACTID, &kNS_COOKIESERVICE_CID },
 #endif
 #ifdef NECKO_WIFI
     { NS_WIFI_MONITOR_CONTRACTID, &kNS_WIFI_MONITOR_COMPONENT_CID },
 #endif
 #ifdef NECKO_PROTOCOL_data
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/AppProtocolHandler.cpp
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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 "AppProtocolHandler.h"
+#include "nsBaseChannel.h"
+#include "nsJARChannel.h"
+#include "nsNetCID.h"
+#include "nsIAppsService.h"
+#include "nsCxPusher.h"
+#include "nsXULAppAPI.h"
+
+/**
+  * This dummy channel implementation only provides enough functionality
+  * to return a fake 404 error when the caller asks for an app:// URL
+  * containing an unknown appId.
+  */
+class DummyChannel : public nsIJARChannel
+                          , nsRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUEST
+  NS_DECL_NSICHANNEL
+  NS_DECL_NSIJARCHANNEL
+
+  DummyChannel();
+
+  NS_IMETHODIMP Run();
+
+private:
+  bool                        mPending;
+  uint32_t                    mSuspendCount;
+  nsCOMPtr<nsISupports>       mListenerContext;
+  nsCOMPtr<nsIStreamListener> mListener;
+  nsCOMPtr<nsILoadGroup>      mLoadGroup;
+  nsLoadFlags                 mLoadFlags;
+};
+
+NS_IMPL_ISUPPORTS3(DummyChannel, nsIRequest, nsIChannel, nsIJARChannel)
+
+DummyChannel::DummyChannel() : mPending(false)
+                             , mSuspendCount(0)
+                             , mLoadFlags(LOAD_NORMAL)
+{
+}
+
+NS_IMETHODIMP DummyChannel::GetName(nsACString &result)
+{
+  result = "dummy_app_channel";
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::GetStatus(nsresult *aStatus)
+{
+  *aStatus = NS_ERROR_FILE_NOT_FOUND;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::IsPending(bool *aResult)
+{
+  *aResult = mPending;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::Suspend()
+{
+  mSuspendCount++;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::Resume()
+{
+  if (mSuspendCount <= 0) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (--mSuspendCount == 0) {
+    NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::Open(nsIInputStream**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext)
+{
+  mListener = aListener;
+  mListenerContext = aContext;
+  mPending = true;
+
+  if (mLoadGroup) {
+    mLoadGroup->AddRequest(this, aContext);
+  }
+
+  if (mSuspendCount == 0) {
+    NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+  }
+
+  return NS_OK;
+}
+
+// nsIJarChannel, needed for XHR to turn NS_ERROR_FILE_NOT_FOUND into
+// a 404 error.
+NS_IMETHODIMP DummyChannel::GetIsUnsafe(bool *aResult)
+{
+  *aResult = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::SetAppURI(nsIURI *aURI)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::Run()
+{
+  nsresult rv = mListener->OnStartRequest(this, mListenerContext);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mPending = false;
+  rv = mListener->OnStopRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (mLoadGroup) {
+    mLoadGroup->RemoveRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND);
+  }
+
+  mListener = nullptr;
+  mListenerContext = nullptr;
+  rv = SetNotificationCallbacks(nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::Cancel(nsresult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
+{
+  *aLoadGroup = mLoadGroup;
+  NS_IF_ADDREF(*aLoadGroup);
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+  mLoadGroup = aLoadGroup;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
+{
+  *aLoadFlags = mLoadFlags;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
+{
+  mLoadFlags = aLoadFlags;
+  return NS_OK;
+}
+
+NS_IMETHODIMP DummyChannel::GetOriginalURI(nsIURI**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetOriginalURI(nsIURI*)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetOwner(nsISupports**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetOwner(nsISupports*)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetNotificationCallbacks(nsIInterfaceRequestor**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetNotificationCallbacks(nsIInterfaceRequestor*)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetSecurityInfo(nsISupports**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentType(nsACString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetContentType(const nsACString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentCharset(nsACString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetContentCharset(const nsACString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentLength(int64_t*)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetContentLength(int64_t)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentDisposition(uint32_t*)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetContentDisposition(uint32_t)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetURI(nsIURI**)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentDispositionFilename(nsAString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::SetContentDispositionFilename(nsAString const &)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP DummyChannel::GetContentDispositionHeader(nsACString&)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/**
+  * app:// protocol implementation.
+  */
+
+AppProtocolHandler::AppProtocolHandler() {
+}
+
+AppProtocolHandler::~AppProtocolHandler() {
+  mAppInfoCache.Clear();
+}
+
+NS_IMPL_ISUPPORTS1(AppProtocolHandler, nsIProtocolHandler)
+
+/* static */
+nsresult
+AppProtocolHandler::Create(nsISupports* aOuter,
+                           const nsIID& aIID,
+                           void* *aResult)
+{
+  AppProtocolHandler* ph = new AppProtocolHandler();
+  if (ph == nullptr) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  NS_ADDREF(ph);
+  nsresult rv = ph->QueryInterface(aIID, aResult);
+  NS_RELEASE(ph);
+  return rv;
+}
+
+NS_IMETHODIMP
+AppProtocolHandler::GetScheme(nsACString &aResult)
+{
+  aResult.AssignLiteral("app");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AppProtocolHandler::GetDefaultPort(int32_t *aResult)
+{
+  // No ports for the app protocol.
+  *aResult = -1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AppProtocolHandler::GetProtocolFlags(uint32_t *aResult)
+{
+  *aResult = URI_NOAUTH |
+             URI_DANGEROUS_TO_LOAD |
+             URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AppProtocolHandler::NewURI(const nsACString &aSpec,
+                           const char *aCharset, // ignore charset info
+                           nsIURI *aBaseURI,
+                           nsIURI **result)
+{
+  nsresult rv;
+  nsCOMPtr<nsIStandardURL> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aCharset, aBaseURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURL> url(do_QueryInterface(surl, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  surl->SetMutable(false);
+  NS_ADDREF(*result = url);
+  return NS_OK;
+}
+
+// We map app://ABCDEF/path/to/file.ext to
+// jar:file:///path/to/profile/webapps/ABCDEF/application.zip!/path/to/file.ext
+NS_IMETHODIMP
+AppProtocolHandler::NewChannel(nsIURI* aUri, nsIChannel* *aResult)
+{
+  NS_ENSURE_ARG_POINTER(aUri);
+  nsJARChannel* channel = new nsJARChannel();
+  if (!channel) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsAutoCString host;
+  nsresult rv = aUri->GetHost(host);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString fileSpec;
+  nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
+  rv = url->GetFilePath(fileSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mozilla::dom::AppInfo appInfo;
+
+  if (!mAppInfoCache.Get(host, &appInfo)) {
+    nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+    if (!appsService) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JS::Value jsInfo;
+    rv = appsService->GetAppInfo(NS_ConvertUTF8toUTF16(host), &jsInfo);
+    if (NS_FAILED(rv)) {
+      // Return a DummyChannel.
+      delete channel;
+      NS_IF_ADDREF(*aResult = new DummyChannel());
+      return NS_OK;
+    }
+
+    mozilla::AutoSafeJSContext cx;
+    if (!appInfo.Init(cx, JS::Handle<JS::Value>::fromMarkedLocation(&jsInfo)) ||
+        appInfo.mPath.IsEmpty()) {
+      printf_stderr("!! No appInfo for %s\n", host.get());
+      // Return a DummyChannel.
+      delete channel;
+      NS_IF_ADDREF(*aResult = new DummyChannel());
+      return NS_OK;
+    }
+
+    mAppInfoCache.Put(host, appInfo);
+  }
+
+  bool noRemote = (appInfo.mIsCoreApp ||
+                   XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // In-parent and CoreApps can directly access files, so use jar:file://
+  nsAutoCString jarSpec(noRemote ? "jar:file://"
+                                 : "jar:remoteopenfile://");
+  jarSpec += NS_ConvertUTF16toUTF8(appInfo.mPath) +
+             NS_LITERAL_CSTRING("/application.zip!") +
+             fileSpec;
+
+  nsCOMPtr<nsIURI> jarURI;
+  rv = NS_NewURI(getter_AddRefs(jarURI),
+                 jarSpec, nullptr, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = channel->Init(jarURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = channel->SetAppURI(aUri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = channel->SetOriginalURI(aUri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ADDREF(*aResult = channel);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AppProtocolHandler::AllowPort(int32_t aPort, const char *aScheme, bool *aRetval)
+{
+  // No port allowed for this scheme.
+  *aRetval = false;
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/AppProtocolHandler.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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 AppProtocolHandler_
+#define AppProtocolHandler_
+
+#include "nsIProtocolHandler.h"
+#include "nsDataHashtable.h"
+#include "mozilla/dom/AppInfoBinding.h"
+
+class AppProtocolHandler : public nsIProtocolHandler
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // nsIProtocolHandler methods:
+  NS_DECL_NSIPROTOCOLHANDLER
+
+  // AppProtocolHandler methods:
+  AppProtocolHandler();
+  virtual ~AppProtocolHandler();
+
+  // Define a Create method to be used with a factory:
+  static nsresult Create(nsISupports* aOuter,
+                         const nsIID& aIID,
+                         void* *aResult);
+
+private:
+  nsDataHashtable<nsCStringHashKey, mozilla::dom::AppInfo> mAppInfoCache;
+};
+
+#endif /* AppProtocolHandler_ */
deleted file mode 100644
--- a/netwerk/protocol/app/AppProtocolHandler.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "appsService",
-                                   "@mozilla.org/AppsService;1",
-                                   "nsIAppsService");
-
-function AppProtocolHandler() {
-  this._appInfo = [];
-  this._runningInParent = Cc["@mozilla.org/xre/runtime;1"]
-                            .getService(Ci.nsIXULRuntime)
-                            .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-}
-
-AppProtocolHandler.prototype = {
-  classID: Components.ID("{b7ad6144-d344-4687-b2d0-b6b9dce1f07f}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
-
-  scheme: "app",
-  defaultPort: -1,
-  // Don't allow loading from other protocols, and only from app:// if webapps is granted
-  protocolFlags: Ci.nsIProtocolHandler.URI_NOAUTH |
-                 Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
-                 Ci.nsIProtocolHandler.URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
-
-  getAppInfo: function app_phGetAppInfo(aId) {
-
-    if (!this._appInfo[aId]) {
-      this._appInfo[aId] = appsService.getAppInfo(aId);
-    }
-    return this._appInfo[aId];
-  },
-
-  newURI: function app_phNewURI(aSpec, aOriginCharset, aBaseURI) {
-    let uri = Cc["@mozilla.org/network/standard-url;1"]
-              .createInstance(Ci.nsIStandardURL);
-    uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, aSpec, aOriginCharset,
-             aBaseURI);
-    return uri.QueryInterface(Ci.nsIURI);
-  },
-
-  newChannel: function app_phNewChannel(aURI) {
-    // We map app://ABCDEF/path/to/file.ext to
-    // jar:file:///path/to/profile/webapps/ABCDEF/application.zip!/path/to/file.ext
-    let url = aURI.QueryInterface(Ci.nsIURL);
-    let appId = aURI.host;
-    let fileSpec = url.filePath;
-
-    // Build a jar channel and masquerade as an app:// URI.
-    let appInfo = this.getAppInfo(appId);
-    if (!appInfo) {
-      // That should not happen, so dump() inconditionnally.
-      // We create a dummy channel instead of throwing to let the
-      // downstream user get a 404 error.
-      dump("!! got no appInfo for " + appId + "\n");
-      return new DummyChannel();
-    }
-
-    let uri;
-    if (this._runningInParent || appInfo.isCoreApp) {
-      // In-parent and CoreApps can directly access files, so use jar:file://
-      uri = "jar:file://" + appInfo.path + "/application.zip!" + fileSpec;
-    } else {
-      // non-CoreApps in child need to ask parent for file handle, use jar:ipcfile://
-      uri = "jar:remoteopenfile://" + appInfo.path + "/application.zip!" + fileSpec;
-    }
-    let channel = Services.io.newChannel(uri, null, null);
-    channel.QueryInterface(Ci.nsIJARChannel).setAppURI(aURI);
-    channel.QueryInterface(Ci.nsIChannel).originalURI = aURI;
-
-    return channel;
-  },
-
-  allowPort: function app_phAllowPort(aPort, aScheme) {
-    return false;
-  }
-};
-
-/**
-  * This dummy channel implementation only provides enough functionality
-  * to return a fake 404 error when the caller asks for an app:// URL
-  * containing an unknown appId.
-  */
-function DummyChannel() {
-  this.originalURI = Services.io.newURI("app://unknown/nothing.html", null, null);
-  this.URI = Services.io.newURI("app://unknown/nothing.html", null, null);
-}
-
-DummyChannel.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequest,
-                                         Ci.nsIChannel,
-                                         Ci.nsIJARChannel]),
-
-  // nsIRequest
-  name: "dummy_app_channel",
-
-  isPending: function dc_isPending() {
-    return this._pending;
-  },
-
-  status: Cr.NS_ERROR_FILE_NOT_FOUND,
-
-  cancel: function dc_cancel() {
-  },
-
-  suspend: function dc_suspend() {
-    this._suspendCount++;
-  },
-
-  resume: function dc_resume() {
-    if (this._suspendCount <= 0)
-      throw Cr.NS_ERROR_UNEXPECTED;
-
-    if (--this._suspendCount == 0 && this._pending) {
-      this._dispatch();
-    }
-  },
-
-  loadGroup: null,
-  loadFlags: Ci.nsIRequest.LOAD_NORMAL,
-
-  // nsIChannel
-  owner: null,
-  notificationCallbacks: null,
-  securityInfo: null,
-  contentType: null,
-  contentCharset: null,
-  contentLength: 0,
-  contentDisposition: Ci.nsIChannel.DISPOSITION_INLINE,
-  contentDispositionFilename: "",
-
-  _pending: false,
-  _suspendCount: 0,
-  _listener: null,
-  _context: null,
-
-  open: function dc_open() {
-    return Cr.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
-  _dispatch: function dc_dispatch() {
-    let request = this;
-
-    Services.tm.currentThread.dispatch(
-    {
-      run: function dc_run() {
-        request._listener.onStartRequest(request, request._context);
-        request._listener.onStopRequest(request, request._context,
-                                        Cr.NS_ERROR_FILE_NOT_FOUND);
-        if (request.loadGroup) {
-          request.loadGroup.removeRequest(request, request._context,
-                                          Cr.NS_ERROR_FILE_NOT_FOUND);
-        }
-        request._pending = false;
-        request.notificationCallbacks = null;
-        request._listener = null;
-        request._context = null;
-      }
-    },
-    Ci.nsIThread.DISPATCH_NORMAL);
-  },
-
-  asyncOpen: function dc_asyncopenfunction(aListener, aContext) {
-    if (this.loadGroup) {
-      this.loadGroup.addRequest(this, aContext);
-    }
-
-    this._listener = aListener;
-    this._context = aContext;
-    this._pending = true;
-
-    if (!this._suspended) {
-      this._dispatch();
-    }
-  },
-
-  // nsIJarChannel, needed for XHR to turn NS_ERROR_FILE_NOT_FOUND into
-  // a 404 error.
-  isUnsafe: false,
-
-  setAppURI: function(aURI) {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AppProtocolHandler]);
deleted file mode 100644
--- a/netwerk/protocol/app/AppProtocolHandler.manifest
+++ /dev/null
@@ -1,3 +0,0 @@
-# AppProtocolHander.js
-component {b7ad6144-d344-4687-b2d0-b6b9dce1f07f} AppProtocolHandler.js
-contract @mozilla.org/network/protocol;1?name=app {b7ad6144-d344-4687-b2d0-b6b9dce1f07f}
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/Makefile.in
@@ -0,0 +1,9 @@
+#
+# 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/.
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../../base/src \
+  -I$(srcdir)/../../../modules/libjar \
+  $(NULL)
--- a/netwerk/protocol/app/moz.build
+++ b/netwerk/protocol/app/moz.build
@@ -1,10 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-EXTRA_COMPONENTS += [
-    'AppProtocolHandler.js',
-    'AppProtocolHandler.manifest',
+MODULE = 'necko'
+
+CPP_SOURCES += [
+    'AppProtocolHandler.cpp',
 ]
+
+LIBRARY_NAME = 'nkapp_s'
+
+FAIL_ON_WARNINGS = True
+
+LIBXUL_LIBRARY = True
+