diff -r 000000000000 -r 6474c204b198 netwerk/base/src/Dashboard.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netwerk/base/src/Dashboard.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,920 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http:mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/NetDashboardBinding.h" +#include "mozilla/net/Dashboard.h" +#include "mozilla/net/HttpInfo.h" +#include "nsCxPusher.h" +#include "nsHttp.h" +#include "nsICancelable.h" +#include "nsIDNSService.h" +#include "nsIDNSRecord.h" +#include "nsIInputStream.h" +#include "nsISocketTransport.h" +#include "nsIThread.h" +#include "nsProxyRelease.h" +#include "nsSocketTransportService2.h" +#include "nsThreadUtils.h" +#include "nsURLHelper.h" + +using mozilla::AutoSafeJSContext; +using mozilla::dom::Sequence; + +namespace mozilla { +namespace net { + +class SocketData + : public nsISupports +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + SocketData() + { + mTotalSent = 0; + mTotalRecv = 0; + mThread = nullptr; + } + + virtual ~SocketData() + { + } + + uint64_t mTotalSent; + uint64_t mTotalRecv; + nsTArray mData; + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; +}; + +NS_IMPL_ISUPPORTS0(SocketData) + + +class HttpData + : public nsISupports +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + HttpData() + { + mThread = nullptr; + } + + virtual ~HttpData() + { + } + + nsTArray mData; + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; +}; + +NS_IMPL_ISUPPORTS0(HttpData) + + +class WebSocketRequest + : public nsISupports +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + WebSocketRequest() + { + mThread = nullptr; + } + + virtual ~WebSocketRequest() + { + } + + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; +}; + +NS_IMPL_ISUPPORTS0(WebSocketRequest) + + +class DnsData + : public nsISupports +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + DnsData() + { + mThread = nullptr; + } + + virtual ~DnsData() + { + } + + nsTArray mData; + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; +}; + +NS_IMPL_ISUPPORTS0(DnsData) + + +class ConnectionData + : public nsITransportEventSink + , public nsITimerCallback +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITRANSPORTEVENTSINK + NS_DECL_NSITIMERCALLBACK + + void StartTimer(uint32_t aTimeout); + void StopTimer(); + + ConnectionData(Dashboard *target) + { + mThread = nullptr; + mDashboard = target; + } + + virtual ~ConnectionData() + { + if (mTimer) { + mTimer->Cancel(); + } + } + + nsCOMPtr mSocket; + nsCOMPtr mStreamIn; + nsCOMPtr mTimer; + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; + Dashboard *mDashboard; + + nsCString mHost; + uint32_t mPort; + const char *mProtocol; + uint32_t mTimeout; + + nsString mStatus; +}; + +NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback) + +NS_IMETHODIMP +ConnectionData::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, + uint64_t aProgress, uint64_t aProgressMax) +{ + if (aStatus == NS_NET_STATUS_CONNECTED_TO) { + StopTimer(); + } + + CopyASCIItoUTF16(Dashboard::GetErrorString(aStatus), mStatus); + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (mDashboard, &Dashboard::GetConnectionStatus, this); + mThread->Dispatch(event, NS_DISPATCH_NORMAL); + + return NS_OK; +} + +NS_IMETHODIMP +ConnectionData::Notify(nsITimer *aTimer) +{ + MOZ_ASSERT(aTimer == mTimer); + + if (mSocket) { + mSocket->Close(NS_ERROR_ABORT); + mSocket = nullptr; + mStreamIn = nullptr; + } + + mTimer = nullptr; + + mStatus.Assign(NS_LITERAL_STRING("NS_ERROR_NET_TIMEOUT")); + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (mDashboard, &Dashboard::GetConnectionStatus, this); + mThread->Dispatch(event, NS_DISPATCH_NORMAL); + + return NS_OK; +} + +void +ConnectionData::StartTimer(uint32_t aTimeout) +{ + if (!mTimer) { + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + } + + mTimer->InitWithCallback(this, aTimeout * 1000, + nsITimer::TYPE_ONE_SHOT); +} + +void +ConnectionData::StopTimer() +{ + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } +} + + +class LookupHelper; + +class LookupArgument + : public nsISupports +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + LookupArgument(nsIDNSRecord *aRecord, LookupHelper *aHelper) + { + mRecord = aRecord; + mHelper = aHelper; + } + + virtual ~LookupArgument() + { + } + + nsCOMPtr mRecord; + nsRefPtr mHelper; +}; + +NS_IMPL_ISUPPORTS0(LookupArgument) + + +class LookupHelper + : public nsIDNSListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + + LookupHelper() { + } + + virtual ~LookupHelper() + { + if (mCancel) { + mCancel->Cancel(NS_ERROR_ABORT); + } + } + + nsresult ConstructAnswer(LookupArgument *aArgument); +public: + nsCOMPtr mCancel; + nsMainThreadPtrHandle mCallback; + nsIThread *mThread; + nsresult mStatus; +}; + +NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSListener) + +NS_IMETHODIMP +LookupHelper::OnLookupComplete(nsICancelable *aRequest, + nsIDNSRecord *aRecord, nsresult aStatus) +{ + MOZ_ASSERT(aRequest == mCancel); + mCancel = nullptr; + mStatus = aStatus; + + nsRefPtr arg = new LookupArgument(aRecord, this); + nsCOMPtr event = + NS_NewRunnableMethodWithArg >( + this, &LookupHelper::ConstructAnswer, arg); + mThread->Dispatch(event, NS_DISPATCH_NORMAL); + + return NS_OK; +} + +nsresult +LookupHelper::ConstructAnswer(LookupArgument *aArgument) +{ + + nsIDNSRecord *aRecord = aArgument->mRecord; + AutoSafeJSContext cx; + + mozilla::dom::DNSLookupDict dict; + dict.mAddress.Construct(); + + Sequence &addresses = dict.mAddress.Value(); + + if (NS_SUCCEEDED(mStatus)) { + dict.mAnswer = true; + bool hasMore; + aRecord->HasMore(&hasMore); + while (hasMore) { + nsCString nextAddress; + aRecord->GetNextAddrAsString(nextAddress); + CopyASCIItoUTF16(nextAddress, *addresses.AppendElement()); + aRecord->HasMore(&hasMore); + } + } else { + dict.mAnswer = false; + CopyASCIItoUTF16(Dashboard::GetErrorString(mStatus), dict.mError); + } + + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) { + return NS_ERROR_FAILURE; + } + + this->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier) + +Dashboard::Dashboard() +{ + mEnableLogging = false; +} + +Dashboard::~Dashboard() +{ +} + +NS_IMETHODIMP +Dashboard::RequestSockets(NetDashboardCallback *aCallback) +{ + nsRefPtr socketData = new SocketData(); + socketData->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + socketData->mThread = NS_GetCurrentThread(); + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetSocketsDispatch, socketData); + gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetSocketsDispatch(SocketData *aSocketData) +{ + nsRefPtr socketData = aSocketData; + if (gSocketTransportService) { + gSocketTransportService->GetSocketConnections(&socketData->mData); + socketData->mTotalSent = gSocketTransportService->GetSentBytes(); + socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes(); + } + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetSockets, socketData); + socketData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetSockets(SocketData *aSocketData) +{ + nsRefPtr socketData = aSocketData; + AutoSafeJSContext cx; + + mozilla::dom::SocketsDict dict; + dict.mSockets.Construct(); + dict.mSent = 0; + dict.mReceived = 0; + + Sequence &sockets = dict.mSockets.Value(); + + uint32_t length = socketData->mData.Length(); + if (!sockets.SetCapacity(length)) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < socketData->mData.Length(); i++) { + mozilla::dom::SocketElement &mSocket = *sockets.AppendElement(); + CopyASCIItoUTF16(socketData->mData[i].host, mSocket.mHost); + mSocket.mPort = socketData->mData[i].port; + mSocket.mActive = socketData->mData[i].active; + mSocket.mTcp = socketData->mData[i].tcp; + mSocket.mSent = (double) socketData->mData[i].sent; + mSocket.mReceived = (double) socketData->mData[i].received; + dict.mSent += socketData->mData[i].sent; + dict.mReceived += socketData->mData[i].received; + } + + dict.mSent += socketData->mTotalSent; + dict.mReceived += socketData->mTotalRecv; + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) + return NS_ERROR_FAILURE; + socketData->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::RequestHttpConnections(NetDashboardCallback *aCallback) +{ + nsRefPtr httpData = new HttpData(); + httpData->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + httpData->mThread = NS_GetCurrentThread(); + + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetHttpDispatch, httpData); + gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetHttpDispatch(HttpData *aHttpData) +{ + nsRefPtr httpData = aHttpData; + HttpInfo::GetHttpConnectionData(&httpData->mData); + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetHttpConnections, httpData); + httpData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + + +nsresult +Dashboard::GetHttpConnections(HttpData *aHttpData) +{ + nsRefPtr httpData = aHttpData; + AutoSafeJSContext cx; + + mozilla::dom::HttpConnDict dict; + dict.mConnections.Construct(); + + using mozilla::dom::HalfOpenInfoDict; + using mozilla::dom::HttpConnectionElement; + using mozilla::dom::HttpConnInfo; + Sequence &connections = dict.mConnections.Value(); + + uint32_t length = httpData->mData.Length(); + if (!connections.SetCapacity(length)) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < httpData->mData.Length(); i++) { + HttpConnectionElement &connection = *connections.AppendElement(); + + CopyASCIItoUTF16(httpData->mData[i].host, connection.mHost); + connection.mPort = httpData->mData[i].port; + connection.mSpdy = httpData->mData[i].spdy; + connection.mSsl = httpData->mData[i].ssl; + + connection.mActive.Construct(); + connection.mIdle.Construct(); + connection.mHalfOpens.Construct(); + + Sequence &active = connection.mActive.Value(); + Sequence &idle = connection.mIdle.Value(); + Sequence &halfOpens = connection.mHalfOpens.Value(); + + if (!active.SetCapacity(httpData->mData[i].active.Length()) || + !idle.SetCapacity(httpData->mData[i].idle.Length()) || + !halfOpens.SetCapacity(httpData->mData[i].halfOpens.Length())) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t j = 0; j < httpData->mData[i].active.Length(); j++) { + HttpConnInfo &info = *active.AppendElement(); + info.mRtt = httpData->mData[i].active[j].rtt; + info.mTtl = httpData->mData[i].active[j].ttl; + info.mProtocolVersion = + httpData->mData[i].active[j].protocolVersion; + } + + for (uint32_t j = 0; j < httpData->mData[i].idle.Length(); j++) { + HttpConnInfo &info = *idle.AppendElement(); + info.mRtt = httpData->mData[i].idle[j].rtt; + info.mTtl = httpData->mData[i].idle[j].ttl; + info.mProtocolVersion = httpData->mData[i].idle[j].protocolVersion; + } + + for (uint32_t j = 0; j < httpData->mData[i].halfOpens.Length(); j++) { + HalfOpenInfoDict &info = *halfOpens.AppendElement(); + info.mSpeculative = httpData->mData[i].halfOpens[j].speculative; + } + } + + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) { + return NS_ERROR_FAILURE; + } + + httpData->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::GetEnableLogging(bool *value) +{ + *value = mEnableLogging; + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::SetEnableLogging(const bool value) +{ + mEnableLogging = value; + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted) +{ + if (mEnableLogging) { + mozilla::MutexAutoLock lock(mWs.lock); + LogData mData(nsCString(aHost), aSerial, aEncrypted); + if (mWs.data.Contains(mData)) { + return NS_OK; + } + if (!mWs.data.AppendElement(mData)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial) +{ + if (mEnableLogging) { + mozilla::MutexAutoLock lock(mWs.lock); + int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); + if (index == -1) + return NS_ERROR_FAILURE; + mWs.data.RemoveElementAt(index); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial, uint32_t aLength) +{ + if (mEnableLogging) { + mozilla::MutexAutoLock lock(mWs.lock); + int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); + if (index == -1) + return NS_ERROR_FAILURE; + mWs.data[index].mMsgSent++; + mWs.data[index].mSizeSent += aLength; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial, uint32_t aLength) +{ + if (mEnableLogging) { + mozilla::MutexAutoLock lock(mWs.lock); + int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); + if (index == -1) + return NS_ERROR_FAILURE; + mWs.data[index].mMsgReceived++; + mWs.data[index].mSizeReceived += aLength; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +Dashboard::RequestWebsocketConnections(NetDashboardCallback *aCallback) +{ + nsRefPtr wsRequest = new WebSocketRequest(); + wsRequest->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + wsRequest->mThread = NS_GetCurrentThread(); + + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetWebSocketConnections, wsRequest); + wsRequest->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetWebSocketConnections(WebSocketRequest *aWsRequest) +{ + nsRefPtr wsRequest = aWsRequest; + AutoSafeJSContext cx; + + mozilla::dom::WebSocketDict dict; + dict.mWebsockets.Construct(); + Sequence &websockets = + dict.mWebsockets.Value(); + + mozilla::MutexAutoLock lock(mWs.lock); + uint32_t length = mWs.data.Length(); + if (!websockets.SetCapacity(length)) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < mWs.data.Length(); i++) { + mozilla::dom::WebSocketElement &websocket = *websockets.AppendElement(); + CopyASCIItoUTF16(mWs.data[i].mHost, websocket.mHostport); + websocket.mMsgsent = mWs.data[i].mMsgSent; + websocket.mMsgreceived = mWs.data[i].mMsgReceived; + websocket.mSentsize = mWs.data[i].mSizeSent; + websocket.mReceivedsize = mWs.data[i].mSizeReceived; + websocket.mEncrypted = mWs.data[i].mEncrypted; + } + + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) { + return NS_ERROR_FAILURE; + } + wsRequest->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::RequestDNSInfo(NetDashboardCallback *aCallback) +{ + nsRefPtr dnsData = new DnsData(); + dnsData->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + + nsresult rv; + dnsData->mData.Clear(); + dnsData->mThread = NS_GetCurrentThread(); + + if (!mDnsService) { + mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); + if (NS_FAILED(rv)) { + return rv; + } + } + + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetDnsInfoDispatch, dnsData); + gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetDnsInfoDispatch(DnsData *aDnsData) +{ + nsRefPtr dnsData = aDnsData; + if (mDnsService) { + mDnsService->GetDNSCacheEntries(&dnsData->mData); + } + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetDNSCacheEntries, dnsData); + dnsData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +Dashboard::GetDNSCacheEntries(DnsData *dnsData) +{ + AutoSafeJSContext cx; + + mozilla::dom::DNSCacheDict dict; + dict.mEntries.Construct(); + Sequence &entries = dict.mEntries.Value(); + + uint32_t length = dnsData->mData.Length(); + if (!entries.SetCapacity(length)) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < dnsData->mData.Length(); i++) { + mozilla::dom::DnsCacheEntry &entry = *entries.AppendElement(); + entry.mHostaddr.Construct(); + + Sequence &addrs = entry.mHostaddr.Value(); + if (!addrs.SetCapacity(dnsData->mData[i].hostaddr.Length())) { + JS_ReportOutOfMemory(cx); + return NS_ERROR_OUT_OF_MEMORY; + } + + CopyASCIItoUTF16(dnsData->mData[i].hostname, entry.mHostname); + entry.mExpiration = dnsData->mData[i].expiration; + + for (uint32_t j = 0; j < dnsData->mData[i].hostaddr.Length(); j++) { + CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], + *addrs.AppendElement()); + } + + if (dnsData->mData[i].family == PR_AF_INET6) { + CopyASCIItoUTF16("ipv6", entry.mFamily); + } else { + CopyASCIItoUTF16("ipv4", entry.mFamily); + } + } + + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) { + return NS_ERROR_FAILURE; + } + dnsData->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +NS_IMETHODIMP +Dashboard::RequestDNSLookup(const nsACString &aHost, + NetDashboardCallback *aCallback) +{ + nsresult rv; + + if (!mDnsService) { + mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); + if (NS_FAILED(rv)) { + return rv; + } + } + + nsRefPtr helper = new LookupHelper(); + helper->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + helper->mThread = NS_GetCurrentThread(); + rv = mDnsService->AsyncResolve(aHost, 0, helper.get(), + NS_GetCurrentThread(), + getter_AddRefs(helper->mCancel)); + return rv; +} + +void +HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv) +{ + switch (pv) { + case NS_HTTP_VERSION_0_9: + protocolVersion.Assign(NS_LITERAL_STRING("http/0.9")); + break; + case NS_HTTP_VERSION_1_0: + protocolVersion.Assign(NS_LITERAL_STRING("http/1.0")); + break; + case NS_HTTP_VERSION_1_1: + protocolVersion.Assign(NS_LITERAL_STRING("http/1.1")); + break; + case NS_HTTP_VERSION_2_0: + protocolVersion.Assign(NS_LITERAL_STRING("http/2.0")); + break; + default: + protocolVersion.Assign(NS_LITERAL_STRING("unknown protocol version")); + } +} + +void +HttpConnInfo::SetHTTP2ProtocolVersion(uint8_t pv) +{ + if (pv == SPDY_VERSION_3) { + protocolVersion.Assign(NS_LITERAL_STRING("spdy/3")); + } else if (pv == SPDY_VERSION_31) { + protocolVersion.Assign(NS_LITERAL_STRING("spdy/3.1")); + } else { + MOZ_ASSERT (pv == NS_HTTP2_DRAFT_VERSION); + protocolVersion.Assign(NS_LITERAL_STRING(NS_HTTP2_DRAFT_TOKEN)); + } +} + +NS_IMETHODIMP +Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort, + const char *aProtocol, uint32_t aTimeout, + NetDashboardCallback *aCallback) +{ + nsresult rv; + nsRefPtr connectionData = new ConnectionData(this); + connectionData->mHost = aHost; + connectionData->mPort = aPort; + connectionData->mProtocol = aProtocol; + connectionData->mTimeout = aTimeout; + + connectionData->mCallback = + new nsMainThreadPtrHolder(aCallback, true); + connectionData->mThread = NS_GetCurrentThread(); + + rv = TestNewConnection(connectionData); + if (NS_FAILED(rv)) { + CopyASCIItoUTF16(GetErrorString(rv), connectionData->mStatus); + nsCOMPtr event = + NS_NewRunnableMethodWithArg > + (this, &Dashboard::GetConnectionStatus, connectionData); + connectionData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + return rv; + } + + return NS_OK; +} + +nsresult +Dashboard::GetConnectionStatus(ConnectionData *aConnectionData) +{ + nsRefPtr connectionData = aConnectionData; + AutoSafeJSContext cx; + + mozilla::dom::ConnStatusDict dict; + dict.mStatus = connectionData->mStatus; + + JS::RootedValue val(cx); + if (!dict.ToObject(cx, &val)) + return NS_ERROR_FAILURE; + + connectionData->mCallback->OnDashboardDataAvailable(val); + + return NS_OK; +} + +nsresult +Dashboard::TestNewConnection(ConnectionData *aConnectionData) +{ + nsRefPtr connectionData = aConnectionData; + + nsresult rv; + if (!connectionData->mHost.Length() || + !net_IsValidHostName(connectionData->mHost)) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (connectionData->mProtocol && + NS_LITERAL_STRING("ssl").EqualsASCII(connectionData->mProtocol)) { + rv = gSocketTransportService->CreateTransport( + &connectionData->mProtocol, 1, connectionData->mHost, + connectionData->mPort, nullptr, + getter_AddRefs(connectionData->mSocket)); + } else { + rv = gSocketTransportService->CreateTransport( + nullptr, 0, connectionData->mHost, + connectionData->mPort, nullptr, + getter_AddRefs(connectionData->mSocket)); + } + if (NS_FAILED(rv)) { + return rv; + } + + rv = connectionData->mSocket->SetEventSink(connectionData, + NS_GetCurrentThread()); + if (NS_FAILED(rv)) { + return rv; + } + + rv = connectionData->mSocket->OpenInputStream( + nsITransport::OPEN_BLOCKING, 0, 0, + getter_AddRefs(connectionData->mStreamIn)); + if (NS_FAILED(rv)) { + return rv; + } + + connectionData->StartTimer(connectionData->mTimeout); + + return rv; +} + +typedef struct +{ + nsresult key; + const char *error; +} ErrorEntry; + +#undef ERROR +#define ERROR(key, val) {key, #key} + +ErrorEntry errors[] = { + #include "ErrorList.h" +}; + +ErrorEntry socketTransportStatuses[] = { + ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), + ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), + ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), + ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), + ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), + ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), + ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), +}; +#undef ERROR + +const char * +Dashboard::GetErrorString(nsresult rv) +{ + int length = sizeof(socketTransportStatuses) / sizeof(ErrorEntry); + for (int i = 0;i < length;i++) + if (socketTransportStatuses[i].key == rv) { + return socketTransportStatuses[i].error; + } + + length = sizeof(errors) / sizeof(ErrorEntry); + for (int i = 0;i < length;i++) + if (errors[i].key == rv) { + return errors[i].error; + } + + return nullptr; +} + +} } // namespace mozilla::net