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