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/net/BackgroundDataBridgeChild.h"
18 #include "mozilla/Unused.h"
19 #include "nsSocketTransportService2.h"
20 
21 using mozilla::ipc::BackgroundChild;
22 using mozilla::ipc::IPCResult;
23 
24 namespace mozilla {
25 namespace net {
26 
27 // HttpBackgroundChannelChild
28 HttpBackgroundChannelChild::HttpBackgroundChannelChild() = default;
29 
30 HttpBackgroundChannelChild::~HttpBackgroundChannelChild() = default;
31 
Init(HttpChannelChild * aChannelChild)32 nsresult HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild) {
33   LOG(
34       ("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p "
35        "channelId=%" PRIu64 "]\n",
36        this, aChannelChild, aChannelChild->ChannelId()));
37   MOZ_ASSERT(OnSocketThread());
38   NS_ENSURE_ARG(aChannelChild);
39 
40   mChannelChild = aChannelChild;
41 
42   if (NS_WARN_IF(!CreateBackgroundChannel())) {
43 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
44     // mChannelChild may be nulled already. Use aChannelChild
45     aChannelChild->mCreateBackgroundChannelFailed = true;
46 #endif
47     mChannelChild = nullptr;
48     return NS_ERROR_FAILURE;
49   }
50 
51   mFirstODASource = ODA_PENDING;
52   mOnStopRequestCalled = false;
53   return NS_OK;
54 }
55 
CreateDataBridge()56 void HttpBackgroundChannelChild::CreateDataBridge() {
57   MOZ_ASSERT(OnSocketThread());
58 
59   if (!mChannelChild) {
60     return;
61   }
62 
63   PBackgroundChild* actorChild =
64       BackgroundChild::GetOrCreateSocketActorForCurrentThread();
65   if (NS_WARN_IF(!actorChild)) {
66     return;
67   }
68 
69   RefPtr<BackgroundDataBridgeChild> dataBridgeChild =
70       new BackgroundDataBridgeChild(this);
71   Unused << actorChild->SendPBackgroundDataBridgeConstructor(
72       dataBridgeChild, mChannelChild->ChannelId());
73 }
74 
OnChannelClosed()75 void HttpBackgroundChannelChild::OnChannelClosed() {
76   LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
77   MOZ_ASSERT(OnSocketThread());
78 
79 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
80   if (mChannelChild) {
81     mChannelChild->mBackgroundChildQueueFinalState =
82         mQueuedRunnables.IsEmpty() ? HttpChannelChild::BCKCHILD_EMPTY
83                                    : HttpChannelChild::BCKCHILD_NON_EMPTY;
84   }
85 #endif
86 
87   // HttpChannelChild is not going to handle any incoming message.
88   mChannelChild = nullptr;
89 
90   // Remove pending IPC messages as well.
91   mQueuedRunnables.Clear();
92   mConsoleReportTask = nullptr;
93 }
94 
ChannelClosed()95 bool HttpBackgroundChannelChild::ChannelClosed() {
96   MOZ_ASSERT(OnSocketThread());
97 
98   return !mChannelChild;
99 }
100 
OnStartRequestReceived(Maybe<uint32_t> aMultiPartID)101 void HttpBackgroundChannelChild::OnStartRequestReceived(
102     Maybe<uint32_t> aMultiPartID) {
103   LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
104   MOZ_ASSERT(OnSocketThread());
105   MOZ_ASSERT(mChannelChild);
106   MOZ_ASSERT(!mStartReceived || *aMultiPartID > 0);
107 
108   mStartReceived = true;
109 
110   nsTArray<nsCOMPtr<nsIRunnable>> runnables = std::move(mQueuedRunnables);
111 
112   for (const auto& event : runnables) {
113     // Note: these runnables call Recv* methods on HttpBackgroundChannelChild
114     // but not the Process* methods on HttpChannelChild.
115     event->Run();
116   }
117 
118   // Ensure no new message is enqueued.
119   MOZ_ASSERT(mQueuedRunnables.IsEmpty());
120 }
121 
CreateBackgroundChannel()122 bool HttpBackgroundChannelChild::CreateBackgroundChannel() {
123   LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n",
124        this));
125   MOZ_ASSERT(OnSocketThread());
126   MOZ_ASSERT(mChannelChild);
127 
128   PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
129   if (NS_WARN_IF(!actorChild)) {
130     return false;
131   }
132 
133   const uint64_t channelId = mChannelChild->ChannelId();
134   if (!actorChild->SendPHttpBackgroundChannelConstructor(this, channelId)) {
135     return false;
136   }
137 
138   mChannelChild->OnBackgroundChildReady(this);
139   return true;
140 }
141 
RecvOnAfterLastPart(const nsresult & aStatus)142 IPCResult HttpBackgroundChannelChild::RecvOnAfterLastPart(
143     const nsresult& aStatus) {
144   LOG(("HttpBackgroundChannelChild::RecvOnAfterLastPart [this=%p]\n", this));
145   MOZ_ASSERT(OnSocketThread());
146 
147   if (NS_WARN_IF(!mChannelChild)) {
148     return IPC_OK();
149   }
150 
151   mChannelChild->ProcessOnAfterLastPart(aStatus);
152   return IPC_OK();
153 }
154 
RecvOnProgress(const int64_t & aProgress,const int64_t & aProgressMax)155 IPCResult HttpBackgroundChannelChild::RecvOnProgress(
156     const int64_t& aProgress, const int64_t& aProgressMax) {
157   LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p]\n", this));
158   MOZ_ASSERT(OnSocketThread());
159 
160   if (NS_WARN_IF(!mChannelChild)) {
161     return IPC_OK();
162   }
163 
164   mChannelChild->ProcessOnProgress(aProgress, aProgressMax);
165   return IPC_OK();
166 }
167 
RecvOnStatus(const nsresult & aStatus)168 IPCResult HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus) {
169   LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p]\n", this));
170   MOZ_ASSERT(OnSocketThread());
171 
172   if (NS_WARN_IF(!mChannelChild)) {
173     return IPC_OK();
174   }
175 
176   mChannelChild->ProcessOnStatus(aStatus);
177   return IPC_OK();
178 }
179 
IsWaitingOnStartRequest()180 bool HttpBackgroundChannelChild::IsWaitingOnStartRequest() {
181   MOZ_ASSERT(OnSocketThread());
182 
183   // Need to wait for OnStartRequest if it is sent by
184   // parent process but not received by content process.
185   return !mStartReceived;
186 }
187 
188 // PHttpBackgroundChannelChild
RecvOnStartRequest(const nsHttpResponseHead & aResponseHead,const bool & aUseResponseHead,const nsHttpHeaderArray & aRequestHeaders,const HttpChannelOnStartRequestArgs & aArgs)189 IPCResult HttpBackgroundChannelChild::RecvOnStartRequest(
190     const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
191     const nsHttpHeaderArray& aRequestHeaders,
192     const HttpChannelOnStartRequestArgs& aArgs) {
193   LOG((
194       "HttpBackgroundChannelChild::RecvOnStartRequest [this=%p, status=%" PRIx32
195       "]\n",
196       this, static_cast<uint32_t>(aArgs.channelStatus())));
197   MOZ_ASSERT(OnSocketThread());
198 
199   if (NS_WARN_IF(!mChannelChild)) {
200     return IPC_OK();
201   }
202 
203   mFirstODASource =
204       aArgs.dataFromSocketProcess() ? ODA_FROM_SOCKET : ODA_FROM_PARENT;
205 
206   mChannelChild->ProcessOnStartRequest(aResponseHead, aUseResponseHead,
207                                        aRequestHeaders, aArgs);
208   // Allow to queue other runnable since OnStartRequest Event already hits the
209   // child's mEventQ.
210   OnStartRequestReceived(aArgs.multiPartID());
211 
212   return IPC_OK();
213 }
214 
RecvOnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsDependentCSubstring & aData,const bool & aDataFromSocketProcess)215 IPCResult HttpBackgroundChannelChild::RecvOnTransportAndData(
216     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
217     const uint64_t& aOffset, const uint32_t& aCount,
218     const nsDependentCSubstring& aData, const bool& aDataFromSocketProcess) {
219   RefPtr<HttpBackgroundChannelChild> self = this;
220   nsCString data(aData);
221   std::function<void()> callProcessOnTransportAndData =
222       [self, aChannelStatus, aTransportStatus, aOffset, aCount, data,
223        aDataFromSocketProcess]() {
224         LOG(
225             ("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p, "
226              "aDataFromSocketProcess=%d, mFirstODASource=%d]\n",
227              self.get(), aDataFromSocketProcess, self->mFirstODASource));
228         MOZ_ASSERT(OnSocketThread());
229 
230         if (NS_WARN_IF(!self->mChannelChild)) {
231           return;
232         }
233 
234         if (((self->mFirstODASource == ODA_FROM_SOCKET) &&
235              !aDataFromSocketProcess) ||
236             ((self->mFirstODASource == ODA_FROM_PARENT) &&
237              aDataFromSocketProcess)) {
238           return;
239         }
240 
241         // The HttpTransactionChild in socket process may not know that this
242         // request is cancelled or failed due to the IPC delay. In this case, we
243         // should not forward ODA to HttpChannelChild.
244         nsresult channelStatus;
245         self->mChannelChild->GetStatus(&channelStatus);
246         if (NS_FAILED(channelStatus)) {
247           return;
248         }
249 
250         self->mChannelChild->ProcessOnTransportAndData(
251             aChannelStatus, aTransportStatus, aOffset, aCount, data);
252       };
253 
254   // Bug 1641336: Race only happens if the data is from socket process.
255   if (IsWaitingOnStartRequest()) {
256     LOG(("  > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
257          "]\n",
258          aOffset, aCount));
259 
260     mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
261         "HttpBackgroundChannelChild::RecvOnTransportAndData",
262         std::move(callProcessOnTransportAndData)));
263     return IPC_OK();
264   }
265 
266   callProcessOnTransportAndData();
267   return IPC_OK();
268 }
269 
RecvOnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const TimeStamp & aLastActiveTabOptHit,const nsHttpHeaderArray & aResponseTrailers,nsTArray<ConsoleReportCollected> && aConsoleReports,const bool & aFromSocketProcess)270 IPCResult HttpBackgroundChannelChild::RecvOnStopRequest(
271     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
272     const TimeStamp& aLastActiveTabOptHit,
273     const nsHttpHeaderArray& aResponseTrailers,
274     nsTArray<ConsoleReportCollected>&& aConsoleReports,
275     const bool& aFromSocketProcess) {
276   LOG(
277       ("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p, "
278        "aFromSocketProcess=%d, mFirstODASource=%d]\n",
279        this, aFromSocketProcess, mFirstODASource));
280   MOZ_ASSERT(gSocketTransportService);
281   MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
282 
283   // It's enough to set this from (just before) OnStopRequest notification,
284   // since we don't need this value sooner than a channel was done loading -
285   // everything this timestamp affects takes place only after a channel's
286   // OnStopRequest.
287   nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
288 
289   if (NS_WARN_IF(!mChannelChild)) {
290     return IPC_OK();
291   }
292 
293   if (IsWaitingOnStartRequest()) {
294     LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
295          static_cast<uint32_t>(aChannelStatus)));
296 
297     RefPtr<HttpBackgroundChannelChild> self = this;
298 
299     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
300         "HttpBackgroundChannelChild::RecvOnStopRequest",
301         [self, aChannelStatus, aTiming, aLastActiveTabOptHit, aResponseTrailers,
302          consoleReports = CopyableTArray{std::move(aConsoleReports)},
303          aFromSocketProcess]() mutable {
304           self->RecvOnStopRequest(aChannelStatus, aTiming, aLastActiveTabOptHit,
305                                   aResponseTrailers, std::move(consoleReports),
306                                   aFromSocketProcess);
307         });
308 
309     mQueuedRunnables.AppendElement(task.forget());
310     return IPC_OK();
311   }
312 
313   if (mFirstODASource != ODA_FROM_SOCKET) {
314     if (!aFromSocketProcess) {
315       mOnStopRequestCalled = true;
316       mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming,
317                                           aResponseTrailers,
318                                           std::move(aConsoleReports), false);
319     }
320     return IPC_OK();
321   }
322 
323   MOZ_ASSERT(mFirstODASource == ODA_FROM_SOCKET);
324 
325   if (aFromSocketProcess) {
326     MOZ_ASSERT(!mOnStopRequestCalled);
327     mOnStopRequestCalled = true;
328     mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming,
329                                         aResponseTrailers,
330                                         std::move(aConsoleReports), true);
331     if (mConsoleReportTask) {
332       mConsoleReportTask();
333       mConsoleReportTask = nullptr;
334     }
335     return IPC_OK();
336   }
337 
338   return IPC_OK();
339 }
340 
RecvOnConsoleReport(nsTArray<ConsoleReportCollected> && aConsoleReports)341 IPCResult HttpBackgroundChannelChild::RecvOnConsoleReport(
342     nsTArray<ConsoleReportCollected>&& aConsoleReports) {
343   LOG(("HttpBackgroundChannelChild::RecvOnConsoleReport [this=%p]\n", this));
344   MOZ_ASSERT(mFirstODASource == ODA_FROM_SOCKET);
345   MOZ_ASSERT(gSocketTransportService);
346   MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
347 
348   if (IsWaitingOnStartRequest()) {
349     LOG(("  > pending until OnStartRequest\n"));
350 
351     RefPtr<HttpBackgroundChannelChild> self = this;
352 
353     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
354         "HttpBackgroundChannelChild::RecvOnConsoleReport",
355         [self, consoleReports =
356                    CopyableTArray{std::move(aConsoleReports)}]() mutable {
357           self->RecvOnConsoleReport(std::move(consoleReports));
358         });
359 
360     mQueuedRunnables.AppendElement(task.forget());
361     return IPC_OK();
362   }
363 
364   if (mOnStopRequestCalled) {
365     mChannelChild->ProcessOnConsoleReport(std::move(aConsoleReports));
366   } else {
367     RefPtr<HttpBackgroundChannelChild> self = this;
368     mConsoleReportTask = [self, consoleReports = CopyableTArray{
369                                     std::move(aConsoleReports)}]() mutable {
370       self->mChannelChild->ProcessOnConsoleReport(std::move(consoleReports));
371     };
372   }
373 
374   return IPC_OK();
375 }
376 
RecvNotifyClassificationFlags(const uint32_t & aClassificationFlags,const bool & aIsThirdParty)377 IPCResult HttpBackgroundChannelChild::RecvNotifyClassificationFlags(
378     const uint32_t& aClassificationFlags, const bool& aIsThirdParty) {
379   LOG(
380       ("HttpBackgroundChannelChild::RecvNotifyClassificationFlags "
381        "classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
382        aClassificationFlags, static_cast<int>(aIsThirdParty), this));
383   MOZ_ASSERT(OnSocketThread());
384 
385   if (NS_WARN_IF(!mChannelChild)) {
386     return IPC_OK();
387   }
388 
389   // NotifyClassificationFlags has no order dependency to OnStartRequest.
390   // It this be handled as soon as possible
391   mChannelChild->ProcessNotifyClassificationFlags(aClassificationFlags,
392                                                   aIsThirdParty);
393 
394   return IPC_OK();
395 }
396 
RecvNotifyFlashPluginStateChanged(const nsIHttpChannel::FlashPluginState & aState)397 IPCResult HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged(
398     const nsIHttpChannel::FlashPluginState& aState) {
399   LOG(
400       ("HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged "
401        "[this=%p]\n",
402        this));
403   MOZ_ASSERT(OnSocketThread());
404 
405   if (NS_WARN_IF(!mChannelChild)) {
406     return IPC_OK();
407   }
408 
409   // NotifyFlashPluginStateChanged has no order dependency to OnStartRequest.
410   // It this be handled as soon as possible
411   mChannelChild->ProcessNotifyFlashPluginStateChanged(aState);
412 
413   return IPC_OK();
414 }
415 
RecvSetClassifierMatchedInfo(const ClassifierInfo & info)416 IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
417     const ClassifierInfo& info) {
418   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
419        this));
420   MOZ_ASSERT(OnSocketThread());
421 
422   if (NS_WARN_IF(!mChannelChild)) {
423     return IPC_OK();
424   }
425 
426   // SetClassifierMatchedInfo has no order dependency to OnStartRequest.
427   // It this be handled as soon as possible
428   mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(),
429                                                  info.fullhash());
430 
431   return IPC_OK();
432 }
433 
RecvSetClassifierMatchedTrackingInfo(const ClassifierInfo & info)434 IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedTrackingInfo(
435     const ClassifierInfo& info) {
436   LOG(
437       ("HttpBackgroundChannelChild::RecvSetClassifierMatchedTrackingInfo "
438        "[this=%p]\n",
439        this));
440   MOZ_ASSERT(OnSocketThread());
441 
442   if (NS_WARN_IF(!mChannelChild)) {
443     return IPC_OK();
444   }
445 
446   // SetClassifierMatchedTrackingInfo has no order dependency to
447   // OnStartRequest. It this be handled as soon as possible
448   mChannelChild->ProcessSetClassifierMatchedTrackingInfo(info.list(),
449                                                          info.fullhash());
450 
451   return IPC_OK();
452 }
453 
RecvAttachStreamFilter(Endpoint<extensions::PStreamFilterParent> && aEndpoint)454 IPCResult HttpBackgroundChannelChild::RecvAttachStreamFilter(
455     Endpoint<extensions::PStreamFilterParent>&& aEndpoint) {
456   LOG(("HttpBackgroundChannelChild::RecvAttachStreamFilter [this=%p]\n", this));
457   MOZ_ASSERT(OnSocketThread());
458 
459   if (NS_WARN_IF(!mChannelChild)) {
460     return IPC_OK();
461   }
462 
463   mChannelChild->ProcessAttachStreamFilter(std::move(aEndpoint));
464   return IPC_OK();
465 }
466 
ActorDestroy(ActorDestroyReason aWhy)467 void HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
468   LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
469   // This function might be called during shutdown phase, so OnSocketThread()
470   // might return false even on STS thread. Use IsOnCurrentThreadInfallible()
471   // to get correct information.
472   MOZ_ASSERT(gSocketTransportService);
473   MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
474 
475   // Ensure all IPC messages received before ActorDestroy can be
476   // handled correctly. If there is any pending IPC message, destroyed
477   // mChannelChild until those messages are flushed.
478   // If background channel is not closed by normal IPDL actor deletion,
479   // remove the HttpChannelChild reference and notify background channel
480   // destroyed immediately.
481   if (aWhy == Deletion && !mQueuedRunnables.IsEmpty()) {
482     LOG(("  > pending until queued messages are flushed\n"));
483     RefPtr<HttpBackgroundChannelChild> self = this;
484     mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
485         "HttpBackgroundChannelChild::ActorDestroy", [self]() {
486           MOZ_ASSERT(OnSocketThread());
487           RefPtr<HttpChannelChild> channelChild =
488               std::move(self->mChannelChild);
489 
490           if (channelChild) {
491             channelChild->OnBackgroundChildDestroyed(self);
492           }
493         }));
494     return;
495   }
496 
497   if (mChannelChild) {
498     RefPtr<HttpChannelChild> channelChild = std::move(mChannelChild);
499 
500     channelChild->OnBackgroundChildDestroyed(this);
501   }
502 }
503 
504 }  // namespace net
505 }  // namespace mozilla
506