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