1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 // HttpLog.h should generally be included first
9 #include "HttpLog.h"
10 
11 #include "HttpBackgroundChannelParent.h"
12 
13 #include "HttpChannelParent.h"
14 #include "mozilla/ipc/BackgroundParent.h"
15 #include "mozilla/IntegerPrintfMacros.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/net/BackgroundChannelRegistrar.h"
18 #include "nsNetCID.h"
19 #include "nsQueryObject.h"
20 #include "nsThreadUtils.h"
21 
22 using mozilla::dom::ContentParent;
23 using mozilla::ipc::AssertIsInMainProcess;
24 using mozilla::ipc::AssertIsOnBackgroundThread;
25 using mozilla::ipc::BackgroundParent;
26 using mozilla::ipc::IPCResult;
27 using mozilla::ipc::IsOnBackgroundThread;
28 
29 namespace mozilla {
30 namespace net {
31 
32 /*
33  * Helper class for continuing the AsyncOpen procedure on main thread.
34  */
35 class ContinueAsyncOpenRunnable final : public Runnable {
36  public:
ContinueAsyncOpenRunnable(HttpBackgroundChannelParent * aActor,const uint64_t & aChannelId)37   ContinueAsyncOpenRunnable(HttpBackgroundChannelParent* aActor,
38                             const uint64_t& aChannelId)
39       : Runnable("net::ContinueAsyncOpenRunnable"),
40         mActor(aActor),
41         mChannelId(aChannelId) {
42     AssertIsInMainProcess();
43     AssertIsOnBackgroundThread();
44     MOZ_ASSERT(mActor);
45   }
46 
Run()47   NS_IMETHOD Run() override {
48     LOG(
49         ("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p "
50          "channelId=%" PRIu64 "]\n",
51          mActor.get(), mChannelId));
52     AssertIsInMainProcess();
53     MOZ_ASSERT(NS_IsMainThread());
54 
55     nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
56         BackgroundChannelRegistrar::GetOrCreate();
57     MOZ_ASSERT(registrar);
58 
59     registrar->LinkBackgroundChannel(mChannelId, mActor);
60     return NS_OK;
61   }
62 
63  private:
64   RefPtr<HttpBackgroundChannelParent> mActor;
65   const uint64_t mChannelId;
66 };
67 
HttpBackgroundChannelParent()68 HttpBackgroundChannelParent::HttpBackgroundChannelParent()
69     : mIPCOpened(true),
70       mBgThreadMutex("HttpBackgroundChannelParent::BgThreadMutex") {
71   AssertIsInMainProcess();
72   AssertIsOnBackgroundThread();
73 
74   {
75     MutexAutoLock lock(mBgThreadMutex);
76     mBackgroundThread = NS_GetCurrentThread();
77   }
78 }
79 
~HttpBackgroundChannelParent()80 HttpBackgroundChannelParent::~HttpBackgroundChannelParent() {
81   MOZ_ASSERT(NS_IsMainThread() || IsOnBackgroundThread());
82   MOZ_ASSERT(!mIPCOpened);
83 }
84 
Init(const uint64_t & aChannelId)85 nsresult HttpBackgroundChannelParent::Init(const uint64_t& aChannelId) {
86   LOG(("HttpBackgroundChannelParent::Init [this=%p channelId=%" PRIu64 "]\n",
87        this, aChannelId));
88   AssertIsInMainProcess();
89   AssertIsOnBackgroundThread();
90 
91   RefPtr<ContinueAsyncOpenRunnable> runnable =
92       new ContinueAsyncOpenRunnable(this, aChannelId);
93 
94   return NS_DispatchToMainThread(runnable);
95 }
96 
LinkToChannel(HttpChannelParent * aChannelParent)97 void HttpBackgroundChannelParent::LinkToChannel(
98     HttpChannelParent* aChannelParent) {
99   LOG(("HttpBackgroundChannelParent::LinkToChannel [this=%p channel=%p]\n",
100        this, aChannelParent));
101   AssertIsInMainProcess();
102   MOZ_ASSERT(NS_IsMainThread());
103 
104   if (!mIPCOpened) {
105     return;
106   }
107 
108   mChannelParent = aChannelParent;
109 }
110 
OnChannelClosed()111 void HttpBackgroundChannelParent::OnChannelClosed() {
112   LOG(("HttpBackgroundChannelParent::OnChannelClosed [this=%p]\n", this));
113   AssertIsInMainProcess();
114   MOZ_ASSERT(NS_IsMainThread());
115 
116   if (!mIPCOpened) {
117     return;
118   }
119 
120   nsresult rv;
121 
122   {
123     MutexAutoLock lock(mBgThreadMutex);
124     RefPtr<HttpBackgroundChannelParent> self = this;
125     rv = mBackgroundThread->Dispatch(
126         NS_NewRunnableFunction(
127             "net::HttpBackgroundChannelParent::OnChannelClosed",
128             [self]() {
129               LOG(("HttpBackgroundChannelParent::DeleteRunnable [this=%p]\n",
130                    self.get()));
131               AssertIsOnBackgroundThread();
132 
133               if (!self->mIPCOpened.compareExchange(true, false)) {
134                 return;
135               }
136 
137               Unused << self->Send__delete__(self);
138             }),
139         NS_DISPATCH_NORMAL);
140   }
141 
142   Unused << NS_WARN_IF(NS_FAILED(rv));
143 }
144 
OnStartRequest(const nsHttpResponseHead & aResponseHead,const bool & aUseResponseHead,const nsHttpHeaderArray & aRequestHeaders,const HttpChannelOnStartRequestArgs & aArgs)145 bool HttpBackgroundChannelParent::OnStartRequest(
146     const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
147     const nsHttpHeaderArray& aRequestHeaders,
148     const HttpChannelOnStartRequestArgs& aArgs) {
149   LOG(("HttpBackgroundChannelParent::OnStartRequest [this=%p]\n", this));
150   AssertIsInMainProcess();
151 
152   if (NS_WARN_IF(!mIPCOpened)) {
153     return false;
154   }
155 
156   if (!IsOnBackgroundThread()) {
157     MutexAutoLock lock(mBgThreadMutex);
158     nsresult rv = mBackgroundThread->Dispatch(
159         NewRunnableMethod<const nsHttpResponseHead, const bool,
160                           const nsHttpHeaderArray,
161                           const HttpChannelOnStartRequestArgs>(
162             "net::HttpBackgroundChannelParent::OnStartRequest", this,
163             &HttpBackgroundChannelParent::OnStartRequest, aResponseHead,
164             aUseResponseHead, aRequestHeaders, aArgs),
165         NS_DISPATCH_NORMAL);
166 
167     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
168 
169     return NS_SUCCEEDED(rv);
170   }
171 
172   return SendOnStartRequest(aResponseHead, aUseResponseHead, aRequestHeaders,
173                             aArgs);
174 }
175 
OnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)176 bool HttpBackgroundChannelParent::OnTransportAndData(
177     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
178     const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
179   LOG(("HttpBackgroundChannelParent::OnTransportAndData [this=%p]\n", this));
180   AssertIsInMainProcess();
181 
182   if (NS_WARN_IF(!mIPCOpened)) {
183     return false;
184   }
185 
186   if (!IsOnBackgroundThread()) {
187     MutexAutoLock lock(mBgThreadMutex);
188     nsresult rv = mBackgroundThread->Dispatch(
189         NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
190                           const uint32_t, const nsCString>(
191             "net::HttpBackgroundChannelParent::OnTransportAndData", this,
192             &HttpBackgroundChannelParent::OnTransportAndData, aChannelStatus,
193             aTransportStatus, aOffset, aCount, aData),
194         NS_DISPATCH_NORMAL);
195 
196     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
197 
198     return NS_SUCCEEDED(rv);
199   }
200 
201   nsHttp::SendFunc<nsDependentCSubstring> sendFunc =
202       [self = UnsafePtr<HttpBackgroundChannelParent>(this), aChannelStatus,
203        aTransportStatus](const nsDependentCSubstring& aData, uint64_t aOffset,
204                          uint32_t aCount) {
205         return self->SendOnTransportAndData(aChannelStatus, aTransportStatus,
206                                             aOffset, aCount, aData, false);
207       };
208 
209   return nsHttp::SendDataInChunks(aData, aOffset, aCount, sendFunc);
210 }
211 
OnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const nsHttpHeaderArray & aResponseTrailers,const nsTArray<ConsoleReportCollected> & aConsoleReports)212 bool HttpBackgroundChannelParent::OnStopRequest(
213     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
214     const nsHttpHeaderArray& aResponseTrailers,
215     const nsTArray<ConsoleReportCollected>& aConsoleReports) {
216   LOG(
217       ("HttpBackgroundChannelParent::OnStopRequest [this=%p "
218        "status=%" PRIx32 "]\n",
219        this, static_cast<uint32_t>(aChannelStatus)));
220   AssertIsInMainProcess();
221 
222   if (NS_WARN_IF(!mIPCOpened)) {
223     return false;
224   }
225 
226   if (!IsOnBackgroundThread()) {
227     MutexAutoLock lock(mBgThreadMutex);
228     nsresult rv = mBackgroundThread->Dispatch(
229         NewRunnableMethod<const nsresult, const ResourceTimingStructArgs,
230                           const nsHttpHeaderArray,
231                           const CopyableTArray<ConsoleReportCollected>>(
232             "net::HttpBackgroundChannelParent::OnStopRequest", this,
233             &HttpBackgroundChannelParent::OnStopRequest, aChannelStatus,
234             aTiming, aResponseTrailers, aConsoleReports),
235         NS_DISPATCH_NORMAL);
236 
237     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
238 
239     return NS_SUCCEEDED(rv);
240   }
241 
242   // See the child code for why we do this.
243   TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
244 
245   return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt,
246                            aResponseTrailers, aConsoleReports, false);
247 }
248 
OnConsoleReport(const nsTArray<ConsoleReportCollected> & aConsoleReports)249 bool HttpBackgroundChannelParent::OnConsoleReport(
250     const nsTArray<ConsoleReportCollected>& aConsoleReports) {
251   LOG(("HttpBackgroundChannelParent::OnConsoleReport [this=%p]", this));
252   AssertIsInMainProcess();
253 
254   if (NS_WARN_IF(!mIPCOpened)) {
255     return false;
256   }
257 
258   if (!IsOnBackgroundThread()) {
259     MutexAutoLock lock(mBgThreadMutex);
260     nsresult rv = mBackgroundThread->Dispatch(
261         NewRunnableMethod<const CopyableTArray<ConsoleReportCollected>>(
262             "net::HttpBackgroundChannelParent::OnConsoleReport", this,
263             &HttpBackgroundChannelParent::OnConsoleReport, aConsoleReports),
264         NS_DISPATCH_NORMAL);
265 
266     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
267 
268     return NS_SUCCEEDED(rv);
269   }
270 
271   return SendOnConsoleReport(aConsoleReports);
272 }
273 
OnAfterLastPart(const nsresult aStatus)274 bool HttpBackgroundChannelParent::OnAfterLastPart(const nsresult aStatus) {
275   LOG(("HttpBackgroundChannelParent::OnAfterLastPart [this=%p]\n", this));
276   AssertIsInMainProcess();
277 
278   if (NS_WARN_IF(!mIPCOpened)) {
279     return false;
280   }
281 
282   if (!IsOnBackgroundThread()) {
283     MutexAutoLock lock(mBgThreadMutex);
284     nsresult rv = mBackgroundThread->Dispatch(
285         NewRunnableMethod<const nsresult>(
286             "net::HttpBackgroundChannelParent::OnAfterLastPart", this,
287             &HttpBackgroundChannelParent::OnAfterLastPart, aStatus),
288         NS_DISPATCH_NORMAL);
289 
290     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
291 
292     return NS_SUCCEEDED(rv);
293   }
294 
295   return SendOnAfterLastPart(aStatus);
296 }
297 
OnProgress(const int64_t aProgress,const int64_t aProgressMax)298 bool HttpBackgroundChannelParent::OnProgress(const int64_t aProgress,
299                                              const int64_t aProgressMax) {
300   LOG(("HttpBackgroundChannelParent::OnProgress [this=%p]\n", this));
301   AssertIsInMainProcess();
302 
303   if (NS_WARN_IF(!mIPCOpened)) {
304     return false;
305   }
306 
307   if (!IsOnBackgroundThread()) {
308     MutexAutoLock lock(mBgThreadMutex);
309     nsresult rv = mBackgroundThread->Dispatch(
310         NewRunnableMethod<const int64_t, const int64_t>(
311             "net::HttpBackgroundChannelParent::OnProgress", this,
312             &HttpBackgroundChannelParent::OnProgress, aProgress, aProgressMax),
313         NS_DISPATCH_NORMAL);
314 
315     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
316 
317     return NS_SUCCEEDED(rv);
318   }
319 
320   return SendOnProgress(aProgress, aProgressMax);
321 }
322 
OnStatus(const nsresult aStatus)323 bool HttpBackgroundChannelParent::OnStatus(const nsresult aStatus) {
324   LOG(("HttpBackgroundChannelParent::OnStatus [this=%p]\n", this));
325   AssertIsInMainProcess();
326 
327   if (NS_WARN_IF(!mIPCOpened)) {
328     return false;
329   }
330 
331   if (!IsOnBackgroundThread()) {
332     MutexAutoLock lock(mBgThreadMutex);
333     nsresult rv = mBackgroundThread->Dispatch(
334         NewRunnableMethod<const nsresult>(
335             "net::HttpBackgroundChannelParent::OnStatus", this,
336             &HttpBackgroundChannelParent::OnStatus, aStatus),
337         NS_DISPATCH_NORMAL);
338 
339     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
340 
341     return NS_SUCCEEDED(rv);
342   }
343 
344   return SendOnStatus(aStatus);
345 }
346 
OnNotifyClassificationFlags(uint32_t aClassificationFlags,bool aIsThirdParty)347 bool HttpBackgroundChannelParent::OnNotifyClassificationFlags(
348     uint32_t aClassificationFlags, bool aIsThirdParty) {
349   LOG(
350       ("HttpBackgroundChannelParent::OnNotifyClassificationFlags "
351        "classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
352        aClassificationFlags, static_cast<int>(aIsThirdParty), this));
353   AssertIsInMainProcess();
354 
355   if (NS_WARN_IF(!mIPCOpened)) {
356     return false;
357   }
358 
359   if (!IsOnBackgroundThread()) {
360     MutexAutoLock lock(mBgThreadMutex);
361     nsresult rv = mBackgroundThread->Dispatch(
362         NewRunnableMethod<uint32_t, bool>(
363             "net::HttpBackgroundChannelParent::OnNotifyClassificationFlags",
364             this, &HttpBackgroundChannelParent::OnNotifyClassificationFlags,
365             aClassificationFlags, aIsThirdParty),
366         NS_DISPATCH_NORMAL);
367 
368     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
369 
370     return NS_SUCCEEDED(rv);
371   }
372 
373   return SendNotifyClassificationFlags(aClassificationFlags, aIsThirdParty);
374 }
375 
OnNotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState)376 bool HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged(
377     nsIHttpChannel::FlashPluginState aState) {
378   LOG(
379       ("HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged "
380        "[this=%p]\n",
381        this));
382   AssertIsInMainProcess();
383 
384   if (NS_WARN_IF(!mIPCOpened)) {
385     return false;
386   }
387 
388   if (!IsOnBackgroundThread()) {
389     MutexAutoLock lock(mBgThreadMutex);
390     RefPtr<HttpBackgroundChannelParent> self = this;
391     nsresult rv = mBackgroundThread->Dispatch(
392         NS_NewRunnableFunction(
393             "net::HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged",
394             [self, aState]() {
395               self->OnNotifyFlashPluginStateChanged(aState);
396             }),
397         NS_DISPATCH_NORMAL);
398 
399     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
400 
401     return NS_SUCCEEDED(rv);
402   }
403 
404   return SendNotifyFlashPluginStateChanged(aState);
405 }
406 
OnSetClassifierMatchedInfo(const nsACString & aList,const nsACString & aProvider,const nsACString & aFullHash)407 bool HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
408     const nsACString& aList, const nsACString& aProvider,
409     const nsACString& aFullHash) {
410   LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n",
411        this));
412   AssertIsInMainProcess();
413 
414   if (NS_WARN_IF(!mIPCOpened)) {
415     return false;
416   }
417 
418   if (!IsOnBackgroundThread()) {
419     MutexAutoLock lock(mBgThreadMutex);
420     nsresult rv = mBackgroundThread->Dispatch(
421         NewRunnableMethod<const nsCString, const nsCString, const nsCString>(
422             "net::HttpBackgroundChannelParent::OnSetClassifierMatchedInfo",
423             this, &HttpBackgroundChannelParent::OnSetClassifierMatchedInfo,
424             aList, aProvider, aFullHash),
425         NS_DISPATCH_NORMAL);
426 
427     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
428 
429     return NS_SUCCEEDED(rv);
430   }
431 
432   ClassifierInfo info;
433   info.list() = aList;
434   info.fullhash() = aFullHash;
435   info.provider() = aProvider;
436 
437   return SendSetClassifierMatchedInfo(info);
438 }
439 
OnSetClassifierMatchedTrackingInfo(const nsACString & aLists,const nsACString & aFullHashes)440 bool HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo(
441     const nsACString& aLists, const nsACString& aFullHashes) {
442   LOG(
443       ("HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo "
444        "[this=%p]\n",
445        this));
446   AssertIsInMainProcess();
447 
448   if (NS_WARN_IF(!mIPCOpened)) {
449     return false;
450   }
451 
452   if (!IsOnBackgroundThread()) {
453     MutexAutoLock lock(mBgThreadMutex);
454     nsresult rv = mBackgroundThread->Dispatch(
455         NewRunnableMethod<const nsCString, const nsCString>(
456             "net::HttpBackgroundChannelParent::"
457             "OnSetClassifierMatchedTrackingInfo",
458             this,
459             &HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo,
460             aLists, aFullHashes),
461         NS_DISPATCH_NORMAL);
462 
463     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
464 
465     return NS_SUCCEEDED(rv);
466   }
467 
468   ClassifierInfo info;
469   info.list() = aLists;
470   info.fullhash() = aFullHashes;
471 
472   return SendSetClassifierMatchedTrackingInfo(info);
473 }
474 
GetBackgroundTarget()475 nsISerialEventTarget* HttpBackgroundChannelParent::GetBackgroundTarget() {
476   MOZ_ASSERT(mBackgroundThread);
477   return mBackgroundThread.get();
478 }
479 
AttachStreamFilter(Endpoint<extensions::PStreamFilterParent> && aParentEndpoint,Endpoint<extensions::PStreamFilterChild> && aChildEndpoint)480 auto HttpBackgroundChannelParent::AttachStreamFilter(
481     Endpoint<extensions::PStreamFilterParent>&& aParentEndpoint,
482     Endpoint<extensions::PStreamFilterChild>&& aChildEndpoint)
483     -> RefPtr<ChildEndpointPromise> {
484   LOG(("HttpBackgroundChannelParent::AttachStreamFilter [this=%p]\n", this));
485   MOZ_ASSERT(IsOnBackgroundThread());
486 
487   if (NS_WARN_IF(!mIPCOpened) ||
488       !SendAttachStreamFilter(std::move(aParentEndpoint))) {
489     return ChildEndpointPromise::CreateAndReject(false, __func__);
490   }
491 
492   return ChildEndpointPromise::CreateAndResolve(std::move(aChildEndpoint),
493                                                 __func__);
494 }
495 
ActorDestroy(ActorDestroyReason aWhy)496 void HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy) {
497   LOG(("HttpBackgroundChannelParent::ActorDestroy [this=%p]\n", this));
498   AssertIsInMainProcess();
499   AssertIsOnBackgroundThread();
500 
501   mIPCOpened = false;
502 
503   RefPtr<HttpBackgroundChannelParent> self = this;
504   DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
505       "net::HttpBackgroundChannelParent::ActorDestroy", [self]() {
506         MOZ_ASSERT(NS_IsMainThread());
507 
508         RefPtr<HttpChannelParent> channelParent =
509             std::move(self->mChannelParent);
510 
511         if (channelParent) {
512           channelParent->OnBackgroundParentDestroyed();
513         }
514       }));
515   MOZ_ASSERT(NS_SUCCEEDED(rv));
516 }
517 
518 }  // namespace net
519 }  // namespace mozilla
520