Bug 1845134 - Part 4: Update existing ui-icons to use the latest source from acorn...
[gecko.git] / netwerk / base / Dashboard.cpp
blobbc1619ee6095a4313aa71ce9d128fc20d6ccf4c1
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http:mozilla.org/MPL/2.0/. */
5 #include "mozilla/dom/NetDashboardBinding.h"
6 #include "mozilla/dom/ToJSValue.h"
7 #include "mozilla/ErrorNames.h"
8 #include "mozilla/net/Dashboard.h"
9 #include "mozilla/net/HttpInfo.h"
10 #include "mozilla/net/HTTPSSVC.h"
11 #include "mozilla/net/SocketProcessParent.h"
12 #include "nsHttp.h"
13 #include "nsICancelable.h"
14 #include "nsIDNSListener.h"
15 #include "nsIDNSService.h"
16 #include "nsIDNSRecord.h"
17 #include "nsIDNSByTypeRecord.h"
18 #include "nsIInputStream.h"
19 #include "nsINamed.h"
20 #include "nsINetAddr.h"
21 #include "nsISocketTransport.h"
22 #include "nsProxyRelease.h"
23 #include "nsSocketTransportService2.h"
24 #include "nsThreadUtils.h"
25 #include "nsURLHelper.h"
26 #include "mozilla/Logging.h"
27 #include "nsIOService.h"
28 #include "../cache2/CacheFileUtils.h"
30 using mozilla::AutoSafeJSContext;
31 using mozilla::dom::Sequence;
32 using mozilla::dom::ToJSValue;
34 namespace mozilla {
35 namespace net {
37 class SocketData : public nsISupports {
38 public:
39 NS_DECL_THREADSAFE_ISUPPORTS
41 SocketData() = default;
43 uint64_t mTotalSent{0};
44 uint64_t mTotalRecv{0};
45 nsTArray<SocketInfo> mData;
46 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
47 nsIEventTarget* mEventTarget{nullptr};
49 private:
50 virtual ~SocketData() = default;
53 static void GetErrorString(nsresult rv, nsAString& errorString);
55 NS_IMPL_ISUPPORTS0(SocketData)
57 class HttpData : public nsISupports {
58 virtual ~HttpData() = default;
60 public:
61 NS_DECL_THREADSAFE_ISUPPORTS
63 HttpData() = default;
65 nsTArray<HttpRetParams> mData;
66 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
67 nsIEventTarget* mEventTarget{nullptr};
70 NS_IMPL_ISUPPORTS0(HttpData)
72 class WebSocketRequest : public nsISupports {
73 virtual ~WebSocketRequest() = default;
75 public:
76 NS_DECL_THREADSAFE_ISUPPORTS
78 WebSocketRequest() = default;
80 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
81 nsIEventTarget* mEventTarget{nullptr};
84 NS_IMPL_ISUPPORTS0(WebSocketRequest)
86 class DnsData : public nsISupports {
87 virtual ~DnsData() = default;
89 public:
90 NS_DECL_THREADSAFE_ISUPPORTS
92 DnsData() = default;
94 nsTArray<DNSCacheEntries> mData;
95 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
96 nsIEventTarget* mEventTarget{nullptr};
99 NS_IMPL_ISUPPORTS0(DnsData)
101 class ConnectionData : public nsITransportEventSink,
102 public nsITimerCallback,
103 public nsINamed {
104 virtual ~ConnectionData() {
105 if (mTimer) {
106 mTimer->Cancel();
110 public:
111 NS_DECL_THREADSAFE_ISUPPORTS
112 NS_DECL_NSITRANSPORTEVENTSINK
113 NS_DECL_NSITIMERCALLBACK
115 NS_IMETHOD GetName(nsACString& aName) override {
116 aName.AssignLiteral("net::ConnectionData");
117 return NS_OK;
120 void StartTimer(uint32_t aTimeout);
121 void StopTimer();
123 explicit ConnectionData(Dashboard* target) { mDashboard = target; }
125 nsCOMPtr<nsISocketTransport> mSocket;
126 nsCOMPtr<nsIInputStream> mStreamIn;
127 nsCOMPtr<nsITimer> mTimer;
128 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
129 nsIEventTarget* mEventTarget{nullptr};
130 Dashboard* mDashboard;
132 nsCString mHost;
133 uint32_t mPort{0};
134 nsCString mProtocol;
135 uint32_t mTimeout{0};
137 nsString mStatus;
140 NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback,
141 nsINamed)
143 class RcwnData : public nsISupports {
144 virtual ~RcwnData() = default;
146 public:
147 NS_DECL_THREADSAFE_ISUPPORTS
149 RcwnData() = default;
151 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
152 nsIEventTarget* mEventTarget{nullptr};
155 NS_IMPL_ISUPPORTS0(RcwnData)
157 NS_IMETHODIMP
158 ConnectionData::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
159 int64_t aProgress, int64_t aProgressMax) {
160 if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
161 StopTimer();
164 GetErrorString(aStatus, mStatus);
165 mEventTarget->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>(
166 "net::Dashboard::GetConnectionStatus", mDashboard,
167 &Dashboard::GetConnectionStatus, this),
168 NS_DISPATCH_NORMAL);
170 return NS_OK;
173 NS_IMETHODIMP
174 ConnectionData::Notify(nsITimer* aTimer) {
175 MOZ_ASSERT(aTimer == mTimer);
177 if (mSocket) {
178 mSocket->Close(NS_ERROR_ABORT);
179 mSocket = nullptr;
180 mStreamIn = nullptr;
183 mTimer = nullptr;
185 mStatus.AssignLiteral(u"NS_ERROR_NET_TIMEOUT");
186 mEventTarget->Dispatch(NewRunnableMethod<RefPtr<ConnectionData>>(
187 "net::Dashboard::GetConnectionStatus", mDashboard,
188 &Dashboard::GetConnectionStatus, this),
189 NS_DISPATCH_NORMAL);
191 return NS_OK;
194 void ConnectionData::StartTimer(uint32_t aTimeout) {
195 if (!mTimer) {
196 mTimer = NS_NewTimer();
199 mTimer->InitWithCallback(this, aTimeout * 1000, nsITimer::TYPE_ONE_SHOT);
202 void ConnectionData::StopTimer() {
203 if (mTimer) {
204 mTimer->Cancel();
205 mTimer = nullptr;
209 class LookupHelper;
211 class LookupArgument : public nsISupports {
212 virtual ~LookupArgument() = default;
214 public:
215 NS_DECL_THREADSAFE_ISUPPORTS
217 LookupArgument(nsIDNSRecord* aRecord, LookupHelper* aHelper) {
218 mRecord = aRecord;
219 mHelper = aHelper;
222 nsCOMPtr<nsIDNSRecord> mRecord;
223 RefPtr<LookupHelper> mHelper;
226 NS_IMPL_ISUPPORTS0(LookupArgument)
228 class LookupHelper final : public nsIDNSListener {
229 virtual ~LookupHelper() {
230 if (mCancel) {
231 mCancel->Cancel(NS_ERROR_ABORT);
235 public:
236 NS_DECL_THREADSAFE_ISUPPORTS
237 NS_DECL_NSIDNSLISTENER
239 LookupHelper() = default;
241 nsresult ConstructAnswer(LookupArgument* aArgument);
242 nsresult ConstructHTTPSRRAnswer(LookupArgument* aArgument);
244 public:
245 nsCOMPtr<nsICancelable> mCancel;
246 nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
247 nsIEventTarget* mEventTarget{nullptr};
248 nsresult mStatus{NS_ERROR_NOT_INITIALIZED};
251 NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSListener)
253 NS_IMETHODIMP
254 LookupHelper::OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRecord,
255 nsresult aStatus) {
256 MOZ_ASSERT(aRequest == mCancel);
257 mCancel = nullptr;
258 mStatus = aStatus;
260 nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord = do_QueryInterface(aRecord);
261 if (httpsRecord) {
262 RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
263 mEventTarget->Dispatch(
264 NewRunnableMethod<RefPtr<LookupArgument>>(
265 "net::LookupHelper::ConstructHTTPSRRAnswer", this,
266 &LookupHelper::ConstructHTTPSRRAnswer, arg),
267 NS_DISPATCH_NORMAL);
268 return NS_OK;
271 RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
272 mEventTarget->Dispatch(NewRunnableMethod<RefPtr<LookupArgument>>(
273 "net::LookupHelper::ConstructAnswer", this,
274 &LookupHelper::ConstructAnswer, arg),
275 NS_DISPATCH_NORMAL);
277 return NS_OK;
280 nsresult LookupHelper::ConstructAnswer(LookupArgument* aArgument) {
281 nsIDNSRecord* aRecord = aArgument->mRecord;
282 AutoSafeJSContext cx;
284 mozilla::dom::DNSLookupDict dict;
285 dict.mAddress.Construct();
287 Sequence<nsString>& addresses = dict.mAddress.Value();
288 nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(aRecord);
289 if (NS_SUCCEEDED(mStatus) && record) {
290 dict.mAnswer = true;
291 bool hasMore;
292 record->HasMore(&hasMore);
293 while (hasMore) {
294 nsString* nextAddress = addresses.AppendElement(fallible);
295 if (!nextAddress) {
296 return NS_ERROR_OUT_OF_MEMORY;
299 nsCString nextAddressASCII;
300 record->GetNextAddrAsString(nextAddressASCII);
301 CopyASCIItoUTF16(nextAddressASCII, *nextAddress);
302 record->HasMore(&hasMore);
304 } else {
305 dict.mAnswer = false;
306 GetErrorString(mStatus, dict.mError);
309 JS::Rooted<JS::Value> val(cx);
310 if (!ToJSValue(cx, dict, &val)) {
311 return NS_ERROR_FAILURE;
314 this->mCallback->OnDashboardDataAvailable(val);
316 return NS_OK;
319 static void CStringToHexString(const nsACString& aIn, nsAString& aOut) {
320 static const char* const lut = "0123456789ABCDEF";
322 size_t len = aIn.Length();
324 aOut.SetCapacity(2 * len);
325 for (size_t i = 0; i < aIn.Length(); ++i) {
326 const char c = static_cast<char>(aIn[i]);
327 aOut.Append(lut[(c >> 4) & 0x0F]);
328 aOut.Append(lut[c & 15]);
332 nsresult LookupHelper::ConstructHTTPSRRAnswer(LookupArgument* aArgument) {
333 nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord =
334 do_QueryInterface(aArgument->mRecord);
336 AutoSafeJSContext cx;
338 mozilla::dom::HTTPSRRLookupDict dict;
339 dict.mRecords.Construct();
341 Sequence<dom::HTTPSRecord>& records = dict.mRecords.Value();
342 if (NS_SUCCEEDED(mStatus) && httpsRecord) {
343 dict.mAnswer = true;
344 nsTArray<RefPtr<nsISVCBRecord>> svcbRecords;
345 httpsRecord->GetRecords(svcbRecords);
347 for (const auto& record : svcbRecords) {
348 dom::HTTPSRecord* nextRecord = records.AppendElement(fallible);
349 if (!nextRecord) {
350 return NS_ERROR_OUT_OF_MEMORY;
353 Unused << record->GetPriority(&nextRecord->mPriority);
354 nsCString name;
355 Unused << record->GetName(name);
356 CopyASCIItoUTF16(name, nextRecord->mTargetName);
358 nsTArray<RefPtr<nsISVCParam>> values;
359 Unused << record->GetValues(values);
360 if (values.IsEmpty()) {
361 continue;
364 for (const auto& value : values) {
365 uint16_t type;
366 Unused << value->GetType(&type);
367 switch (type) {
368 case SvcParamKeyAlpn: {
369 nextRecord->mAlpn.Construct();
370 nextRecord->mAlpn.Value().mType = type;
371 nsCOMPtr<nsISVCParamAlpn> alpnParam = do_QueryInterface(value);
372 nsTArray<nsCString> alpn;
373 Unused << alpnParam->GetAlpn(alpn);
374 nsAutoCString alpnStr;
375 for (const auto& str : alpn) {
376 alpnStr.Append(str);
377 alpnStr.Append(',');
379 CopyASCIItoUTF16(Span(alpnStr.BeginReading(), alpnStr.Length() - 1),
380 nextRecord->mAlpn.Value().mAlpn);
381 break;
383 case SvcParamKeyNoDefaultAlpn: {
384 nextRecord->mNoDefaultAlpn.Construct();
385 nextRecord->mNoDefaultAlpn.Value().mType = type;
386 break;
388 case SvcParamKeyPort: {
389 nextRecord->mPort.Construct();
390 nextRecord->mPort.Value().mType = type;
391 nsCOMPtr<nsISVCParamPort> portParam = do_QueryInterface(value);
392 Unused << portParam->GetPort(&nextRecord->mPort.Value().mPort);
393 break;
395 case SvcParamKeyIpv4Hint: {
396 nextRecord->mIpv4Hint.Construct();
397 nextRecord->mIpv4Hint.Value().mType = type;
398 nsCOMPtr<nsISVCParamIPv4Hint> ipv4Param = do_QueryInterface(value);
399 nsTArray<RefPtr<nsINetAddr>> ipv4Hint;
400 Unused << ipv4Param->GetIpv4Hint(ipv4Hint);
401 if (!ipv4Hint.IsEmpty()) {
402 nextRecord->mIpv4Hint.Value().mAddress.Construct();
403 for (const auto& address : ipv4Hint) {
404 nsString* nextAddress = nextRecord->mIpv4Hint.Value()
405 .mAddress.Value()
406 .AppendElement(fallible);
407 if (!nextAddress) {
408 return NS_ERROR_OUT_OF_MEMORY;
411 nsCString addressASCII;
412 Unused << address->GetAddress(addressASCII);
413 CopyASCIItoUTF16(addressASCII, *nextAddress);
416 break;
418 case SvcParamKeyIpv6Hint: {
419 nextRecord->mIpv6Hint.Construct();
420 nextRecord->mIpv6Hint.Value().mType = type;
421 nsCOMPtr<nsISVCParamIPv6Hint> ipv6Param = do_QueryInterface(value);
422 nsTArray<RefPtr<nsINetAddr>> ipv6Hint;
423 Unused << ipv6Param->GetIpv6Hint(ipv6Hint);
424 if (!ipv6Hint.IsEmpty()) {
425 nextRecord->mIpv6Hint.Value().mAddress.Construct();
426 for (const auto& address : ipv6Hint) {
427 nsString* nextAddress = nextRecord->mIpv6Hint.Value()
428 .mAddress.Value()
429 .AppendElement(fallible);
430 if (!nextAddress) {
431 return NS_ERROR_OUT_OF_MEMORY;
434 nsCString addressASCII;
435 Unused << address->GetAddress(addressASCII);
436 CopyASCIItoUTF16(addressASCII, *nextAddress);
439 break;
441 case SvcParamKeyEchConfig: {
442 nextRecord->mEchConfig.Construct();
443 nextRecord->mEchConfig.Value().mType = type;
444 nsCOMPtr<nsISVCParamEchConfig> echConfigParam =
445 do_QueryInterface(value);
446 nsCString echConfigStr;
447 Unused << echConfigParam->GetEchconfig(echConfigStr);
448 CStringToHexString(echConfigStr,
449 nextRecord->mEchConfig.Value().mEchConfig);
450 break;
452 case SvcParamKeyODoHConfig: {
453 nextRecord->mODoHConfig.Construct();
454 nextRecord->mODoHConfig.Value().mType = type;
455 nsCOMPtr<nsISVCParamODoHConfig> ODoHConfigParam =
456 do_QueryInterface(value);
457 nsCString ODoHConfigStr;
458 Unused << ODoHConfigParam->GetODoHConfig(ODoHConfigStr);
459 CStringToHexString(ODoHConfigStr,
460 nextRecord->mODoHConfig.Value().mODoHConfig);
461 break;
463 default:
464 break;
468 } else {
469 dict.mAnswer = false;
470 GetErrorString(mStatus, dict.mError);
473 JS::Rooted<JS::Value> val(cx);
474 if (!ToJSValue(cx, dict, &val)) {
475 return NS_ERROR_FAILURE;
478 this->mCallback->OnDashboardDataAvailable(val);
480 return NS_OK;
483 NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
485 Dashboard::Dashboard() { mEnableLogging = false; }
487 NS_IMETHODIMP
488 Dashboard::RequestSockets(nsINetDashboardCallback* aCallback) {
489 RefPtr<SocketData> socketData = new SocketData();
490 socketData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
491 "nsINetDashboardCallback", aCallback, true);
492 socketData->mEventTarget = GetCurrentSerialEventTarget();
494 if (nsIOService::UseSocketProcess()) {
495 if (!gIOService->SocketProcessReady()) {
496 return NS_ERROR_NOT_AVAILABLE;
499 RefPtr<Dashboard> self(this);
500 SocketProcessParent::GetSingleton()->SendGetSocketData()->Then(
501 GetMainThreadSerialEventTarget(), __func__,
502 [self{std::move(self)},
503 socketData{std::move(socketData)}](SocketDataArgs&& args) {
504 socketData->mData.Assign(args.info());
505 socketData->mTotalSent = args.totalSent();
506 socketData->mTotalRecv = args.totalRecv();
507 socketData->mEventTarget->Dispatch(
508 NewRunnableMethod<RefPtr<SocketData>>(
509 "net::Dashboard::GetSockets", self, &Dashboard::GetSockets,
510 socketData),
511 NS_DISPATCH_NORMAL);
513 [self](const mozilla::ipc::ResponseRejectReason) {});
514 return NS_OK;
517 gSocketTransportService->Dispatch(
518 NewRunnableMethod<RefPtr<SocketData>>(
519 "net::Dashboard::GetSocketsDispatch", this,
520 &Dashboard::GetSocketsDispatch, socketData),
521 NS_DISPATCH_NORMAL);
522 return NS_OK;
525 nsresult Dashboard::GetSocketsDispatch(SocketData* aSocketData) {
526 RefPtr<SocketData> socketData = aSocketData;
527 if (gSocketTransportService) {
528 gSocketTransportService->GetSocketConnections(&socketData->mData);
529 socketData->mTotalSent = gSocketTransportService->GetSentBytes();
530 socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes();
532 socketData->mEventTarget->Dispatch(
533 NewRunnableMethod<RefPtr<SocketData>>("net::Dashboard::GetSockets", this,
534 &Dashboard::GetSockets, socketData),
535 NS_DISPATCH_NORMAL);
536 return NS_OK;
539 nsresult Dashboard::GetSockets(SocketData* aSocketData) {
540 RefPtr<SocketData> socketData = aSocketData;
541 AutoSafeJSContext cx;
543 mozilla::dom::SocketsDict dict;
544 dict.mSockets.Construct();
545 dict.mSent = 0;
546 dict.mReceived = 0;
548 Sequence<mozilla::dom::SocketElement>& sockets = dict.mSockets.Value();
550 uint32_t length = socketData->mData.Length();
551 if (!sockets.SetCapacity(length, fallible)) {
552 JS_ReportOutOfMemory(cx);
553 return NS_ERROR_OUT_OF_MEMORY;
556 for (uint32_t i = 0; i < socketData->mData.Length(); i++) {
557 dom::SocketElement& mSocket = *sockets.AppendElement(fallible);
558 CopyASCIItoUTF16(socketData->mData[i].host, mSocket.mHost);
559 mSocket.mPort = socketData->mData[i].port;
560 mSocket.mActive = socketData->mData[i].active;
561 CopyASCIItoUTF16(socketData->mData[i].type, mSocket.mType);
562 mSocket.mSent = (double)socketData->mData[i].sent;
563 mSocket.mReceived = (double)socketData->mData[i].received;
564 dict.mSent += socketData->mData[i].sent;
565 dict.mReceived += socketData->mData[i].received;
568 dict.mSent += socketData->mTotalSent;
569 dict.mReceived += socketData->mTotalRecv;
570 JS::Rooted<JS::Value> val(cx);
571 if (!ToJSValue(cx, dict, &val)) return NS_ERROR_FAILURE;
572 socketData->mCallback->OnDashboardDataAvailable(val);
574 return NS_OK;
577 NS_IMETHODIMP
578 Dashboard::RequestHttpConnections(nsINetDashboardCallback* aCallback) {
579 RefPtr<HttpData> httpData = new HttpData();
580 httpData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
581 "nsINetDashboardCallback", aCallback, true);
582 httpData->mEventTarget = GetCurrentSerialEventTarget();
584 if (nsIOService::UseSocketProcess()) {
585 if (!gIOService->SocketProcessReady()) {
586 return NS_ERROR_NOT_AVAILABLE;
589 RefPtr<Dashboard> self(this);
590 SocketProcessParent::GetSingleton()->SendGetHttpConnectionData()->Then(
591 GetMainThreadSerialEventTarget(), __func__,
592 [self{std::move(self)}, httpData](nsTArray<HttpRetParams>&& params) {
593 httpData->mData.Assign(std::move(params));
594 self->GetHttpConnections(httpData);
595 httpData->mEventTarget->Dispatch(
596 NewRunnableMethod<RefPtr<HttpData>>(
597 "net::Dashboard::GetHttpConnections", self,
598 &Dashboard::GetHttpConnections, httpData),
599 NS_DISPATCH_NORMAL);
601 [self](const mozilla::ipc::ResponseRejectReason) {});
602 return NS_OK;
605 gSocketTransportService->Dispatch(NewRunnableMethod<RefPtr<HttpData>>(
606 "net::Dashboard::GetHttpDispatch", this,
607 &Dashboard::GetHttpDispatch, httpData),
608 NS_DISPATCH_NORMAL);
609 return NS_OK;
612 nsresult Dashboard::GetHttpDispatch(HttpData* aHttpData) {
613 RefPtr<HttpData> httpData = aHttpData;
614 HttpInfo::GetHttpConnectionData(&httpData->mData);
615 httpData->mEventTarget->Dispatch(
616 NewRunnableMethod<RefPtr<HttpData>>("net::Dashboard::GetHttpConnections",
617 this, &Dashboard::GetHttpConnections,
618 httpData),
619 NS_DISPATCH_NORMAL);
620 return NS_OK;
623 nsresult Dashboard::GetHttpConnections(HttpData* aHttpData) {
624 RefPtr<HttpData> httpData = aHttpData;
625 AutoSafeJSContext cx;
627 mozilla::dom::HttpConnDict dict;
628 dict.mConnections.Construct();
630 using mozilla::dom::DnsAndSockInfoDict;
631 using mozilla::dom::HttpConnectionElement;
632 using mozilla::dom::HttpConnInfo;
633 Sequence<HttpConnectionElement>& connections = dict.mConnections.Value();
635 uint32_t length = httpData->mData.Length();
636 if (!connections.SetCapacity(length, fallible)) {
637 JS_ReportOutOfMemory(cx);
638 return NS_ERROR_OUT_OF_MEMORY;
641 for (uint32_t i = 0; i < httpData->mData.Length(); i++) {
642 HttpConnectionElement& connection = *connections.AppendElement(fallible);
644 CopyASCIItoUTF16(httpData->mData[i].host, connection.mHost);
645 connection.mPort = httpData->mData[i].port;
646 CopyASCIItoUTF16(httpData->mData[i].httpVersion, connection.mHttpVersion);
647 connection.mSsl = httpData->mData[i].ssl;
649 connection.mActive.Construct();
650 connection.mIdle.Construct();
651 connection.mDnsAndSocks.Construct();
653 Sequence<HttpConnInfo>& active = connection.mActive.Value();
654 Sequence<HttpConnInfo>& idle = connection.mIdle.Value();
655 Sequence<DnsAndSockInfoDict>& dnsAndSocks = connection.mDnsAndSocks.Value();
657 if (!active.SetCapacity(httpData->mData[i].active.Length(), fallible) ||
658 !idle.SetCapacity(httpData->mData[i].idle.Length(), fallible) ||
659 !dnsAndSocks.SetCapacity(httpData->mData[i].dnsAndSocks.Length(),
660 fallible)) {
661 JS_ReportOutOfMemory(cx);
662 return NS_ERROR_OUT_OF_MEMORY;
665 for (uint32_t j = 0; j < httpData->mData[i].active.Length(); j++) {
666 HttpConnInfo& info = *active.AppendElement(fallible);
667 info.mRtt = httpData->mData[i].active[j].rtt;
668 info.mTtl = httpData->mData[i].active[j].ttl;
669 info.mProtocolVersion = httpData->mData[i].active[j].protocolVersion;
672 for (uint32_t j = 0; j < httpData->mData[i].idle.Length(); j++) {
673 HttpConnInfo& info = *idle.AppendElement(fallible);
674 info.mRtt = httpData->mData[i].idle[j].rtt;
675 info.mTtl = httpData->mData[i].idle[j].ttl;
676 info.mProtocolVersion = httpData->mData[i].idle[j].protocolVersion;
679 for (uint32_t j = 0; j < httpData->mData[i].dnsAndSocks.Length(); j++) {
680 DnsAndSockInfoDict& info = *dnsAndSocks.AppendElement(fallible);
681 info.mSpeculative = httpData->mData[i].dnsAndSocks[j].speculative;
685 JS::Rooted<JS::Value> val(cx);
686 if (!ToJSValue(cx, dict, &val)) {
687 return NS_ERROR_FAILURE;
690 httpData->mCallback->OnDashboardDataAvailable(val);
692 return NS_OK;
695 NS_IMETHODIMP
696 Dashboard::GetEnableLogging(bool* value) {
697 *value = mEnableLogging;
698 return NS_OK;
701 NS_IMETHODIMP
702 Dashboard::SetEnableLogging(const bool value) {
703 mEnableLogging = value;
704 return NS_OK;
707 NS_IMETHODIMP
708 Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted) {
709 if (mEnableLogging) {
710 mozilla::MutexAutoLock lock(mWs.lock);
711 LogData mData(nsCString(aHost), aSerial, aEncrypted);
712 if (mWs.data.Contains(mData)) {
713 return NS_OK;
715 // XXX(Bug 1631371) Check if this should use a fallible operation as it
716 // pretended earlier.
717 mWs.data.AppendElement(mData);
718 return NS_OK;
720 return NS_ERROR_FAILURE;
723 NS_IMETHODIMP
724 Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial) {
725 if (mEnableLogging) {
726 mozilla::MutexAutoLock lock(mWs.lock);
727 int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
728 if (index == -1) return NS_ERROR_FAILURE;
729 mWs.data.RemoveElementAt(index);
730 return NS_OK;
732 return NS_ERROR_FAILURE;
735 NS_IMETHODIMP
736 Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial,
737 uint32_t aLength) {
738 if (mEnableLogging) {
739 mozilla::MutexAutoLock lock(mWs.lock);
740 int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
741 if (index == -1) return NS_ERROR_FAILURE;
742 mWs.data[index].mMsgSent++;
743 mWs.data[index].mSizeSent += aLength;
744 return NS_OK;
746 return NS_ERROR_FAILURE;
749 NS_IMETHODIMP
750 Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial,
751 uint32_t aLength) {
752 if (mEnableLogging) {
753 mozilla::MutexAutoLock lock(mWs.lock);
754 int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
755 if (index == -1) return NS_ERROR_FAILURE;
756 mWs.data[index].mMsgReceived++;
757 mWs.data[index].mSizeReceived += aLength;
758 return NS_OK;
760 return NS_ERROR_FAILURE;
763 NS_IMETHODIMP
764 Dashboard::RequestWebsocketConnections(nsINetDashboardCallback* aCallback) {
765 RefPtr<WebSocketRequest> wsRequest = new WebSocketRequest();
766 wsRequest->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
767 "nsINetDashboardCallback", aCallback, true);
768 wsRequest->mEventTarget = GetCurrentSerialEventTarget();
770 wsRequest->mEventTarget->Dispatch(
771 NewRunnableMethod<RefPtr<WebSocketRequest>>(
772 "net::Dashboard::GetWebSocketConnections", this,
773 &Dashboard::GetWebSocketConnections, wsRequest),
774 NS_DISPATCH_NORMAL);
775 return NS_OK;
778 nsresult Dashboard::GetWebSocketConnections(WebSocketRequest* aWsRequest) {
779 RefPtr<WebSocketRequest> wsRequest = aWsRequest;
780 AutoSafeJSContext cx;
782 mozilla::dom::WebSocketDict dict;
783 dict.mWebsockets.Construct();
784 Sequence<mozilla::dom::WebSocketElement>& websockets =
785 dict.mWebsockets.Value();
787 mozilla::MutexAutoLock lock(mWs.lock);
788 uint32_t length = mWs.data.Length();
789 if (!websockets.SetCapacity(length, fallible)) {
790 JS_ReportOutOfMemory(cx);
791 return NS_ERROR_OUT_OF_MEMORY;
794 for (uint32_t i = 0; i < mWs.data.Length(); i++) {
795 dom::WebSocketElement& websocket = *websockets.AppendElement(fallible);
796 CopyASCIItoUTF16(mWs.data[i].mHost, websocket.mHostport);
797 websocket.mMsgsent = mWs.data[i].mMsgSent;
798 websocket.mMsgreceived = mWs.data[i].mMsgReceived;
799 websocket.mSentsize = mWs.data[i].mSizeSent;
800 websocket.mReceivedsize = mWs.data[i].mSizeReceived;
801 websocket.mEncrypted = mWs.data[i].mEncrypted;
804 JS::Rooted<JS::Value> val(cx);
805 if (!ToJSValue(cx, dict, &val)) {
806 return NS_ERROR_FAILURE;
808 wsRequest->mCallback->OnDashboardDataAvailable(val);
810 return NS_OK;
813 NS_IMETHODIMP
814 Dashboard::RequestDNSInfo(nsINetDashboardCallback* aCallback) {
815 RefPtr<DnsData> dnsData = new DnsData();
816 dnsData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
817 "nsINetDashboardCallback", aCallback, true);
819 nsresult rv;
820 dnsData->mData.Clear();
821 dnsData->mEventTarget = GetCurrentSerialEventTarget();
823 if (!mDnsService) {
824 mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
825 if (NS_FAILED(rv)) {
826 return rv;
830 if (nsIOService::UseSocketProcess()) {
831 if (!gIOService->SocketProcessReady()) {
832 return NS_ERROR_NOT_AVAILABLE;
835 RefPtr<Dashboard> self(this);
836 SocketProcessParent::GetSingleton()->SendGetDNSCacheEntries()->Then(
837 GetMainThreadSerialEventTarget(), __func__,
838 [self{std::move(self)},
839 dnsData{std::move(dnsData)}](nsTArray<DNSCacheEntries>&& entries) {
840 dnsData->mData.Assign(std::move(entries));
841 dnsData->mEventTarget->Dispatch(
842 NewRunnableMethod<RefPtr<DnsData>>(
843 "net::Dashboard::GetDNSCacheEntries", self,
844 &Dashboard::GetDNSCacheEntries, dnsData),
845 NS_DISPATCH_NORMAL);
847 [self](const mozilla::ipc::ResponseRejectReason) {});
848 return NS_OK;
851 gSocketTransportService->Dispatch(
852 NewRunnableMethod<RefPtr<DnsData>>("net::Dashboard::GetDnsInfoDispatch",
853 this, &Dashboard::GetDnsInfoDispatch,
854 dnsData),
855 NS_DISPATCH_NORMAL);
856 return NS_OK;
859 nsresult Dashboard::GetDnsInfoDispatch(DnsData* aDnsData) {
860 RefPtr<DnsData> dnsData = aDnsData;
861 if (mDnsService) {
862 mDnsService->GetDNSCacheEntries(&dnsData->mData);
864 dnsData->mEventTarget->Dispatch(
865 NewRunnableMethod<RefPtr<DnsData>>("net::Dashboard::GetDNSCacheEntries",
866 this, &Dashboard::GetDNSCacheEntries,
867 dnsData),
868 NS_DISPATCH_NORMAL);
869 return NS_OK;
872 nsresult Dashboard::GetDNSCacheEntries(DnsData* dnsData) {
873 AutoSafeJSContext cx;
875 mozilla::dom::DNSCacheDict dict;
876 dict.mEntries.Construct();
877 Sequence<mozilla::dom::DnsCacheEntry>& entries = dict.mEntries.Value();
879 uint32_t length = dnsData->mData.Length();
880 if (!entries.SetCapacity(length, fallible)) {
881 JS_ReportOutOfMemory(cx);
882 return NS_ERROR_OUT_OF_MEMORY;
885 for (uint32_t i = 0; i < dnsData->mData.Length(); i++) {
886 dom::DnsCacheEntry& entry = *entries.AppendElement(fallible);
887 entry.mHostaddr.Construct();
889 Sequence<nsString>& addrs = entry.mHostaddr.Value();
890 if (!addrs.SetCapacity(dnsData->mData[i].hostaddr.Length(), fallible)) {
891 JS_ReportOutOfMemory(cx);
892 return NS_ERROR_OUT_OF_MEMORY;
895 CopyASCIItoUTF16(dnsData->mData[i].hostname, entry.mHostname);
896 entry.mExpiration = dnsData->mData[i].expiration;
897 entry.mTrr = dnsData->mData[i].TRR;
899 for (uint32_t j = 0; j < dnsData->mData[i].hostaddr.Length(); j++) {
900 nsString* addr = addrs.AppendElement(fallible);
901 if (!addr) {
902 JS_ReportOutOfMemory(cx);
903 return NS_ERROR_OUT_OF_MEMORY;
905 CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], *addr);
908 if (dnsData->mData[i].family == PR_AF_INET6) {
909 entry.mFamily.AssignLiteral(u"ipv6");
910 } else {
911 entry.mFamily.AssignLiteral(u"ipv4");
914 entry.mOriginAttributesSuffix =
915 NS_ConvertUTF8toUTF16(dnsData->mData[i].originAttributesSuffix);
916 entry.mFlags = NS_ConvertUTF8toUTF16(dnsData->mData[i].flags);
919 JS::Rooted<JS::Value> val(cx);
920 if (!ToJSValue(cx, dict, &val)) {
921 return NS_ERROR_FAILURE;
923 dnsData->mCallback->OnDashboardDataAvailable(val);
925 return NS_OK;
928 NS_IMETHODIMP
929 Dashboard::RequestDNSLookup(const nsACString& aHost,
930 nsINetDashboardCallback* aCallback) {
931 nsresult rv;
933 if (!mDnsService) {
934 mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
935 if (NS_FAILED(rv)) {
936 return rv;
940 RefPtr<LookupHelper> helper = new LookupHelper();
941 helper->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
942 "nsINetDashboardCallback", aCallback, true);
943 helper->mEventTarget = GetCurrentSerialEventTarget();
944 OriginAttributes attrs;
945 rv = mDnsService->AsyncResolveNative(
946 aHost, nsIDNSService::RESOLVE_TYPE_DEFAULT,
947 nsIDNSService::RESOLVE_DEFAULT_FLAGS, nullptr, helper.get(),
948 NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
949 return rv;
952 NS_IMETHODIMP
953 Dashboard::RequestDNSHTTPSRRLookup(const nsACString& aHost,
954 nsINetDashboardCallback* aCallback) {
955 nsresult rv;
957 if (!mDnsService) {
958 mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
959 if (NS_FAILED(rv)) {
960 return rv;
964 RefPtr<LookupHelper> helper = new LookupHelper();
965 helper->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
966 "nsINetDashboardCallback", aCallback, true);
967 helper->mEventTarget = GetCurrentSerialEventTarget();
968 OriginAttributes attrs;
969 rv = mDnsService->AsyncResolveNative(
970 aHost, nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
971 nsIDNSService::RESOLVE_DEFAULT_FLAGS, nullptr, helper.get(),
972 NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
973 return rv;
976 NS_IMETHODIMP
977 Dashboard::RequestRcwnStats(nsINetDashboardCallback* aCallback) {
978 RefPtr<RcwnData> rcwnData = new RcwnData();
979 rcwnData->mEventTarget = GetCurrentSerialEventTarget();
980 rcwnData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
981 "nsINetDashboardCallback", aCallback, true);
983 return rcwnData->mEventTarget->Dispatch(
984 NewRunnableMethod<RefPtr<RcwnData>>("net::Dashboard::GetRcwnData", this,
985 &Dashboard::GetRcwnData, rcwnData),
986 NS_DISPATCH_NORMAL);
989 nsresult Dashboard::GetRcwnData(RcwnData* aData) {
990 AutoSafeJSContext cx;
991 mozilla::dom::RcwnStatus dict;
993 dict.mTotalNetworkRequests = gIOService->GetTotalRequestNumber();
994 dict.mRcwnCacheWonCount = gIOService->GetCacheWonRequestNumber();
995 dict.mRcwnNetWonCount = gIOService->GetNetWonRequestNumber();
997 uint32_t cacheSlow, cacheNotSlow;
998 CacheFileUtils::CachePerfStats::GetSlowStats(&cacheSlow, &cacheNotSlow);
999 dict.mCacheSlowCount = cacheSlow;
1000 dict.mCacheNotSlowCount = cacheNotSlow;
1002 dict.mPerfStats.Construct();
1003 Sequence<mozilla::dom::RcwnPerfStats>& perfStats = dict.mPerfStats.Value();
1004 uint32_t length = CacheFileUtils::CachePerfStats::LAST;
1005 if (!perfStats.SetCapacity(length, fallible)) {
1006 JS_ReportOutOfMemory(cx);
1007 return NS_ERROR_OUT_OF_MEMORY;
1010 for (uint32_t i = 0; i < length; i++) {
1011 CacheFileUtils::CachePerfStats::EDataType perfType =
1012 static_cast<CacheFileUtils::CachePerfStats::EDataType>(i);
1013 dom::RcwnPerfStats& elem = *perfStats.AppendElement(fallible);
1014 elem.mAvgShort =
1015 CacheFileUtils::CachePerfStats::GetAverage(perfType, false);
1016 elem.mAvgLong = CacheFileUtils::CachePerfStats::GetAverage(perfType, true);
1017 elem.mStddevLong =
1018 CacheFileUtils::CachePerfStats::GetStdDev(perfType, true);
1021 JS::Rooted<JS::Value> val(cx);
1022 if (!ToJSValue(cx, dict, &val)) {
1023 return NS_ERROR_FAILURE;
1026 aData->mCallback->OnDashboardDataAvailable(val);
1028 return NS_OK;
1031 void HttpConnInfo::SetHTTPProtocolVersion(HttpVersion pv) {
1032 switch (pv) {
1033 case HttpVersion::v0_9:
1034 protocolVersion.AssignLiteral(u"http/0.9");
1035 break;
1036 case HttpVersion::v1_0:
1037 protocolVersion.AssignLiteral(u"http/1.0");
1038 break;
1039 case HttpVersion::v1_1:
1040 protocolVersion.AssignLiteral(u"http/1.1");
1041 break;
1042 case HttpVersion::v2_0:
1043 protocolVersion.AssignLiteral(u"http/2");
1044 break;
1045 case HttpVersion::v3_0:
1046 protocolVersion.AssignLiteral(u"http/3");
1047 break;
1048 default:
1049 protocolVersion.AssignLiteral(u"unknown protocol version");
1053 NS_IMETHODIMP
1054 Dashboard::GetLogPath(nsACString& aLogPath) {
1055 aLogPath.SetLength(2048);
1056 uint32_t len = LogModule::GetLogFile(aLogPath.BeginWriting(), 2048);
1057 aLogPath.SetLength(len);
1058 return NS_OK;
1061 NS_IMETHODIMP
1062 Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort,
1063 const char* aProtocol, uint32_t aTimeout,
1064 nsINetDashboardCallback* aCallback) {
1065 nsresult rv;
1066 RefPtr<ConnectionData> connectionData = new ConnectionData(this);
1067 connectionData->mHost = aHost;
1068 connectionData->mPort = aPort;
1069 connectionData->mProtocol = aProtocol;
1070 connectionData->mTimeout = aTimeout;
1072 connectionData->mCallback =
1073 new nsMainThreadPtrHolder<nsINetDashboardCallback>(
1074 "nsINetDashboardCallback", aCallback, true);
1075 connectionData->mEventTarget = GetCurrentSerialEventTarget();
1077 rv = TestNewConnection(connectionData);
1078 if (NS_FAILED(rv)) {
1079 mozilla::net::GetErrorString(rv, connectionData->mStatus);
1080 connectionData->mEventTarget->Dispatch(
1081 NewRunnableMethod<RefPtr<ConnectionData>>(
1082 "net::Dashboard::GetConnectionStatus", this,
1083 &Dashboard::GetConnectionStatus, connectionData),
1084 NS_DISPATCH_NORMAL);
1085 return rv;
1088 return NS_OK;
1091 nsresult Dashboard::GetConnectionStatus(ConnectionData* aConnectionData) {
1092 RefPtr<ConnectionData> connectionData = aConnectionData;
1093 AutoSafeJSContext cx;
1095 mozilla::dom::ConnStatusDict dict;
1096 dict.mStatus = connectionData->mStatus;
1098 JS::Rooted<JS::Value> val(cx);
1099 if (!ToJSValue(cx, dict, &val)) return NS_ERROR_FAILURE;
1101 connectionData->mCallback->OnDashboardDataAvailable(val);
1103 return NS_OK;
1106 nsresult Dashboard::TestNewConnection(ConnectionData* aConnectionData) {
1107 RefPtr<ConnectionData> connectionData = aConnectionData;
1109 nsresult rv;
1110 if (!connectionData->mHost.Length() ||
1111 !net_IsValidHostName(connectionData->mHost)) {
1112 return NS_ERROR_UNKNOWN_HOST;
1115 if (connectionData->mProtocol.EqualsLiteral("ssl")) {
1116 AutoTArray<nsCString, 1> socketTypes = {connectionData->mProtocol};
1117 rv = gSocketTransportService->CreateTransport(
1118 socketTypes, connectionData->mHost, connectionData->mPort, nullptr,
1119 nullptr, getter_AddRefs(connectionData->mSocket));
1120 } else {
1121 rv = gSocketTransportService->CreateTransport(
1122 nsTArray<nsCString>(), connectionData->mHost, connectionData->mPort,
1123 nullptr, nullptr, getter_AddRefs(connectionData->mSocket));
1125 if (NS_FAILED(rv)) {
1126 return rv;
1129 rv = connectionData->mSocket->SetEventSink(connectionData,
1130 GetCurrentSerialEventTarget());
1131 if (NS_FAILED(rv)) {
1132 return rv;
1135 rv = connectionData->mSocket->OpenInputStream(
1136 nsITransport::OPEN_BLOCKING, 0, 0,
1137 getter_AddRefs(connectionData->mStreamIn));
1138 if (NS_FAILED(rv)) {
1139 return rv;
1142 connectionData->StartTimer(connectionData->mTimeout);
1144 return rv;
1147 using ErrorEntry = struct {
1148 nsresult key;
1149 const char* error;
1152 #undef ERROR
1153 #define ERROR(key, val) \
1154 { key, #key }
1156 ErrorEntry socketTransportStatuses[] = {
1157 ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
1158 ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
1159 ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
1160 ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
1161 ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)),
1162 ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)),
1163 ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
1164 ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
1165 ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
1167 #undef ERROR
1169 static void GetErrorString(nsresult rv, nsAString& errorString) {
1170 for (auto& socketTransportStatus : socketTransportStatuses) {
1171 if (socketTransportStatus.key == rv) {
1172 errorString.AssignASCII(socketTransportStatus.error);
1173 return;
1176 nsAutoCString errorCString;
1177 mozilla::GetErrorName(rv, errorCString);
1178 CopyUTF8toUTF16(errorCString, errorString);
1181 } // namespace net
1182 } // namespace mozilla