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 "HttpBackgroundChannelChild.h"
12 
13 #include "HttpChannelChild.h"
14 #include "mozilla/ipc/BackgroundChild.h"
15 #include "mozilla/ipc/PBackgroundChild.h"
16 #include "mozilla/IntegerPrintfMacros.h"
17 #include "mozilla/Unused.h"
18 #include "nsSocketTransportService2.h"
19 
20 using mozilla::ipc::BackgroundChild;
21 using mozilla::ipc::IPCResult;
22 
23 namespace mozilla {
24 namespace net {
25 
26 // HttpBackgroundChannelChild
HttpBackgroundChannelChild()27 HttpBackgroundChannelChild::HttpBackgroundChannelChild() {}
28 
~HttpBackgroundChannelChild()29 HttpBackgroundChannelChild::~HttpBackgroundChannelChild() {}
30 
Init(HttpChannelChild * aChannelChild)31 nsresult HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild) {
32   LOG(
33       ("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p "
34        "channelId=%" PRIu64 "]\n",
35        this, aChannelChild, aChannelChild->ChannelId()));
36   MOZ_ASSERT(OnSocketThread());
37   NS_ENSURE_ARG(aChannelChild);
38 
39   mChannelChild = aChannelChild;
40 
41   if (NS_WARN_IF(!CreateBackgroundChannel())) {
42     mChannelChild = nullptr;
43     return NS_ERROR_FAILURE;
44   }
45 
46   return NS_OK;
47 }
48 
OnChannelClosed()49 void HttpBackgroundChannelChild::OnChannelClosed() {
50   LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
51   MOZ_ASSERT(OnSocketThread());
52 
53   // HttpChannelChild is not going to handle any incoming message.
54   mChannelChild = nullptr;
55 
56   // Remove pending IPC messages as well.
57   mQueuedRunnables.Clear();
58 }
59 
OnStartRequestReceived()60 void HttpBackgroundChannelChild::OnStartRequestReceived() {
61   LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
62   MOZ_ASSERT(OnSocketThread());
63   MOZ_ASSERT(mChannelChild);
64   MOZ_ASSERT(!mStartReceived);  // Should only be called once.
65 
66   mStartReceived = true;
67 
68   nsTArray<nsCOMPtr<nsIRunnable>> runnables;
69   runnables.SwapElements(mQueuedRunnables);
70 
71   for (auto event : runnables) {
72     // Note: these runnables call Recv* methods on HttpBackgroundChannelChild
73     // but not the Process* methods on HttpChannelChild.
74     event->Run();
75   }
76 
77   // Ensure no new message is enqueued.
78   MOZ_ASSERT(mQueuedRunnables.IsEmpty());
79 }
80 
CreateBackgroundChannel()81 bool HttpBackgroundChannelChild::CreateBackgroundChannel() {
82   LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n",
83        this));
84   MOZ_ASSERT(OnSocketThread());
85   MOZ_ASSERT(mChannelChild);
86 
87   PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
88   if (NS_WARN_IF(!actorChild)) {
89     return false;
90   }
91 
92   const uint64_t channelId = mChannelChild->ChannelId();
93   if (!actorChild->SendPHttpBackgroundChannelConstructor(this, channelId)) {
94     return false;
95   }
96 
97   // hold extra reference for IPDL
98   RefPtr<HttpBackgroundChannelChild> self = this;
99   Unused << self.forget().take();
100 
101   mChannelChild->OnBackgroundChildReady(this);
102   return true;
103 }
104 
IsWaitingOnStartRequest()105 bool HttpBackgroundChannelChild::IsWaitingOnStartRequest() {
106   MOZ_ASSERT(OnSocketThread());
107   // Need to wait for OnStartRequest if it is sent by
108   // parent process but not received by content process.
109   return (mStartSent && !mStartReceived);
110 }
111 
112 // PHttpBackgroundChannelChild
RecvOnStartRequestSent()113 IPCResult HttpBackgroundChannelChild::RecvOnStartRequestSent() {
114   LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
115   MOZ_ASSERT(OnSocketThread());
116   MOZ_ASSERT(!mStartSent);  // Should only receive this message once.
117 
118   mStartSent = true;
119   return IPC_OK();
120 }
121 
RecvOnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)122 IPCResult HttpBackgroundChannelChild::RecvOnTransportAndData(
123     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
124     const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
125   LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this));
126   MOZ_ASSERT(OnSocketThread());
127 
128   if (NS_WARN_IF(!mChannelChild)) {
129     return IPC_OK();
130   }
131 
132   if (IsWaitingOnStartRequest()) {
133     LOG(("  > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
134          "]\n",
135          aOffset, aCount));
136 
137     mQueuedRunnables.AppendElement(
138         NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
139                           const uint32_t, const nsCString>(
140             "HttpBackgroundChannelChild::RecvOnTransportAndData", this,
141             &HttpBackgroundChannelChild::RecvOnTransportAndData, aChannelStatus,
142             aTransportStatus, aOffset, aCount, aData));
143 
144     return IPC_OK();
145   }
146 
147   mChannelChild->ProcessOnTransportAndData(aChannelStatus, aTransportStatus,
148                                            aOffset, aCount, aData);
149 
150   return IPC_OK();
151 }
152 
RecvOnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStruct & aTiming,const TimeStamp & aLastActiveTabOptHit,const nsHttpHeaderArray & aResponseTrailers)153 IPCResult HttpBackgroundChannelChild::RecvOnStopRequest(
154     const nsresult& aChannelStatus, const ResourceTimingStruct& aTiming,
155     const TimeStamp& aLastActiveTabOptHit,
156     const nsHttpHeaderArray& aResponseTrailers) {
157   LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
158   MOZ_ASSERT(OnSocketThread());
159 
160   // It's enough to set this from (just before) OnStopRequest notification,
161   // since we don't need this value sooner than a channel was done loading -
162   // everything this timestamp affects takes place only after a channel's
163   // OnStopRequest.
164   nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
165 
166   if (NS_WARN_IF(!mChannelChild)) {
167     return IPC_OK();
168   }
169 
170   if (IsWaitingOnStartRequest()) {
171     LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
172          static_cast<uint32_t>(aChannelStatus)));
173 
174     mQueuedRunnables.AppendElement(
175         NewRunnableMethod<const nsresult, const ResourceTimingStruct,
176                           const TimeStamp, const nsHttpHeaderArray>(
177             "HttpBackgroundChannelChild::RecvOnStopRequest", this,
178             &HttpBackgroundChannelChild::RecvOnStopRequest, aChannelStatus,
179             aTiming, aLastActiveTabOptHit, aResponseTrailers));
180 
181     return IPC_OK();
182   }
183 
184   mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming,
185                                       aResponseTrailers);
186 
187   return IPC_OK();
188 }
189 
RecvOnProgress(const int64_t & aProgress,const int64_t & aProgressMax)190 IPCResult HttpBackgroundChannelChild::RecvOnProgress(
191     const int64_t& aProgress, const int64_t& aProgressMax) {
192   LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%" PRId64
193        " max=%" PRId64 "]\n",
194        this, aProgress, aProgressMax));
195   MOZ_ASSERT(OnSocketThread());
196 
197   if (NS_WARN_IF(!mChannelChild)) {
198     return IPC_OK();
199   }
200 
201   if (IsWaitingOnStartRequest()) {
202     LOG(("  > pending until OnStartRequest [progress=%" PRId64 " max=%" PRId64
203          "]\n",
204          aProgress, aProgressMax));
205 
206     mQueuedRunnables.AppendElement(
207         NewRunnableMethod<const int64_t, const int64_t>(
208             "HttpBackgroundChannelChild::RecvOnProgress", this,
209             &HttpBackgroundChannelChild::RecvOnProgress, aProgress,
210             aProgressMax));
211 
212     return IPC_OK();
213   }
214 
215   mChannelChild->ProcessOnProgress(aProgress, aProgressMax);
216 
217   return IPC_OK();
218 }
219 
RecvOnStatus(const nsresult & aStatus)220 IPCResult HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus) {
221   LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%" PRIx32
222        "]\n",
223        this, static_cast<uint32_t>(aStatus)));
224   MOZ_ASSERT(OnSocketThread());
225 
226   if (NS_WARN_IF(!mChannelChild)) {
227     return IPC_OK();
228   }
229 
230   if (IsWaitingOnStartRequest()) {
231     LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
232          static_cast<uint32_t>(aStatus)));
233 
234     mQueuedRunnables.AppendElement(NewRunnableMethod<const nsresult>(
235         "HttpBackgroundChannelChild::RecvOnStatus", this,
236         &HttpBackgroundChannelChild::RecvOnStatus, aStatus));
237 
238     return IPC_OK();
239   }
240 
241   mChannelChild->ProcessOnStatus(aStatus);
242 
243   return IPC_OK();
244 }
245 
RecvFlushedForDiversion()246 IPCResult HttpBackgroundChannelChild::RecvFlushedForDiversion() {
247   LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n",
248        this));
249   MOZ_ASSERT(OnSocketThread());
250 
251   if (NS_WARN_IF(!mChannelChild)) {
252     return IPC_OK();
253   }
254 
255   if (IsWaitingOnStartRequest()) {
256     LOG(("  > pending until OnStartRequest\n"));
257 
258     mQueuedRunnables.AppendElement(NewRunnableMethod(
259         "HttpBackgroundChannelChild::RecvFlushedForDiversion", this,
260         &HttpBackgroundChannelChild::RecvFlushedForDiversion));
261 
262     return IPC_OK();
263   }
264 
265   mChannelChild->ProcessFlushedForDiversion();
266 
267   return IPC_OK();
268 }
269 
RecvDivertMessages()270 IPCResult HttpBackgroundChannelChild::RecvDivertMessages() {
271   LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this));
272   MOZ_ASSERT(OnSocketThread());
273 
274   if (NS_WARN_IF(!mChannelChild)) {
275     return IPC_OK();
276   }
277 
278   if (IsWaitingOnStartRequest()) {
279     LOG(("  > pending until OnStartRequest\n"));
280 
281     mQueuedRunnables.AppendElement(NewRunnableMethod(
282         "HttpBackgroundChannelChild::RecvDivertMessages", this,
283         &HttpBackgroundChannelChild::RecvDivertMessages));
284 
285     return IPC_OK();
286   }
287 
288   mChannelChild->ProcessDivertMessages();
289 
290   return IPC_OK();
291 }
292 
RecvNotifyTrackingProtectionDisabled()293 IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled() {
294   LOG(
295       ("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled "
296        "[this=%p]\n",
297        this));
298   MOZ_ASSERT(OnSocketThread());
299 
300   if (NS_WARN_IF(!mChannelChild)) {
301     return IPC_OK();
302   }
303 
304   // NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
305   // It this be handled as soon as possible
306   mChannelChild->ProcessNotifyTrackingProtectionDisabled();
307 
308   return IPC_OK();
309 }
310 
RecvNotifyTrackingResource()311 IPCResult HttpBackgroundChannelChild::RecvNotifyTrackingResource() {
312   LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n",
313        this));
314   MOZ_ASSERT(OnSocketThread());
315 
316   if (NS_WARN_IF(!mChannelChild)) {
317     return IPC_OK();
318   }
319 
320   // NotifyTrackingResource has no order dependency to OnStartRequest.
321   // It this be handled as soon as possible
322   mChannelChild->ProcessNotifyTrackingResource();
323 
324   return IPC_OK();
325 }
326 
RecvSetClassifierMatchedInfo(const ClassifierInfo & info)327 IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
328     const ClassifierInfo& info) {
329   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
330        this));
331   MOZ_ASSERT(OnSocketThread());
332 
333   if (NS_WARN_IF(!mChannelChild)) {
334     return IPC_OK();
335   }
336 
337   // SetClassifierMatchedInfo has no order dependency to OnStartRequest.
338   // It this be handled as soon as possible
339   mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(),
340                                                  info.fullhash());
341 
342   return IPC_OK();
343 }
344 
ActorDestroy(ActorDestroyReason aWhy)345 void HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
346   LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
347   // This function might be called during shutdown phase, so OnSocketThread()
348   // might return false even on STS thread. Use IsOnCurrentThreadInfallible()
349   // to get correct information.
350   MOZ_ASSERT(gSocketTransportService);
351   MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
352 
353   // Ensure all IPC messages received before ActorDestroy can be
354   // handled correctly. If there is any pending IPC message, destroyed
355   // mChannelChild until those messages are flushed.
356   // If background channel is not closed by normal IPDL actor deletion,
357   // remove the HttpChannelChild reference and notify background channel
358   // destroyed immediately.
359   if (aWhy == Deletion && !mQueuedRunnables.IsEmpty()) {
360     LOG(("  > pending until queued messages are flushed\n"));
361     RefPtr<HttpBackgroundChannelChild> self = this;
362     mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
363         "HttpBackgroundChannelChild::ActorDestroy", [self]() {
364           MOZ_ASSERT(OnSocketThread());
365           RefPtr<HttpChannelChild> channelChild = self->mChannelChild.forget();
366 
367           if (channelChild) {
368             channelChild->OnBackgroundChildDestroyed(self);
369           }
370         }));
371     return;
372   }
373 
374   if (mChannelChild) {
375     RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
376 
377     channelChild->OnBackgroundChildDestroyed(this);
378   }
379 }
380 
381 }  // namespace net
382 }  // namespace mozilla
383