media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "WebrtcGlobalInformation.h"
     7 #include <deque>
     8 #include <string>
    10 #include "CSFLog.h"
    12 #include "mozilla/dom/WebrtcGlobalInformationBinding.h"
    14 #include "nsAutoPtr.h"
    15 #include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
    16 #include "nsServiceManagerUtils.h" // do_GetService
    17 #include "mozilla/ErrorResult.h"
    18 #include "mozilla/Vector.h"
    19 #include "nsProxyRelease.h"
    20 #include "mozilla/Telemetry.h"
    22 #include "rlogringbuffer.h"
    23 #include "runnable_utils.h"
    24 #include "PeerConnectionCtx.h"
    25 #include "PeerConnectionImpl.h"
    27 using sipcc::PeerConnectionImpl;
    28 using sipcc::PeerConnectionCtx;
    29 using sipcc::RTCStatsQuery;
    31 static const char* logTag = "WebrtcGlobalInformation";
    33 namespace mozilla {
    34 namespace dom {
    36 typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
    38 static void OnStatsReport_m(
    39   nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
    40   nsAutoPtr<RTCStatsQueries> aQueryList)
    41 {
    42   MOZ_ASSERT(NS_IsMainThread());
    43   MOZ_ASSERT(aQueryList);
    45   WebrtcGlobalStatisticsReport report;
    46   report.mReports.Construct();
    47   for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
    48     MOZ_ASSERT(*q);
    49     report.mReports.Value().AppendElement((*q)->report);
    50   }
    52   ErrorResult rv;
    53   aStatsCallback.get()->Call(report, rv);
    55   if (rv.Failed()) {
    56     CSFLogError(logTag, "Error firing stats observer callback");
    57   }
    58 }
    60 static void GetAllStats_s(
    61   nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
    62   nsAutoPtr<RTCStatsQueries> aQueryList)
    63 {
    64   MOZ_ASSERT(aQueryList);
    66   for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
    67     MOZ_ASSERT(*q);
    68     PeerConnectionImpl::ExecuteStatsQuery_s(*q);
    69   }
    71   NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m,
    72                                          aStatsCallback,
    73                                          aQueryList),
    74                           NS_DISPATCH_NORMAL);
    75 }
    77 static void OnGetLogging_m(
    78   nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
    79   const std::string& aPattern,
    80   nsAutoPtr<std::deque<std::string>> aLogList)
    81 {
    82   ErrorResult rv;
    83   if (!aLogList->empty()) {
    84     Sequence<nsString> nsLogs;
    85     for (auto l = aLogList->begin(); l != aLogList->end(); ++l) {
    86       nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str()));
    87     }
    88     aLoggingCallback.get()->Call(nsLogs, rv);
    89   }
    91   if (rv.Failed()) {
    92     CSFLogError(logTag, "Error firing logging observer callback");
    93   }
    94 }
    96 static void GetLogging_s(
    97   nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
    98   const std::string& aPattern)
    99 {
   100   RLogRingBuffer* logs = RLogRingBuffer::GetInstance();
   101   nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>);
   102   // Might not exist yet.
   103   if (logs) {
   104     logs->Filter(aPattern, 0, result);
   105   }
   106   NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m,
   107                                          aLoggingCallback,
   108                                          aPattern,
   109                                          result),
   110                           NS_DISPATCH_NORMAL);
   111 }
   114 void
   115 WebrtcGlobalInformation::GetAllStats(
   116   const GlobalObject& aGlobal,
   117   WebrtcGlobalStatisticsCallback& aStatsCallback,
   118   ErrorResult& aRv)
   119 {
   120   if (!NS_IsMainThread()) {
   121     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
   122     return;
   123   }
   125   nsresult rv;
   126   nsCOMPtr<nsIEventTarget> stsThread =
   127     do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   129   if (NS_FAILED(rv)) {
   130     aRv.Throw(rv);
   131     return;
   132   }
   134   if (!stsThread) {
   135     aRv.Throw(NS_ERROR_UNEXPECTED);
   136     return;
   137   }
   139   nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
   141   // If there is no PeerConnectionCtx, go through the same motions, since
   142   // the API consumer doesn't care why there are no PeerConnectionImpl.
   143   if (PeerConnectionCtx::isActive()) {
   144     PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
   145     MOZ_ASSERT(ctx);
   146     for (auto p = ctx->mPeerConnections.begin();
   147          p != ctx->mPeerConnections.end();
   148          ++p) {
   149       MOZ_ASSERT(p->second);
   151       if (p->second->HasMedia()) {
   152         queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
   153         p->second->BuildStatsQuery_m(nullptr, // all tracks
   154                                      queries->back());
   155       }
   156     }
   157   }
   159   // CallbackObject does not support threadsafe refcounting, and must be
   160   // destroyed on main.
   161   nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle(
   162     new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback));
   164   rv = RUN_ON_THREAD(stsThread,
   165                      WrapRunnableNM(&GetAllStats_s, callbackHandle, queries),
   166                      NS_DISPATCH_NORMAL);
   168   aRv = rv;
   169 }
   171 void
   172 WebrtcGlobalInformation::GetLogging(
   173   const GlobalObject& aGlobal,
   174   const nsAString& aPattern,
   175   WebrtcGlobalLoggingCallback& aLoggingCallback,
   176   ErrorResult& aRv)
   177 {
   178   if (!NS_IsMainThread()) {
   179     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
   180     return;
   181   }
   183   nsresult rv;
   184   nsCOMPtr<nsIEventTarget> stsThread =
   185     do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   187   if (NS_FAILED(rv)) {
   188     aRv.Throw(rv);
   189     return;
   190   }
   192   if (!stsThread) {
   193     aRv.Throw(NS_ERROR_UNEXPECTED);
   194     return;
   195   }
   197   std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get());
   199   // CallbackObject does not support threadsafe refcounting, and must be
   200   // destroyed on main.
   201   nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> callbackHandle(
   202     new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(&aLoggingCallback));
   204   rv = RUN_ON_THREAD(stsThread,
   205                      WrapRunnableNM(&GetLogging_s, callbackHandle, pattern),
   206                      NS_DISPATCH_NORMAL);
   208   if (NS_FAILED(rv)) {
   209     aLoggingCallback.Release();
   210   }
   212   aRv = rv;
   213 }
   215 struct StreamResult {
   216   StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {}
   217   uint8_t candidateTypeBitpattern;
   218   bool streamSucceeded;
   219 };
   221 static void StoreLongTermICEStatisticsImpl_m(
   222     nsresult result,
   223     nsAutoPtr<RTCStatsQuery> query) {
   225   if (NS_FAILED(result) ||
   226       !query->error.empty() ||
   227       !query->report.mIceCandidateStats.WasPassed()) {
   228     return;
   229   }
   231   // First, store stuff in telemetry
   232   enum {
   233     REMOTE_GATHERED_SERVER_REFLEXIVE = 1,
   234     REMOTE_GATHERED_TURN = 1 << 1,
   235     LOCAL_GATHERED_SERVER_REFLEXIVE = 1 << 2,
   236     LOCAL_GATHERED_TURN_UDP = 1 << 3,
   237     LOCAL_GATHERED_TURN_TCP = 1 << 4,
   238     LOCAL_GATHERED_TURN_TLS = 1 << 5,
   239     LOCAL_GATHERED_TURN_HTTPS = 1 << 6,
   240   };
   242   // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the
   243   // components within a stream didn't have the same types of relayed
   244   // candidates? I have a feeling that late trickle could cause this, but right
   245   // now we don't have enough information to detect it (we would need to know
   246   // the ICE component id for each candidate pair and candidate)
   248   std::map<std::string, StreamResult> streamResults;
   250   // Build list of streams, and whether or not they failed.
   251   for (size_t i = 0;
   252        i < query->report.mIceCandidatePairStats.Value().Length();
   253        ++i) {
   254     const RTCIceCandidatePairStats &pair =
   255       query->report.mIceCandidatePairStats.Value()[i];
   257     if (!pair.mState.WasPassed() || !pair.mComponentId.WasPassed()) {
   258       MOZ_CRASH();
   259       continue;
   260     }
   262     // Note: this is not a "component" in the ICE definition, this is really a
   263     // stream ID. This is just the way the stats API is standardized right now.
   264     // Very confusing.
   265     std::string streamId(
   266       NS_ConvertUTF16toUTF8(pair.mComponentId.Value()).get());
   268     streamResults[streamId].streamSucceeded |=
   269       pair.mState.Value() == RTCStatsIceCandidatePairState::Succeeded;
   270   }
   272   for (size_t i = 0;
   273        i < query->report.mIceCandidateStats.Value().Length();
   274        ++i) {
   275     const RTCIceCandidateStats &cand =
   276       query->report.mIceCandidateStats.Value()[i];
   278     if (!cand.mType.WasPassed() ||
   279         !cand.mCandidateType.WasPassed() ||
   280         !cand.mComponentId.WasPassed()) {
   281       // Crash on debug, ignore this candidate otherwise.
   282       MOZ_CRASH();
   283       continue;
   284     }
   286     // Note: this is not a "component" in the ICE definition, this is really a
   287     // stream ID. This is just the way the stats API is standardized right now
   288     // Very confusing.
   289     std::string streamId(
   290       NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get());
   292     if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) {
   293       if (cand.mType.Value() == RTCStatsType::Localcandidate) {
   294         NS_ConvertUTF16toUTF8 transport(cand.mMozLocalTransport.Value());
   295         if (transport == kNrIceTransportUdp) {
   296           streamResults[streamId].candidateTypeBitpattern |=
   297             LOCAL_GATHERED_TURN_UDP;
   298         } else if (transport == kNrIceTransportTcp) {
   299           streamResults[streamId].candidateTypeBitpattern |=
   300             LOCAL_GATHERED_TURN_TCP;
   301         }
   302       } else {
   303         streamResults[streamId].candidateTypeBitpattern |= REMOTE_GATHERED_TURN;
   304       }
   305     } else if (cand.mCandidateType.Value() ==
   306                RTCStatsIceCandidateType::Serverreflexive) {
   307       if (cand.mType.Value() == RTCStatsType::Localcandidate) {
   308         streamResults[streamId].candidateTypeBitpattern |=
   309           LOCAL_GATHERED_SERVER_REFLEXIVE;
   310       } else {
   311         streamResults[streamId].candidateTypeBitpattern |=
   312           REMOTE_GATHERED_SERVER_REFLEXIVE;
   313       }
   314     }
   315   }
   317   for (auto i = streamResults.begin(); i != streamResults.end(); ++i) {
   318     if (i->second.streamSucceeded) {
   319       Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS,
   320                             i->second.candidateTypeBitpattern);
   321     } else {
   322       Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE,
   323                             i->second.candidateTypeBitpattern);
   324     }
   325   }
   326 }
   328 static void GetStatsForLongTermStorage_s(
   329     nsAutoPtr<RTCStatsQuery> query) {
   331   MOZ_ASSERT(query);
   333   nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
   335   // Even if Telemetry::Accumulate is threadsafe, we still need to send the
   336   // query back to main, since that is where it must be destroyed.
   337   NS_DispatchToMainThread(
   338       WrapRunnableNM(
   339           &StoreLongTermICEStatisticsImpl_m,
   340           rv,
   341           query),
   342       NS_DISPATCH_NORMAL);
   343 }
   345 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
   346     sipcc::PeerConnectionImpl& aPc) {
   347   Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE,
   348                         static_cast<uint32_t>(aPc.IceConnectionState()));
   350   if (aPc.IceConnectionState() == PCImplIceConnectionState::New) {
   351     // ICE has not started; we won't have any remote candidates, so recording
   352     // statistics on gathered candidates is pointless.
   353     return;
   354   }
   356   nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(true));
   358   nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get());
   360   NS_ENSURE_SUCCESS_VOID(rv);
   362   RUN_ON_THREAD(aPc.GetSTSThread(),
   363                 WrapRunnableNM(&GetStatsForLongTermStorage_s,
   364                                query),
   365                 NS_DISPATCH_NORMAL);
   366 }
   369 } // namespace dom
   370 } // namespace mozilla

mercurial