/src/mozilla-central/netwerk/base/RequestContextService.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsAutoPtr.h" |
8 | | #include "nsIDocShell.h" |
9 | | #include "nsIDocument.h" |
10 | | #include "nsIDocumentLoader.h" |
11 | | #include "nsIObserverService.h" |
12 | | #include "nsIXULRuntime.h" |
13 | | #include "nsServiceManagerUtils.h" |
14 | | #include "nsThreadUtils.h" |
15 | | #include "RequestContextService.h" |
16 | | |
17 | | #include "mozilla/Atomics.h" |
18 | | #include "mozilla/Logging.h" |
19 | | #include "mozilla/Services.h" |
20 | | #include "mozilla/TimeStamp.h" |
21 | | |
22 | | #include "mozilla/net/NeckoChild.h" |
23 | | #include "mozilla/net/NeckoCommon.h" |
24 | | #include "mozilla/net/PSpdyPush.h" |
25 | | |
26 | | #include "../protocol/http/nsHttpHandler.h" |
27 | | |
28 | | namespace mozilla { |
29 | | namespace net { |
30 | | |
31 | | LazyLogModule gRequestContextLog("RequestContext"); |
32 | | #undef LOG |
33 | 3 | #define LOG(args) MOZ_LOG(gRequestContextLog, LogLevel::Info, args) |
34 | | |
35 | | // This is used to prevent adding tail pending requests after shutdown |
36 | | static bool sShutdown = false; |
37 | | |
38 | | // nsIRequestContext |
39 | | class RequestContext final : public nsIRequestContext |
40 | | , public nsITimerCallback |
41 | | { |
42 | | public: |
43 | | NS_DECL_THREADSAFE_ISUPPORTS |
44 | | NS_DECL_NSIREQUESTCONTEXT |
45 | | NS_DECL_NSITIMERCALLBACK |
46 | | |
47 | | explicit RequestContext(const uint64_t id); |
48 | | private: |
49 | | virtual ~RequestContext(); |
50 | | |
51 | | void ProcessTailQueue(nsresult aResult); |
52 | | // Reschedules the timer if needed |
53 | | void ScheduleUnblock(); |
54 | | // Hard-reschedules the timer |
55 | | void RescheduleUntailTimer(TimeStamp const& now); |
56 | | |
57 | | uint64_t mID; |
58 | | Atomic<uint32_t> mBlockingTransactionCount; |
59 | | nsAutoPtr<SpdyPushCache> mSpdyCache; |
60 | | nsCString mUserAgentOverride; |
61 | | |
62 | | typedef nsCOMPtr<nsIRequestTailUnblockCallback> PendingTailRequest; |
63 | | // Number of known opened non-tailed requets |
64 | | uint32_t mNonTailRequests; |
65 | | // Queue of requests that have been tailed, when conditions are met |
66 | | // we call each of them to unblock and drop the reference |
67 | | nsTArray<PendingTailRequest> mTailQueue; |
68 | | // Loosly scheduled timer, never scheduled further to the future than |
69 | | // mUntailAt time |
70 | | nsCOMPtr<nsITimer> mUntailTimer; |
71 | | // Timestamp when the timer is expected to fire, |
72 | | // always less than or equal to mUntailAt |
73 | | TimeStamp mTimerScheduledAt; |
74 | | // Timestamp when we want to actually untail queued requets based on |
75 | | // the number of request count change in the past; iff this timestamp |
76 | | // is set, we tail requests |
77 | | TimeStamp mUntailAt; |
78 | | |
79 | | // Timestamp of the navigation start time, set to Now() in BeginLoad(). |
80 | | // This is used to progressively lower the maximum delay time so that |
81 | | // we can't get to a situation when a number of repetitive requests |
82 | | // on the page causes forever tailing. |
83 | | TimeStamp mBeginLoadTime; |
84 | | |
85 | | // This member is true only between DOMContentLoaded notification and |
86 | | // next document load beginning for this request context. |
87 | | // Top level request contexts are recycled. |
88 | | bool mAfterDOMContentLoaded; |
89 | | }; |
90 | | |
91 | | NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext, nsITimerCallback) |
92 | | |
93 | | RequestContext::RequestContext(const uint64_t aID) |
94 | | : mID(aID) |
95 | | , mBlockingTransactionCount(0) |
96 | | , mNonTailRequests(0) |
97 | | , mAfterDOMContentLoaded(false) |
98 | 3 | { |
99 | 3 | LOG(("RequestContext::RequestContext this=%p id=%" PRIx64, this, mID)); |
100 | 3 | } |
101 | | |
102 | | RequestContext::~RequestContext() |
103 | 0 | { |
104 | 0 | MOZ_ASSERT(mTailQueue.Length() == 0); |
105 | 0 |
|
106 | 0 | LOG(("RequestContext::~RequestContext this=%p blockers=%u", |
107 | 0 | this, static_cast<uint32_t>(mBlockingTransactionCount))); |
108 | 0 | } |
109 | | |
110 | | NS_IMETHODIMP |
111 | | RequestContext::BeginLoad() |
112 | 0 | { |
113 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
114 | 0 |
|
115 | 0 | LOG(("RequestContext::BeginLoad %p", this)); |
116 | 0 |
|
117 | 0 | if (IsNeckoChild()) { |
118 | 0 | // Tailing is not supported on the child process |
119 | 0 | if (gNeckoChild) { |
120 | 0 | gNeckoChild->SendRequestContextLoadBegin(mID); |
121 | 0 | } |
122 | 0 | return NS_OK; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | mAfterDOMContentLoaded = false; |
126 | 0 | mBeginLoadTime = TimeStamp::NowLoRes(); |
127 | 0 | return NS_OK; |
128 | 0 | } |
129 | | |
130 | | NS_IMETHODIMP |
131 | | RequestContext::DOMContentLoaded() |
132 | 0 | { |
133 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
134 | 0 |
|
135 | 0 | LOG(("RequestContext::DOMContentLoaded %p", this)); |
136 | 0 |
|
137 | 0 | if (IsNeckoChild()) { |
138 | 0 | // Tailing is not supported on the child process |
139 | 0 | if (gNeckoChild) { |
140 | 0 | gNeckoChild->SendRequestContextAfterDOMContentLoaded(mID); |
141 | 0 | } |
142 | 0 | return NS_OK; |
143 | 0 | } |
144 | 0 |
|
145 | 0 | if (mAfterDOMContentLoaded) { |
146 | 0 | // There is a possibility of a duplicate notification |
147 | 0 | return NS_OK; |
148 | 0 | } |
149 | 0 | |
150 | 0 | mAfterDOMContentLoaded = true; |
151 | 0 |
|
152 | 0 | // Conditions for the delay calculation has changed. |
153 | 0 | ScheduleUnblock(); |
154 | 0 | return NS_OK; |
155 | 0 | } |
156 | | |
157 | | NS_IMETHODIMP |
158 | | RequestContext::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount) |
159 | 0 | { |
160 | 0 | NS_ENSURE_ARG_POINTER(aBlockingTransactionCount); |
161 | 0 | *aBlockingTransactionCount = mBlockingTransactionCount; |
162 | 0 | return NS_OK; |
163 | 0 | } |
164 | | |
165 | | NS_IMETHODIMP |
166 | | RequestContext::AddBlockingTransaction() |
167 | 0 | { |
168 | 0 | mBlockingTransactionCount++; |
169 | 0 | LOG(("RequestContext::AddBlockingTransaction this=%p blockers=%u", |
170 | 0 | this, static_cast<uint32_t>(mBlockingTransactionCount))); |
171 | 0 | return NS_OK; |
172 | 0 | } |
173 | | |
174 | | NS_IMETHODIMP |
175 | | RequestContext::RemoveBlockingTransaction(uint32_t *outval) |
176 | 0 | { |
177 | 0 | NS_ENSURE_ARG_POINTER(outval); |
178 | 0 | mBlockingTransactionCount--; |
179 | 0 | LOG(("RequestContext::RemoveBlockingTransaction this=%p blockers=%u", |
180 | 0 | this, static_cast<uint32_t>(mBlockingTransactionCount))); |
181 | 0 | *outval = mBlockingTransactionCount; |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | | |
185 | | NS_IMETHODIMP |
186 | | RequestContext::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache) |
187 | 0 | { |
188 | 0 | *aSpdyPushCache = mSpdyCache.get(); |
189 | 0 | return NS_OK; |
190 | 0 | } |
191 | | |
192 | | NS_IMETHODIMP |
193 | | RequestContext::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache) |
194 | 0 | { |
195 | 0 | mSpdyCache = aSpdyPushCache; |
196 | 0 | return NS_OK; |
197 | 0 | } |
198 | | |
199 | | NS_IMETHODIMP |
200 | | RequestContext::GetID(uint64_t *outval) |
201 | 0 | { |
202 | 0 | NS_ENSURE_ARG_POINTER(outval); |
203 | 0 | *outval = mID; |
204 | 0 | return NS_OK; |
205 | 0 | } |
206 | | |
207 | | NS_IMETHODIMP |
208 | | RequestContext::GetUserAgentOverride(nsACString& aUserAgentOverride) |
209 | 0 | { |
210 | 0 | aUserAgentOverride = mUserAgentOverride; |
211 | 0 | return NS_OK; |
212 | 0 | } |
213 | | |
214 | | NS_IMETHODIMP |
215 | | RequestContext::SetUserAgentOverride(const nsACString& aUserAgentOverride) |
216 | 0 | { |
217 | 0 | mUserAgentOverride = aUserAgentOverride; |
218 | 0 | return NS_OK; |
219 | 0 | } |
220 | | |
221 | | NS_IMETHODIMP |
222 | | RequestContext::AddNonTailRequest() |
223 | 0 | { |
224 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
225 | 0 |
|
226 | 0 | ++mNonTailRequests; |
227 | 0 | LOG(("RequestContext::AddNonTailRequest this=%p, cnt=%u", |
228 | 0 | this, mNonTailRequests)); |
229 | 0 |
|
230 | 0 | ScheduleUnblock(); |
231 | 0 | return NS_OK; |
232 | 0 | } |
233 | | |
234 | | NS_IMETHODIMP |
235 | | RequestContext::RemoveNonTailRequest() |
236 | 0 | { |
237 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
238 | 0 | MOZ_ASSERT(mNonTailRequests > 0); |
239 | 0 |
|
240 | 0 | LOG(("RequestContext::RemoveNonTailRequest this=%p, cnt=%u", |
241 | 0 | this, mNonTailRequests - 1)); |
242 | 0 |
|
243 | 0 | --mNonTailRequests; |
244 | 0 |
|
245 | 0 | ScheduleUnblock(); |
246 | 0 | return NS_OK; |
247 | 0 | } |
248 | | |
249 | | void |
250 | | RequestContext::ScheduleUnblock() |
251 | 0 | { |
252 | 0 | MOZ_ASSERT(!IsNeckoChild()); |
253 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
254 | 0 |
|
255 | 0 | if (!gHttpHandler) { |
256 | 0 | return; |
257 | 0 | } |
258 | 0 | |
259 | 0 | uint32_t quantum = gHttpHandler->TailBlockingDelayQuantum(mAfterDOMContentLoaded); |
260 | 0 | uint32_t delayMax = gHttpHandler->TailBlockingDelayMax(); |
261 | 0 | uint32_t totalMax = gHttpHandler->TailBlockingTotalMax(); |
262 | 0 |
|
263 | 0 | if (!mBeginLoadTime.IsNull()) { |
264 | 0 | // We decrease the maximum delay progressively with the time since the page load |
265 | 0 | // begin. This seems like a reasonable and clear heuristic allowing us to start |
266 | 0 | // loading tailed requests in a deterministic time after the load has started. |
267 | 0 |
|
268 | 0 | uint32_t sinceBeginLoad = static_cast<uint32_t>( |
269 | 0 | (TimeStamp::NowLoRes() - mBeginLoadTime).ToMilliseconds()); |
270 | 0 | uint32_t tillTotal = totalMax - std::min(sinceBeginLoad, totalMax); |
271 | 0 | uint32_t proportion = totalMax // values clamped between 0 and 60'000 |
272 | 0 | ? (delayMax * tillTotal) / totalMax |
273 | 0 | : 0; |
274 | 0 | delayMax = std::min(delayMax, proportion); |
275 | 0 | } |
276 | 0 |
|
277 | 0 | CheckedInt<uint32_t> delay = quantum * mNonTailRequests; |
278 | 0 |
|
279 | 0 | if (!mAfterDOMContentLoaded) { |
280 | 0 | // Before DOMContentLoaded notification we want to make sure that tailed |
281 | 0 | // requests don't start when there is a short delay during which we may |
282 | 0 | // not have any active requests on the page happening. |
283 | 0 | delay += quantum; |
284 | 0 | } |
285 | 0 |
|
286 | 0 | if (!delay.isValid() || delay.value() > delayMax) { |
287 | 0 | delay = delayMax; |
288 | 0 | } |
289 | 0 |
|
290 | 0 | LOG(("RequestContext::ScheduleUnblock this=%p non-tails=%u tail-queue=%zu delay=%u after-DCL=%d", |
291 | 0 | this, mNonTailRequests, mTailQueue.Length(), delay.value(), mAfterDOMContentLoaded)); |
292 | 0 |
|
293 | 0 | TimeStamp now = TimeStamp::NowLoRes(); |
294 | 0 | mUntailAt = now + TimeDuration::FromMilliseconds(delay.value()); |
295 | 0 |
|
296 | 0 | if (mTimerScheduledAt.IsNull() || mUntailAt < mTimerScheduledAt) { |
297 | 0 | LOG(("RequestContext %p timer would fire too late, rescheduling", this)); |
298 | 0 | RescheduleUntailTimer(now); |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | void |
303 | | RequestContext::RescheduleUntailTimer(TimeStamp const& now) |
304 | 0 | { |
305 | 0 | MOZ_ASSERT(mUntailAt >= now); |
306 | 0 |
|
307 | 0 | if (mUntailTimer) { |
308 | 0 | mUntailTimer->Cancel(); |
309 | 0 | } |
310 | 0 |
|
311 | 0 | if (!mTailQueue.Length()) { |
312 | 0 | mUntailTimer = nullptr; |
313 | 0 | mTimerScheduledAt = TimeStamp(); |
314 | 0 | return; |
315 | 0 | } |
316 | 0 | |
317 | 0 | TimeDuration interval = mUntailAt - now; |
318 | 0 | if (!mTimerScheduledAt.IsNull() && mUntailAt < mTimerScheduledAt) { |
319 | 0 | // When the number of untailed requests goes down, |
320 | 0 | // let's half the interval, since it's likely we would |
321 | 0 | // reschedule for a shorter time again very soon. |
322 | 0 | // This will likely save rescheduling this timer. |
323 | 0 | interval = interval / int64_t(2); |
324 | 0 | mTimerScheduledAt = mUntailAt - interval; |
325 | 0 | } else { |
326 | 0 | mTimerScheduledAt = mUntailAt; |
327 | 0 | } |
328 | 0 |
|
329 | 0 | uint32_t delay = interval.ToMilliseconds(); |
330 | 0 | mUntailTimer = do_CreateInstance("@mozilla.org/timer;1"); |
331 | 0 | mUntailTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT); |
332 | 0 |
|
333 | 0 | LOG(("RequestContext::RescheduleUntailTimer %p in %d", this, delay)); |
334 | 0 | } |
335 | | |
336 | | NS_IMETHODIMP |
337 | | RequestContext::Notify(nsITimer *timer) |
338 | 0 | { |
339 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
340 | 0 | MOZ_ASSERT(timer == mUntailTimer); |
341 | 0 | MOZ_ASSERT(!mTimerScheduledAt.IsNull()); |
342 | 0 | MOZ_ASSERT(mTailQueue.Length()); |
343 | 0 |
|
344 | 0 | mUntailTimer = nullptr; |
345 | 0 |
|
346 | 0 | TimeStamp now = TimeStamp::NowLoRes(); |
347 | 0 | if (mUntailAt > mTimerScheduledAt && mUntailAt > now) { |
348 | 0 | LOG(("RequestContext %p timer fired too soon, rescheduling", this)); |
349 | 0 | RescheduleUntailTimer(now); |
350 | 0 | return NS_OK; |
351 | 0 | } |
352 | 0 |
|
353 | 0 | // Must drop to allow re-engage of the timer |
354 | 0 | mTimerScheduledAt = TimeStamp(); |
355 | 0 |
|
356 | 0 | ProcessTailQueue(NS_OK); |
357 | 0 |
|
358 | 0 | return NS_OK; |
359 | 0 | } |
360 | | |
361 | | NS_IMETHODIMP |
362 | | RequestContext::IsContextTailBlocked(nsIRequestTailUnblockCallback * aRequest, |
363 | | bool *aBlocked) |
364 | 0 | { |
365 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
366 | 0 |
|
367 | 0 | LOG(("RequestContext::IsContextTailBlocked this=%p, request=%p, queued=%zu", |
368 | 0 | this, aRequest, mTailQueue.Length())); |
369 | 0 |
|
370 | 0 | *aBlocked = false; |
371 | 0 |
|
372 | 0 | if (sShutdown) { |
373 | 0 | return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; |
374 | 0 | } |
375 | 0 | |
376 | 0 | if (mUntailAt.IsNull()) { |
377 | 0 | LOG((" untail time passed")); |
378 | 0 | return NS_OK; |
379 | 0 | } |
380 | 0 |
|
381 | 0 | if (mAfterDOMContentLoaded && !mNonTailRequests) { |
382 | 0 | LOG((" after DOMContentLoaded and no untailed requests")); |
383 | 0 | return NS_OK; |
384 | 0 | } |
385 | 0 |
|
386 | 0 | if (!gHttpHandler) { |
387 | 0 | // Xpcshell tests may not have http handler |
388 | 0 | LOG((" missing gHttpHandler?")); |
389 | 0 | return NS_OK; |
390 | 0 | } |
391 | 0 |
|
392 | 0 | *aBlocked = true; |
393 | 0 | mTailQueue.AppendElement(aRequest); |
394 | 0 |
|
395 | 0 | LOG((" request queued")); |
396 | 0 |
|
397 | 0 | if (!mUntailTimer) { |
398 | 0 | ScheduleUnblock(); |
399 | 0 | } |
400 | 0 |
|
401 | 0 | return NS_OK; |
402 | 0 | } |
403 | | |
404 | | NS_IMETHODIMP |
405 | | RequestContext::CancelTailedRequest(nsIRequestTailUnblockCallback * aRequest) |
406 | 0 | { |
407 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
408 | 0 |
|
409 | 0 | bool removed = mTailQueue.RemoveElement(aRequest); |
410 | 0 |
|
411 | 0 | LOG(("RequestContext::CancelTailedRequest %p req=%p removed=%d", |
412 | 0 | this, aRequest, removed)); |
413 | 0 |
|
414 | 0 | // Stop untail timer if all tail requests are canceled. |
415 | 0 | if (removed && mTailQueue.IsEmpty()) { |
416 | 0 | if (mUntailTimer) { |
417 | 0 | mUntailTimer->Cancel(); |
418 | 0 | mUntailTimer = nullptr; |
419 | 0 | } |
420 | 0 |
|
421 | 0 | // Must drop to allow re-engage of the timer |
422 | 0 | mTimerScheduledAt = TimeStamp(); |
423 | 0 | } |
424 | 0 |
|
425 | 0 | return NS_OK; |
426 | 0 | } |
427 | | |
428 | | void |
429 | | RequestContext::ProcessTailQueue(nsresult aResult) |
430 | 0 | { |
431 | 0 | LOG(("RequestContext::ProcessTailQueue this=%p, queued=%zu, rv=%" PRIx32, |
432 | 0 | this, mTailQueue.Length(), static_cast<uint32_t>(aResult))); |
433 | 0 |
|
434 | 0 | if (mUntailTimer) { |
435 | 0 | mUntailTimer->Cancel(); |
436 | 0 | mUntailTimer = nullptr; |
437 | 0 | } |
438 | 0 |
|
439 | 0 | // Must drop to stop tailing requests |
440 | 0 | mUntailAt = TimeStamp(); |
441 | 0 |
|
442 | 0 | nsTArray<PendingTailRequest> queue; |
443 | 0 | queue.SwapElements(mTailQueue); |
444 | 0 |
|
445 | 0 | for (const auto& request : queue) { |
446 | 0 | LOG((" untailing %p", request.get())); |
447 | 0 | request->OnTailUnblock(aResult); |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | | NS_IMETHODIMP |
452 | | RequestContext::CancelTailPendingRequests(nsresult aResult) |
453 | 0 | { |
454 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
455 | 0 | MOZ_ASSERT(NS_FAILED(aResult)); |
456 | 0 |
|
457 | 0 | ProcessTailQueue(aResult); |
458 | 0 | return NS_OK; |
459 | 0 | } |
460 | | |
461 | | //nsIRequestContextService |
462 | | RequestContextService *RequestContextService::sSelf = nullptr; |
463 | | |
464 | | NS_IMPL_ISUPPORTS(RequestContextService, nsIRequestContextService, nsIObserver) |
465 | | |
466 | | RequestContextService::RequestContextService() |
467 | | : mRCIDNamespace(0) |
468 | | , mNextRCID(1) |
469 | 3 | { |
470 | 3 | MOZ_ASSERT(!sSelf, "multiple rcs instances!"); |
471 | 3 | MOZ_ASSERT(NS_IsMainThread()); |
472 | 3 | sSelf = this; |
473 | 3 | |
474 | 3 | nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1"); |
475 | 3 | runtime->GetProcessID(&mRCIDNamespace); |
476 | 3 | } |
477 | | |
478 | | RequestContextService::~RequestContextService() |
479 | 0 | { |
480 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
481 | 0 | Shutdown(); |
482 | 0 | sSelf = nullptr; |
483 | 0 | } |
484 | | |
485 | | nsresult |
486 | | RequestContextService::Init() |
487 | 3 | { |
488 | 3 | nsresult rv; |
489 | 3 | |
490 | 3 | MOZ_ASSERT(NS_IsMainThread()); |
491 | 3 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
492 | 3 | if (!obs) { |
493 | 0 | return NS_ERROR_NOT_AVAILABLE; |
494 | 0 | } |
495 | 3 | |
496 | 3 | rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); |
497 | 3 | if (NS_FAILED(rv)) { |
498 | 0 | return rv; |
499 | 0 | } |
500 | 3 | obs->AddObserver(this, "content-document-interactive", false); |
501 | 3 | if (NS_FAILED(rv)) { |
502 | 0 | return rv; |
503 | 0 | } |
504 | 3 | |
505 | 3 | return NS_OK; |
506 | 3 | } |
507 | | |
508 | | void |
509 | | RequestContextService::Shutdown() |
510 | 0 | { |
511 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
512 | 0 | // We need to do this to prevent the requests from being scheduled after |
513 | 0 | // shutdown. |
514 | 0 | for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) { |
515 | 0 | iter.Data()->CancelTailPendingRequests(NS_ERROR_ABORT); |
516 | 0 | } |
517 | 0 | mTable.Clear(); |
518 | 0 | sShutdown = true; |
519 | 0 | } |
520 | | |
521 | | /* static */ nsresult |
522 | | RequestContextService::Create(nsISupports *aOuter, const nsIID& aIID, void **aResult) |
523 | 0 | { |
524 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
525 | 0 | if (aOuter != nullptr) { |
526 | 0 | return NS_ERROR_NO_AGGREGATION; |
527 | 0 | } |
528 | 0 | |
529 | 0 | RefPtr<RequestContextService> svc = new RequestContextService(); |
530 | 0 | nsresult rv = svc->Init(); |
531 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
532 | 0 |
|
533 | 0 | return svc->QueryInterface(aIID, aResult); |
534 | 0 | } |
535 | | |
536 | | NS_IMETHODIMP |
537 | | RequestContextService::GetRequestContext(const uint64_t rcID, nsIRequestContext **rc) |
538 | 0 | { |
539 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
540 | 0 | NS_ENSURE_ARG_POINTER(rc); |
541 | 0 | *rc = nullptr; |
542 | 0 |
|
543 | 0 | if (sShutdown) { |
544 | 0 | return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; |
545 | 0 | } |
546 | 0 | |
547 | 0 | if (!mTable.Get(rcID, rc)) { |
548 | 0 | nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID); |
549 | 0 | mTable.Put(rcID, newSC); |
550 | 0 | newSC.swap(*rc); |
551 | 0 | } |
552 | 0 |
|
553 | 0 | return NS_OK; |
554 | 0 | } |
555 | | |
556 | | NS_IMETHODIMP |
557 | | RequestContextService::GetRequestContextFromLoadGroup(nsILoadGroup *aLoadGroup, nsIRequestContext **rc) |
558 | 0 | { |
559 | 0 | nsresult rv; |
560 | 0 |
|
561 | 0 | uint64_t rcID; |
562 | 0 | rv = aLoadGroup->GetRequestContextID(&rcID); |
563 | 0 | if (NS_FAILED(rv)) { |
564 | 0 | return rv; |
565 | 0 | } |
566 | 0 | |
567 | 0 | return GetRequestContext(rcID, rc); |
568 | 0 | } |
569 | | |
570 | | NS_IMETHODIMP |
571 | | RequestContextService::NewRequestContext(nsIRequestContext **rc) |
572 | 3 | { |
573 | 3 | MOZ_ASSERT(NS_IsMainThread()); |
574 | 3 | NS_ENSURE_ARG_POINTER(rc); |
575 | 3 | *rc = nullptr; |
576 | 3 | |
577 | 3 | if (sShutdown) { |
578 | 0 | return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; |
579 | 0 | } |
580 | 3 | |
581 | 3 | uint64_t rcID = ((static_cast<uint64_t>(mRCIDNamespace) << 32) & 0xFFFFFFFF00000000LL) | mNextRCID++; |
582 | 3 | |
583 | 3 | nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID); |
584 | 3 | mTable.Put(rcID, newSC); |
585 | 3 | newSC.swap(*rc); |
586 | 3 | |
587 | 3 | return NS_OK; |
588 | 3 | } |
589 | | |
590 | | NS_IMETHODIMP |
591 | | RequestContextService::RemoveRequestContext(const uint64_t rcID) |
592 | 0 | { |
593 | 0 | if (IsNeckoChild() && gNeckoChild) { |
594 | 0 | gNeckoChild->SendRemoveRequestContext(rcID); |
595 | 0 | } |
596 | 0 |
|
597 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
598 | 0 | mTable.Remove(rcID); |
599 | 0 | return NS_OK; |
600 | 0 | } |
601 | | |
602 | | NS_IMETHODIMP |
603 | | RequestContextService::Observe(nsISupports *subject, const char *topic, |
604 | | const char16_t *data_unicode) |
605 | 0 | { |
606 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
607 | 0 | if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) { |
608 | 0 | Shutdown(); |
609 | 0 | return NS_OK; |
610 | 0 | } |
611 | 0 | |
612 | 0 | if (!strcmp("content-document-interactive", topic)) { |
613 | 0 | nsCOMPtr<nsIDocument> document(do_QueryInterface(subject)); |
614 | 0 | MOZ_ASSERT(document); |
615 | 0 | // We want this be triggered also for iframes, since those track their |
616 | 0 | // own request context ids. |
617 | 0 | if (!document) { |
618 | 0 | return NS_OK; |
619 | 0 | } |
620 | 0 | nsIDocShell* ds = document->GetDocShell(); |
621 | 0 | // XML documents don't always have a docshell assigned |
622 | 0 | if (!ds) { |
623 | 0 | return NS_OK; |
624 | 0 | } |
625 | 0 | nsCOMPtr<nsIDocumentLoader> dl(do_QueryInterface(ds)); |
626 | 0 | if (!dl) { |
627 | 0 | return NS_OK; |
628 | 0 | } |
629 | 0 | nsCOMPtr<nsILoadGroup> lg; |
630 | 0 | dl->GetLoadGroup(getter_AddRefs(lg)); |
631 | 0 | if (!lg) { |
632 | 0 | return NS_OK; |
633 | 0 | } |
634 | 0 | nsCOMPtr<nsIRequestContext> rc; |
635 | 0 | GetRequestContextFromLoadGroup(lg, getter_AddRefs(rc)); |
636 | 0 | if (rc) { |
637 | 0 | rc->DOMContentLoaded(); |
638 | 0 | } |
639 | 0 |
|
640 | 0 | return NS_OK; |
641 | 0 | } |
642 | 0 |
|
643 | 0 | MOZ_ASSERT(false, "Unexpected observer topic"); |
644 | 0 | return NS_OK; |
645 | 0 | } |
646 | | |
647 | | } // ::mozilla::net |
648 | | } // ::mozilla |
649 | | |
650 | | #undef LOG |