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