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 "nsIBackgroundChannelRegistrar.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         do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
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 
OnStartRequestSent()145 bool HttpBackgroundChannelParent::OnStartRequestSent() {
146   LOG(("HttpBackgroundChannelParent::OnStartRequestSent [this=%p]\n", this));
147   AssertIsInMainProcess();
148 
149   if (NS_WARN_IF(!mIPCOpened)) {
150     return false;
151   }
152 
153   if (!IsOnBackgroundThread()) {
154     MutexAutoLock lock(mBgThreadMutex);
155     nsresult rv = mBackgroundThread->Dispatch(
156         NewRunnableMethod(
157             "net::HttpBackgroundChannelParent::OnStartRequestSent", this,
158             &HttpBackgroundChannelParent::OnStartRequestSent),
159         NS_DISPATCH_NORMAL);
160 
161     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
162 
163     return NS_SUCCEEDED(rv);
164   }
165 
166   return SendOnStartRequestSent();
167 }
168 
OnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)169 bool HttpBackgroundChannelParent::OnTransportAndData(
170     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
171     const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
172   LOG(("HttpBackgroundChannelParent::OnTransportAndData [this=%p]\n", this));
173   AssertIsInMainProcess();
174 
175   if (NS_WARN_IF(!mIPCOpened)) {
176     return false;
177   }
178 
179   if (!IsOnBackgroundThread()) {
180     MutexAutoLock lock(mBgThreadMutex);
181     nsresult rv = mBackgroundThread->Dispatch(
182         NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
183                           const uint32_t, const nsCString>(
184             "net::HttpBackgroundChannelParent::OnTransportAndData", this,
185             &HttpBackgroundChannelParent::OnTransportAndData, aChannelStatus,
186             aTransportStatus, aOffset, aCount, aData),
187         NS_DISPATCH_NORMAL);
188 
189     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
190 
191     return NS_SUCCEEDED(rv);
192   }
193 
194   return SendOnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
195                                 aCount, aData);
196 }
197 
OnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStruct & aTiming,const nsHttpHeaderArray & aResponseTrailers)198 bool HttpBackgroundChannelParent::OnStopRequest(
199     const nsresult& aChannelStatus, const ResourceTimingStruct& aTiming,
200     const nsHttpHeaderArray& aResponseTrailers) {
201   LOG(
202       ("HttpBackgroundChannelParent::OnStopRequest [this=%p "
203        "status=%" PRIx32 "]\n",
204        this, static_cast<uint32_t>(aChannelStatus)));
205   AssertIsInMainProcess();
206 
207   if (NS_WARN_IF(!mIPCOpened)) {
208     return false;
209   }
210 
211   if (!IsOnBackgroundThread()) {
212     MutexAutoLock lock(mBgThreadMutex);
213     nsresult rv = mBackgroundThread->Dispatch(
214         NewRunnableMethod<const nsresult, const ResourceTimingStruct,
215                           const nsHttpHeaderArray>(
216             "net::HttpBackgroundChannelParent::OnStopRequest", this,
217             &HttpBackgroundChannelParent::OnStopRequest, aChannelStatus,
218             aTiming, aResponseTrailers),
219         NS_DISPATCH_NORMAL);
220 
221     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
222 
223     return NS_SUCCEEDED(rv);
224   }
225 
226   // See the child code for why we do this.
227   TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
228 
229   return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt,
230                            aResponseTrailers);
231 }
232 
OnProgress(const int64_t & aProgress,const int64_t & aProgressMax)233 bool HttpBackgroundChannelParent::OnProgress(const int64_t& aProgress,
234                                              const int64_t& aProgressMax) {
235   LOG(("HttpBackgroundChannelParent::OnProgress [this=%p progress=%" PRId64
236        " max=%" PRId64 "]\n",
237        this, aProgress, aProgressMax));
238   AssertIsInMainProcess();
239 
240   if (NS_WARN_IF(!mIPCOpened)) {
241     return false;
242   }
243 
244   if (!IsOnBackgroundThread()) {
245     MutexAutoLock lock(mBgThreadMutex);
246     nsresult rv = mBackgroundThread->Dispatch(
247         NewRunnableMethod<const int64_t, const int64_t>(
248             "net::HttpBackgroundChannelParent::OnProgress", this,
249             &HttpBackgroundChannelParent::OnProgress, aProgress, aProgressMax),
250         NS_DISPATCH_NORMAL);
251 
252     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
253 
254     return NS_SUCCEEDED(rv);
255   }
256 
257   return SendOnProgress(aProgress, aProgressMax);
258 }
259 
OnStatus(const nsresult & aStatus)260 bool HttpBackgroundChannelParent::OnStatus(const nsresult& aStatus) {
261   LOG(("HttpBackgroundChannelParent::OnStatus [this=%p stauts=%" PRIx32 "]\n",
262        this, static_cast<uint32_t>(aStatus)));
263   AssertIsInMainProcess();
264 
265   if (NS_WARN_IF(!mIPCOpened)) {
266     return false;
267   }
268 
269   if (!IsOnBackgroundThread()) {
270     MutexAutoLock lock(mBgThreadMutex);
271     nsresult rv = mBackgroundThread->Dispatch(
272         NewRunnableMethod<const nsresult>(
273             "net::HttpBackgroundChannelParent::OnStatus", this,
274             &HttpBackgroundChannelParent::OnStatus, aStatus),
275         NS_DISPATCH_NORMAL);
276 
277     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
278 
279     return NS_SUCCEEDED(rv);
280   }
281 
282   return SendOnStatus(aStatus);
283 }
284 
OnDiversion()285 bool HttpBackgroundChannelParent::OnDiversion() {
286   LOG(("HttpBackgroundChannelParent::OnDiversion [this=%p]\n", this));
287   AssertIsInMainProcess();
288 
289   if (NS_WARN_IF(!mIPCOpened)) {
290     return false;
291   }
292 
293   if (!IsOnBackgroundThread()) {
294     MutexAutoLock lock(mBgThreadMutex);
295     nsresult rv = mBackgroundThread->Dispatch(
296         NewRunnableMethod("net::HttpBackgroundChannelParent::OnDiversion", this,
297                           &HttpBackgroundChannelParent::OnDiversion),
298         NS_DISPATCH_NORMAL);
299 
300     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
301 
302     return NS_SUCCEEDED(rv);
303   }
304 
305   if (!SendFlushedForDiversion()) {
306     return false;
307   }
308 
309   // The listener chain should now be setup; tell HttpChannelChild to divert
310   // the OnDataAvailables and OnStopRequest to associated HttpChannelParent.
311   if (!SendDivertMessages()) {
312     return false;
313   }
314 
315   return true;
316 }
317 
OnNotifyTrackingProtectionDisabled()318 bool HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled() {
319   LOG(
320       ("HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled "
321        "[this=%p]\n",
322        this));
323   AssertIsInMainProcess();
324 
325   if (NS_WARN_IF(!mIPCOpened)) {
326     return false;
327   }
328 
329   if (!IsOnBackgroundThread()) {
330     MutexAutoLock lock(mBgThreadMutex);
331     nsresult rv = mBackgroundThread->Dispatch(
332         NewRunnableMethod(
333             "net::HttpBackgroundChannelParent::"
334             "OnNotifyTrackingProtectionDisabled",
335             this,
336             &HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled),
337         NS_DISPATCH_NORMAL);
338 
339     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
340 
341     return NS_SUCCEEDED(rv);
342   }
343 
344   return SendNotifyTrackingProtectionDisabled();
345 }
346 
OnNotifyTrackingResource()347 bool HttpBackgroundChannelParent::OnNotifyTrackingResource() {
348   LOG(("HttpBackgroundChannelParent::OnNotifyTrackingResource [this=%p]\n",
349        this));
350   AssertIsInMainProcess();
351 
352   if (NS_WARN_IF(!mIPCOpened)) {
353     return false;
354   }
355 
356   if (!IsOnBackgroundThread()) {
357     MutexAutoLock lock(mBgThreadMutex);
358     nsresult rv = mBackgroundThread->Dispatch(
359         NewRunnableMethod(
360             "net::HttpBackgroundChannelParent::OnNotifyTrackingResource", this,
361             &HttpBackgroundChannelParent::OnNotifyTrackingResource),
362         NS_DISPATCH_NORMAL);
363 
364     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
365 
366     return NS_SUCCEEDED(rv);
367   }
368 
369   return SendNotifyTrackingResource();
370 }
371 
OnSetClassifierMatchedInfo(const nsACString & aList,const nsACString & aProvider,const nsACString & aFullHash)372 bool HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
373     const nsACString& aList, const nsACString& aProvider,
374     const nsACString& aFullHash) {
375   LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n",
376        this));
377   AssertIsInMainProcess();
378 
379   if (NS_WARN_IF(!mIPCOpened)) {
380     return false;
381   }
382 
383   if (!IsOnBackgroundThread()) {
384     MutexAutoLock lock(mBgThreadMutex);
385     nsresult rv = mBackgroundThread->Dispatch(
386         NewRunnableMethod<const nsCString, const nsCString, const nsCString>(
387             "net::HttpBackgroundChannelParent::OnSetClassifierMatchedInfo",
388             this, &HttpBackgroundChannelParent::OnSetClassifierMatchedInfo,
389             aList, aProvider, aFullHash),
390         NS_DISPATCH_NORMAL);
391 
392     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
393 
394     return NS_SUCCEEDED(rv);
395   }
396 
397   ClassifierInfo info;
398   info.list() = aList;
399   info.fullhash() = aFullHash;
400   info.provider() = aProvider;
401 
402   return SendSetClassifierMatchedInfo(info);
403 }
404 
ActorDestroy(ActorDestroyReason aWhy)405 void HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy) {
406   LOG(("HttpBackgroundChannelParent::ActorDestroy [this=%p]\n", this));
407   AssertIsInMainProcess();
408   AssertIsOnBackgroundThread();
409 
410   mIPCOpened = false;
411 
412   RefPtr<HttpBackgroundChannelParent> self = this;
413   DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
414       "net::HttpBackgroundChannelParent::ActorDestroy", [self]() {
415         MOZ_ASSERT(NS_IsMainThread());
416 
417         RefPtr<HttpChannelParent> channelParent = self->mChannelParent.forget();
418 
419         if (channelParent) {
420           channelParent->OnBackgroundParentDestroyed();
421         }
422       }));
423   MOZ_ASSERT(NS_SUCCEEDED(rv));
424 }
425 
426 }  // namespace net
427 }  // namespace mozilla
428