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 
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, false);
196 }
197 
OnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const nsHttpHeaderArray & aResponseTrailers,const nsTArray<ConsoleReportCollected> & aConsoleReports)198 bool HttpBackgroundChannelParent::OnStopRequest(
199     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
200     const nsHttpHeaderArray& aResponseTrailers,
201     const nsTArray<ConsoleReportCollected>& aConsoleReports) {
202   LOG(
203       ("HttpBackgroundChannelParent::OnStopRequest [this=%p "
204        "status=%" PRIx32 "]\n",
205        this, static_cast<uint32_t>(aChannelStatus)));
206   AssertIsInMainProcess();
207 
208   if (NS_WARN_IF(!mIPCOpened)) {
209     return false;
210   }
211 
212   if (!IsOnBackgroundThread()) {
213     MutexAutoLock lock(mBgThreadMutex);
214     nsresult rv = mBackgroundThread->Dispatch(
215         NewRunnableMethod<const nsresult, const ResourceTimingStructArgs,
216                           const nsHttpHeaderArray,
217                           const CopyableTArray<ConsoleReportCollected>>(
218             "net::HttpBackgroundChannelParent::OnStopRequest", this,
219             &HttpBackgroundChannelParent::OnStopRequest, aChannelStatus,
220             aTiming, aResponseTrailers, aConsoleReports),
221         NS_DISPATCH_NORMAL);
222 
223     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
224 
225     return NS_SUCCEEDED(rv);
226   }
227 
228   // See the child code for why we do this.
229   TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
230 
231   return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt,
232                            aResponseTrailers, aConsoleReports);
233 }
234 
OnDiversion()235 bool HttpBackgroundChannelParent::OnDiversion() {
236   LOG(("HttpBackgroundChannelParent::OnDiversion [this=%p]\n", this));
237   AssertIsInMainProcess();
238 
239   if (NS_WARN_IF(!mIPCOpened)) {
240     return false;
241   }
242 
243   if (!IsOnBackgroundThread()) {
244     MutexAutoLock lock(mBgThreadMutex);
245     nsresult rv = mBackgroundThread->Dispatch(
246         NewRunnableMethod("net::HttpBackgroundChannelParent::OnDiversion", this,
247                           &HttpBackgroundChannelParent::OnDiversion),
248         NS_DISPATCH_NORMAL);
249 
250     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
251 
252     return NS_SUCCEEDED(rv);
253   }
254 
255   if (!SendFlushedForDiversion()) {
256     return false;
257   }
258 
259   // The listener chain should now be setup; tell HttpChannelChild to divert
260   // the OnDataAvailables and OnStopRequest to associated HttpChannelParent.
261   if (!SendDivertMessages()) {
262     return false;
263   }
264 
265   return true;
266 }
267 
ActorDestroy(ActorDestroyReason aWhy)268 void HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy) {
269   LOG(("HttpBackgroundChannelParent::ActorDestroy [this=%p]\n", this));
270   AssertIsInMainProcess();
271   AssertIsOnBackgroundThread();
272 
273   mIPCOpened = false;
274 
275   RefPtr<HttpBackgroundChannelParent> self = this;
276   DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
277       "net::HttpBackgroundChannelParent::ActorDestroy", [self]() {
278         MOZ_ASSERT(NS_IsMainThread());
279 
280         RefPtr<HttpChannelParent> channelParent =
281             std::move(self->mChannelParent);
282 
283         if (channelParent) {
284           channelParent->OnBackgroundParentDestroyed();
285         }
286       }));
287   MOZ_ASSERT(NS_SUCCEEDED(rv));
288 }
289 
290 }  // namespace net
291 }  // namespace mozilla
292