tenfourfox/netwerk/base/NetworkActivityMonitor.cpp

301 lines
8.5 KiB
C++

/* -*- 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:
explicit NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection)
: mDirection(aDirection)
{}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs)
return NS_ERROR_FAILURE;
obs->NotifyObservers(nullptr,
mDirection == NetworkActivityMonitor::kUpload
? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC
: NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC,
nullptr);
return NS_OK;
}
private:
NetworkActivityMonitor::Direction mDirection;
};
NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr;
static PRDescIdentity sNetActivityMonitorLayerIdentity;
static PRIOMethods sNetActivityMonitorLayerMethods;
static PRIOMethods *sNetActivityMonitorLayerMethodsPtr = nullptr;
NetworkActivityMonitor::NetworkActivityMonitor()
: mBlipInterval(PR_INTERVAL_NO_TIMEOUT)
{
MOZ_COUNT_CTOR(NetworkActivityMonitor);
NS_ASSERTION(gInstance==nullptr,
"multiple NetworkActivityMonitor instances!");
}
NetworkActivityMonitor::~NetworkActivityMonitor()
{
MOZ_COUNT_DTOR(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)
{
if (!sNetActivityMonitorLayerMethodsPtr) {
sNetActivityMonitorLayerIdentity =
PR_GetUniqueIdentity("network activity monitor layer");
sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods();
sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect;
sNetActivityMonitorLayerMethods.read = nsNetMon_Read;
sNetActivityMonitorLayerMethods.write = nsNetMon_Write;
sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev;
sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
}
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(sNetActivityMonitorLayerIdentity,
sNetActivityMonitorLayerMethodsPtr);
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)
{
nsCOMPtr<nsIRunnable> ev = new NotifyNetworkActivity(direction);
NS_DispatchToMainThread(ev);
}