Bug 777445 - Network activity indicator for B2G, r=jduell
authorMichal Novotny <michal.novotny@gmail.com>
Sat, 29 Sep 2012 00:39:20 +0200
changeset 108526 de06aeb3c7f393dd03f920f7e38d6e16af1d0897
parent 108525 c19a02de09c4e42951e8061ce13d32fe3e6de697
child 108527 bd990a83194e76ef61ee2a5bf53d9e3f8ee32dc2
push id15571
push usermnovotny@mozilla.com
push dateFri, 28 Sep 2012 22:39:36 +0000
treeherdermozilla-inbound@de06aeb3c7f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs777445
milestone18.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 777445 - Network activity indicator for B2G, r=jduell
b2g/app/b2g.js
modules/libpref/src/init/all.js
netwerk/base/public/nsPISocketTransportService.idl
netwerk/base/src/Makefile.in
netwerk/base/src/NetworkActivityMonitor.cpp
netwerk/base/src/NetworkActivityMonitor.h
netwerk/base/src/nsSocketTransport2.cpp
netwerk/base/src/nsSocketTransportService2.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -554,8 +554,14 @@ pref("dom.disable_window_open_dialog_fea
 pref("accessibility.accessfu.activate", 2);
 
 // Enable hit-target fluffing
 pref("ui.touch.radius.enabled", true);
 pref("ui.mouse.radius.enabled", true);
 
 // Disable native prompt
 pref("browser.prompt.allowNative", false);
+
+// Minimum delay in milliseconds between network activity notifications (0 means
+// no notifications). The delay is the same for both download and upload, though
+// they are handled separately. This pref is only read once at startup:
+// a restart is required to enable a new value.
+pref("network.activity.blipIntervalMilliseconds", 250);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3744,8 +3744,14 @@ pref("toolkit.identity.debug", false);
 pref("dom.mozApps.dev_mode", false);
 
 // Lowest localId for apps.
 pref("dom.mozApps.maxLocalId", 1000);
 
 // Let us know wether we should run the permissions update algorithm.
 // See Bug 787439
 pref("dom.mozApps.runUpdate", true);
+
+// Minimum delay in milliseconds between network activity notifications (0 means
+// no notifications). The delay is the same for both download and upload, though
+// they are handled separately. This pref is only read once at startup:
+// a restart is required to enable a new value.
+pref("network.activity.blipIntervalMilliseconds", 0);
--- a/netwerk/base/public/nsPISocketTransportService.idl
+++ b/netwerk/base/public/nsPISocketTransportService.idl
@@ -32,8 +32,21 @@ interface nsPISocketTransportService : n
   readonly attribute long sendBufferSize;
 
   /**
    * Controls whether the socket transport service is offline.
    * Setting it offline will cause non-local socket detachment.
    */
   attribute boolean offline;
 };
+
+%{C++
+/*
+ * Network activity indicator: we send out these topics no more than every
+ * blipIntervalMilliseconds (as set by the
+ * "network.activity.blipIntervalMilliseconds" preference: if 0 no notifications
+ * are sent) if the network is currently active (i.e. we're sending/receiving
+ * data to/from the socket).
+ */
+#define NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC   "network-activity-blip-upload"
+#define NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC "network-activity-blip-download"
+
+%}
--- a/netwerk/base/src/Makefile.in
+++ b/netwerk/base/src/Makefile.in
@@ -62,16 +62,17 @@ CPPSRCS		= \
 		nsNetStrings.cpp \
 		nsBase64Encoder.cpp \
 		nsSerializationHelper.cpp \
 		nsDNSPrefetch.cpp \
 		RedirectChannelRegistrar.cpp \
 		nsPreloadedStream.cpp \
 		nsStreamListenerWrapper.cpp \
 		ProxyAutoConfig.cpp \
+		NetworkActivityMonitor.cpp \
 		$(NULL)
 
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/base
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 	CPPSRCS += nsURLHelperOS2.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/NetworkActivityMonitor.cpp
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NetworkActivityMonitor.h"
+#include "prmem.h"
+#include "nsIObserverService.h"
+#include "nsPISocketTransportService.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+#include "prerror.h"
+
+using namespace mozilla::net;
+
+static PRStatus
+nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+  PRStatus ret;
+  PRErrorCode code;
+  ret = fd->lower->methods->connect(fd->lower, addr, timeout);
+  if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR ||
+      code == PR_IN_PROGRESS_ERROR)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len)
+{
+  int32_t ret;
+  ret = fd->lower->methods->read(fd->lower, buf, len);
+  if (ret >= 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len)
+{
+  int32_t ret;
+  ret = fd->lower->methods->write(fd->lower, buf, len);
+  if (ret > 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_Writev(PRFileDesc *fd,
+                const PRIOVec *iov,
+                int32_t size,
+                PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
+  if (ret > 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_Recv(PRFileDesc *fd,
+              void *buf,
+              int32_t amount,
+              int flags,
+              PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
+  if (ret >= 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_Send(PRFileDesc *fd,
+              const void *buf,
+              int32_t amount,
+              int flags,
+              PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
+  if (ret > 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_RecvFrom(PRFileDesc *fd,
+                  void *buf,
+                  int32_t amount,
+                  int flags,
+                  PRNetAddr *addr,
+                  PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = fd->lower->methods->recvfrom(fd->lower,
+                                     buf,
+                                     amount,
+                                     flags,
+                                     addr,
+                                     timeout);
+  if (ret >= 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_SendTo(PRFileDesc *fd,
+                const void *buf,
+                int32_t amount,
+                int flags,
+                const PRNetAddr *addr,
+                PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = fd->lower->methods->sendto(fd->lower,
+                                   buf,
+                                   amount,
+                                   flags,
+                                   addr,
+                                   timeout);
+  if (ret > 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload);
+  return ret;
+}
+
+static int32_t
+nsNetMon_AcceptRead(PRFileDesc *listenSock,
+                    PRFileDesc **acceptedSock,
+                    PRNetAddr **peerAddr,
+                    void *buf,
+                    int32_t amount,
+                    PRIntervalTime timeout)
+{
+  int32_t ret;
+  ret = listenSock->lower->methods->acceptread(listenSock->lower,
+                                               acceptedSock,
+                                               peerAddr,
+                                               buf,
+                                               amount,
+                                               timeout);
+  if (ret > 0)
+    NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
+  return ret;
+}
+
+
+class NotifyNetworkActivity : public nsRunnable {
+public:
+  NotifyNetworkActivity(nsIObserverService* aObs,
+                        NetworkActivityMonitor::Direction aDirection)
+    : mObs(aObs)
+    , mDirection(aDirection)
+  {}
+  NS_IMETHOD Run()
+  {
+    mObs->NotifyObservers(nullptr,
+                          mDirection == NetworkActivityMonitor::kUpload
+                            ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC
+                            : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC,
+                          nullptr);
+    return NS_OK;
+  }
+private:
+  nsCOMPtr<nsIObserverService>      mObs;
+  NetworkActivityMonitor::Direction mDirection;
+};
+
+NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr;
+
+NetworkActivityMonitor::NetworkActivityMonitor()
+  : mLayerIdentity(PR_INVALID_IO_LAYER)
+  , mBlipInterval(PR_INTERVAL_NO_TIMEOUT)
+{
+  NS_ASSERTION(gInstance==nullptr,
+               "multiple NetworkActivityMonitor instances!");
+}
+
+NetworkActivityMonitor::~NetworkActivityMonitor()
+{
+  gInstance = nullptr;
+}
+
+nsresult
+NetworkActivityMonitor::Init(int32_t blipInterval)
+{
+  nsresult rv;
+
+  if (gInstance)
+    return NS_ERROR_ALREADY_INITIALIZED;
+
+  NetworkActivityMonitor * mon = new NetworkActivityMonitor();
+  rv = mon->Init_Internal(blipInterval);
+  if (NS_FAILED(rv)) {
+    delete mon;
+    return rv;
+  }
+
+  gInstance = mon;
+  return NS_OK;
+}
+
+nsresult
+NetworkActivityMonitor::Shutdown()
+{
+  if (!gInstance)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  delete gInstance;
+  return NS_OK;
+}
+
+nsresult
+NetworkActivityMonitor::Init_Internal(int32_t blipInterval)
+{
+  mLayerIdentity = PR_GetUniqueIdentity("network activity monitor layer");
+  mLayerMethods  = *PR_GetDefaultIOMethods();
+  mLayerMethods.connect    = nsNetMon_Connect;
+  mLayerMethods.read       = nsNetMon_Read;
+  mLayerMethods.write      = nsNetMon_Write;
+  mLayerMethods.writev     = nsNetMon_Writev;
+  mLayerMethods.recv       = nsNetMon_Recv;
+  mLayerMethods.send       = nsNetMon_Send;
+  mLayerMethods.recvfrom   = nsNetMon_RecvFrom;
+  mLayerMethods.sendto     = nsNetMon_SendTo;
+  mLayerMethods.acceptread = nsNetMon_AcceptRead;
+
+  mObserverService = mozilla::services::GetObserverService();
+  if (!mObserverService)
+    return NS_ERROR_FAILURE;
+
+  mBlipInterval = PR_MillisecondsToInterval(blipInterval);
+  // Set the last notification times to time that has just expired, so any
+  // activity even right now will trigger notification.
+  mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval;
+  mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload];
+
+  return NS_OK;
+}
+
+nsresult
+NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd)
+{
+  if (!gInstance)
+    return NS_OK;
+
+  PRFileDesc * layer;
+  PRStatus     status;
+
+  layer = PR_CreateIOLayerStub(gInstance->mLayerIdentity,
+                               &gInstance->mLayerMethods);
+  if (!layer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer);
+
+  if (status == PR_FAILURE) {
+    PR_DELETE(layer);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+NetworkActivityMonitor::DataInOut(Direction direction)
+{
+  NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  if (gInstance) {
+    PRIntervalTime now = PR_IntervalNow();
+    if ((now - gInstance->mLastNotificationTime[direction]) >
+        gInstance->mBlipInterval) {
+      gInstance->mLastNotificationTime[direction] = now;
+      gInstance->PostNotification(direction);
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+NetworkActivityMonitor::PostNotification(Direction direction)
+{
+  nsRefPtr<nsIRunnable> ev = new NotifyNetworkActivity(mObserverService,
+                                                       direction);
+  NS_DispatchToMainThread(ev);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/NetworkActivityMonitor.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 NetworkActivityMonitor_h___
+#define NetworkActivityMonitor_h___
+
+#include "nsCOMPtr.h"
+#include "prio.h"
+#include "prinrval.h"
+
+class nsIObserverService;
+
+namespace mozilla { namespace net {
+
+class NetworkActivityMonitor
+{
+public:
+  enum Direction {
+    kUpload   = 0,
+    kDownload = 1
+  };
+
+  NetworkActivityMonitor();
+  ~NetworkActivityMonitor();
+
+  static nsresult Init(int32_t blipInterval);
+  static nsresult Shutdown();
+
+  static nsresult AttachIOLayer(PRFileDesc *fd);
+  static nsresult DataInOut(Direction direction);
+
+private:
+  nsresult Init_Internal(int32_t blipInterval);
+  void PostNotification(Direction direction);
+
+  static NetworkActivityMonitor * gInstance;
+  PRDescIdentity                  mLayerIdentity;
+  PRIOMethods                     mLayerMethods;
+  PRIntervalTime                  mBlipInterval;
+  PRIntervalTime                  mLastNotificationTime[2];
+  nsCOMPtr<nsIObserverService>    mObserverService;
+};
+
+}} // namespace mozilla::net
+
+#endif /* NetworkActivityMonitor_h___ */
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -19,16 +19,17 @@
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "netCore.h"
 #include "prmem.h"
 #include "plstr.h"
 #include "prnetdb.h"
 #include "prerror.h"
 #include "prerr.h"
+#include "NetworkActivityMonitor.h"
 
 #include "nsIServiceManager.h"
 #include "nsISocketProviderService.h"
 #include "nsISocketProvider.h"
 #include "nsISSLSocketControl.h"
 #include "nsINSSErrorsService.h"
 #include "nsIPipe.h"
 #include "nsIProgrammingLanguage.h"
@@ -1090,16 +1091,19 @@ nsSocketTransport::InitiateSocket()
     bool usingSSL;
 
     rv = BuildSocket(fd, proxyTransparent, usingSSL);
     if (NS_FAILED(rv)) {
         SOCKET_LOG(("  BuildSocket failed [rv=%x]\n", rv));
         return rv;
     }
 
+    // Attach network activity monitor
+    mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd);
+
     PRStatus status;
 
     // Make the socket non-blocking...
     PRSocketOptionData opt;
     opt.option = PR_SockOpt_Nonblocking;
     opt.value.non_blocking = true;
     status = PR_SetSocketOption(fd, &opt);
     NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -13,16 +13,17 @@
 #include "nsError.h"
 #include "prnetdb.h"
 #include "prerror.h"
 #include "plstr.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIOService.h"
+#include "NetworkActivityMonitor.h"
 
 
 // XXX: There is no good header file to put these in. :(
 namespace mozilla { namespace psm {
 
 void InitializeSSLServerCertVerificationThreads();
 void StopSSLServerCertVerificationThreads();
 
@@ -35,16 +36,17 @@ PRLogModuleInfo *gSocketTransportLog = n
 #endif
 
 nsSocketTransportService *gSocketTransportService = nullptr;
 PRThread                 *gSocketThread           = nullptr;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define SOCKET_LIMIT_TARGET 550U
 #define SOCKET_LIMIT_MIN     50U
+#define BLIB_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
 
 uint32_t nsSocketTransportService::gMaxCount;
 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
@@ -450,18 +452,28 @@ nsSocketTransportService::Init()
     
     {
         MutexAutoLock lock(mLock);
         // Install our mThread, protecting against concurrent readers
         thread.swap(mThread);
     }
 
     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
-    if (tmpPrefService) 
+    if (tmpPrefService) {
         tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
+
+        int32_t blipInterval = 0;
+        rv = tmpPrefService->GetIntPref(BLIB_INTERVAL_PREF, &blipInterval);
+        if (NS_SUCCEEDED(rv) && blipInterval > 0) {
+            rv = mozilla::net::NetworkActivityMonitor::Init(blipInterval);
+            if (NS_FAILED(rv)) {
+                NS_WARNING("Can't initialize NetworkActivityMonitor");
+            }
+        }
+    }
     UpdatePrefs();
 
     mInitialized = true;
     return NS_OK;
 }
 
 // called from main thread only
 NS_IMETHODIMP
@@ -496,16 +508,18 @@ nsSocketTransportService::Shutdown()
         // readers are excluded
         mThread = nullptr;
     }
 
     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (tmpPrefService) 
         tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
 
+    mozilla::net::NetworkActivityMonitor::Shutdown();
+
     mInitialized = false;
     mShuttingDown = false;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::GetOffline(bool *offline)