/src/mozilla-central/uriloader/prefetch/nsPrefetchService.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "nsPrefetchService.h" |
7 | | |
8 | | #include "mozilla/AsyncEventDispatcher.h" |
9 | | #include "mozilla/Attributes.h" |
10 | | #include "mozilla/CORSMode.h" |
11 | | #include "mozilla/dom/ClientInfo.h" |
12 | | #include "mozilla/dom/HTMLLinkElement.h" |
13 | | #include "mozilla/dom/ServiceWorkerDescriptor.h" |
14 | | #include "mozilla/Preferences.h" |
15 | | |
16 | | #include "nsICacheEntry.h" |
17 | | #include "nsIServiceManager.h" |
18 | | #include "nsICategoryManager.h" |
19 | | #include "nsIObserverService.h" |
20 | | #include "nsIWebProgress.h" |
21 | | #include "nsCURILoader.h" |
22 | | #include "nsICacheInfoChannel.h" |
23 | | #include "nsIHttpChannel.h" |
24 | | #include "nsIURL.h" |
25 | | #include "nsISimpleEnumerator.h" |
26 | | #include "nsISupportsPriority.h" |
27 | | #include "nsNetUtil.h" |
28 | | #include "nsString.h" |
29 | | #include "nsReadableUtils.h" |
30 | | #include "nsStreamUtils.h" |
31 | | #include "nsAutoPtr.h" |
32 | | #include "prtime.h" |
33 | | #include "mozilla/Logging.h" |
34 | | #include "plstr.h" |
35 | | #include "nsIAsyncVerifyRedirectCallback.h" |
36 | | #include "nsINode.h" |
37 | | #include "nsIDocument.h" |
38 | | #include "nsContentUtils.h" |
39 | | #include "nsStyleLinkElement.h" |
40 | | #include "mozilla/AsyncEventDispatcher.h" |
41 | | |
42 | | using namespace mozilla; |
43 | | |
44 | | // |
45 | | // To enable logging (see mozilla/Logging.h for full details): |
46 | | // |
47 | | // set MOZ_LOG=nsPrefetch:5 |
48 | | // set MOZ_LOG_FILE=prefetch.log |
49 | | // |
50 | | // this enables LogLevel::Debug level information and places all output in |
51 | | // the file prefetch.log |
52 | | // |
53 | | static LazyLogModule gPrefetchLog("nsPrefetch"); |
54 | | |
55 | | #undef LOG |
56 | 0 | #define LOG(args) MOZ_LOG(gPrefetchLog, mozilla::LogLevel::Debug, args) |
57 | | |
58 | | #undef LOG_ENABLED |
59 | 0 | #define LOG_ENABLED() MOZ_LOG_TEST(gPrefetchLog, mozilla::LogLevel::Debug) |
60 | | |
61 | 0 | #define PREFETCH_PREF "network.prefetch-next" |
62 | 0 | #define PRELOAD_PREF "network.preload" |
63 | 0 | #define PARALLELISM_PREF "network.prefetch-next.parallelism" |
64 | 0 | #define AGGRESSIVE_PREF "network.prefetch-next.aggressive" |
65 | | |
66 | | //----------------------------------------------------------------------------- |
67 | | // helpers |
68 | | //----------------------------------------------------------------------------- |
69 | | |
70 | | static inline uint32_t |
71 | | PRTimeToSeconds(PRTime t_usec) |
72 | 0 | { |
73 | 0 | PRTime usec_per_sec = PR_USEC_PER_SEC; |
74 | 0 | return uint32_t(t_usec /= usec_per_sec); |
75 | 0 | } |
76 | | |
77 | 0 | #define NowInSeconds() PRTimeToSeconds(PR_Now()) |
78 | | |
79 | | //----------------------------------------------------------------------------- |
80 | | // nsPrefetchNode <public> |
81 | | //----------------------------------------------------------------------------- |
82 | | |
83 | | nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService, |
84 | | nsIURI *aURI, |
85 | | nsIURI *aReferrerURI, |
86 | | nsINode *aSource, |
87 | | nsContentPolicyType aPolicyType, |
88 | | bool aPreload) |
89 | | : mURI(aURI) |
90 | | , mReferrerURI(aReferrerURI) |
91 | | , mPolicyType(aPolicyType) |
92 | | , mPreload(aPreload) |
93 | | , mService(aService) |
94 | | , mChannel(nullptr) |
95 | | , mBytesRead(0) |
96 | | , mShouldFireLoadEvent(false) |
97 | 0 | { |
98 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
99 | 0 | mSources.AppendElement(source); |
100 | 0 | } |
101 | | |
102 | | nsresult |
103 | | nsPrefetchNode::OpenChannel() |
104 | 0 | { |
105 | 0 | if (mSources.IsEmpty()) { |
106 | 0 | // Don't attempt to prefetch if we don't have a source node |
107 | 0 | // (which should never happen). |
108 | 0 | return NS_ERROR_FAILURE; |
109 | 0 | } |
110 | 0 | nsCOMPtr<nsINode> source; |
111 | 0 | while (!mSources.IsEmpty() && !(source = do_QueryReferent(mSources.ElementAt(0)))) { |
112 | 0 | // If source is null remove it. |
113 | 0 | // (which should never happen). |
114 | 0 | mSources.RemoveElementAt(0); |
115 | 0 | } |
116 | 0 |
|
117 | 0 | if (!source) { |
118 | 0 | // Don't attempt to prefetch if we don't have a source node |
119 | 0 | // (which should never happen). |
120 | 0 |
|
121 | 0 | return NS_ERROR_FAILURE; |
122 | 0 | } |
123 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup(); |
124 | 0 | CORSMode corsMode = CORS_NONE; |
125 | 0 | net::ReferrerPolicy referrerPolicy = net::RP_Unset; |
126 | 0 | if (auto* link = dom::HTMLLinkElement::FromNode(source)) { |
127 | 0 | corsMode = link->GetCORSMode(); |
128 | 0 | referrerPolicy = link->GetReferrerPolicyAsEnum(); |
129 | 0 | } |
130 | 0 |
|
131 | 0 | if (referrerPolicy == net::RP_Unset) { |
132 | 0 | referrerPolicy = source->OwnerDoc()->GetReferrerPolicy(); |
133 | 0 | } |
134 | 0 |
|
135 | 0 | uint32_t securityFlags; |
136 | 0 | if (corsMode == CORS_NONE) { |
137 | 0 | securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; |
138 | 0 | } else { |
139 | 0 | securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; |
140 | 0 | if (corsMode == CORS_USE_CREDENTIALS) { |
141 | 0 | securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | nsresult rv = NS_NewChannelInternal(getter_AddRefs(mChannel), |
145 | 0 | mURI, |
146 | 0 | source, |
147 | 0 | source->NodePrincipal(), |
148 | 0 | nullptr, //aTriggeringPrincipal |
149 | 0 | Maybe<ClientInfo>(), |
150 | 0 | Maybe<ServiceWorkerDescriptor>(), |
151 | 0 | securityFlags, |
152 | 0 | mPolicyType, |
153 | 0 | nullptr, // aPerformanceStorage |
154 | 0 | loadGroup, // aLoadGroup |
155 | 0 | this, // aCallbacks |
156 | 0 | nsIRequest::LOAD_BACKGROUND | |
157 | 0 | nsICachingChannel::LOAD_ONLY_IF_MODIFIED); |
158 | 0 |
|
159 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
160 | 0 |
|
161 | 0 | // configure HTTP specific stuff |
162 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = |
163 | 0 | do_QueryInterface(mChannel); |
164 | 0 | if (httpChannel) { |
165 | 0 | rv = httpChannel->SetReferrerWithPolicy(mReferrerURI, referrerPolicy); |
166 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
167 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), |
168 | 0 | NS_LITERAL_CSTRING("prefetch"), |
169 | 0 | false); |
170 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
171 | 0 | } |
172 | 0 |
|
173 | 0 | // Reduce the priority of prefetch network requests. |
174 | 0 | nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel); |
175 | 0 | if (priorityChannel) { |
176 | 0 | priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST); |
177 | 0 | } |
178 | 0 |
|
179 | 0 | rv = mChannel->AsyncOpen2(this); |
180 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
181 | 0 | // Drop the ref to the channel, because we don't want to end up with |
182 | 0 | // cycles through it. |
183 | 0 | mChannel = nullptr; |
184 | 0 | } |
185 | 0 | return rv; |
186 | 0 | } |
187 | | |
188 | | nsresult |
189 | | nsPrefetchNode::CancelChannel(nsresult error) |
190 | 0 | { |
191 | 0 | mChannel->Cancel(error); |
192 | 0 | mChannel = nullptr; |
193 | 0 |
|
194 | 0 | return NS_OK; |
195 | 0 | } |
196 | | |
197 | | //----------------------------------------------------------------------------- |
198 | | // nsPrefetchNode::nsISupports |
199 | | //----------------------------------------------------------------------------- |
200 | | |
201 | | NS_IMPL_ISUPPORTS(nsPrefetchNode, |
202 | | nsIRequestObserver, |
203 | | nsIStreamListener, |
204 | | nsIInterfaceRequestor, |
205 | | nsIChannelEventSink, |
206 | | nsIRedirectResultListener) |
207 | | |
208 | | //----------------------------------------------------------------------------- |
209 | | // nsPrefetchNode::nsIStreamListener |
210 | | //----------------------------------------------------------------------------- |
211 | | |
212 | | NS_IMETHODIMP |
213 | | nsPrefetchNode::OnStartRequest(nsIRequest *aRequest, |
214 | | nsISupports *aContext) |
215 | 0 | { |
216 | 0 | nsresult rv; |
217 | 0 |
|
218 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = |
219 | 0 | do_QueryInterface(aRequest, &rv); |
220 | 0 | if (NS_FAILED(rv)) return rv; |
221 | 0 | |
222 | 0 | // if the load is cross origin without CORS, or the CORS access is rejected, |
223 | 0 | // always fire load event to avoid leaking site information. |
224 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo(); |
225 | 0 | if (loadInfo) { |
226 | 0 | mShouldFireLoadEvent = loadInfo->GetTainting() == LoadTainting::Opaque || |
227 | 0 | (loadInfo->GetTainting() == LoadTainting::CORS && |
228 | 0 | (NS_FAILED(httpChannel->GetStatus(&rv)) || |
229 | 0 | NS_FAILED(rv))); |
230 | 0 | } |
231 | 0 |
|
232 | 0 | // no need to prefetch http error page |
233 | 0 | bool requestSucceeded; |
234 | 0 | if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) || |
235 | 0 | !requestSucceeded) { |
236 | 0 | return NS_BINDING_ABORTED; |
237 | 0 | } |
238 | 0 | |
239 | 0 | nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel = |
240 | 0 | do_QueryInterface(aRequest, &rv); |
241 | 0 | if (NS_FAILED(rv)) return rv; |
242 | 0 | |
243 | 0 | // no need to prefetch a document that is already in the cache |
244 | 0 | bool fromCache; |
245 | 0 | if (NS_SUCCEEDED(cacheInfoChannel->IsFromCache(&fromCache)) && |
246 | 0 | fromCache) { |
247 | 0 | LOG(("document is already in the cache; canceling prefetch\n")); |
248 | 0 | // although it's canceled we still want to fire load event |
249 | 0 | mShouldFireLoadEvent = true; |
250 | 0 | return NS_BINDING_ABORTED; |
251 | 0 | } |
252 | 0 |
|
253 | 0 | // |
254 | 0 | // no need to prefetch a document that must be requested fresh each |
255 | 0 | // and every time. |
256 | 0 | // |
257 | 0 | uint32_t expTime; |
258 | 0 | if (NS_SUCCEEDED(cacheInfoChannel->GetCacheTokenExpirationTime(&expTime))) { |
259 | 0 | if (NowInSeconds() >= expTime) { |
260 | 0 | LOG(("document cannot be reused from cache; " |
261 | 0 | "canceling prefetch\n")); |
262 | 0 | return NS_BINDING_ABORTED; |
263 | 0 | } |
264 | 0 | } |
265 | 0 |
|
266 | 0 | return NS_OK; |
267 | 0 | } |
268 | | |
269 | | NS_IMETHODIMP |
270 | | nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest, |
271 | | nsISupports *aContext, |
272 | | nsIInputStream *aStream, |
273 | | uint64_t aOffset, |
274 | | uint32_t aCount) |
275 | 0 | { |
276 | 0 | uint32_t bytesRead = 0; |
277 | 0 | aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead); |
278 | 0 | mBytesRead += bytesRead; |
279 | 0 | LOG(("prefetched %u bytes [offset=%" PRIu64 "]\n", bytesRead, aOffset)); |
280 | 0 | return NS_OK; |
281 | 0 | } |
282 | | |
283 | | |
284 | | NS_IMETHODIMP |
285 | | nsPrefetchNode::OnStopRequest(nsIRequest *aRequest, |
286 | | nsISupports *aContext, |
287 | | nsresult aStatus) |
288 | 0 | { |
289 | 0 | LOG(("done prefetching [status=%" PRIx32 "]\n", static_cast<uint32_t>(aStatus))); |
290 | 0 |
|
291 | 0 | if (mBytesRead == 0 && aStatus == NS_OK && mChannel) { |
292 | 0 | // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was |
293 | 0 | // specified), but the object should report loadedSize as if it |
294 | 0 | // did. |
295 | 0 | mChannel->GetContentLength(&mBytesRead); |
296 | 0 | } |
297 | 0 |
|
298 | 0 | mService->NotifyLoadCompleted(this); |
299 | 0 | mService->DispatchEvent(this, mShouldFireLoadEvent || NS_SUCCEEDED(aStatus)); |
300 | 0 | mService->RemoveNodeAndMaybeStartNextPrefetchURI(this); |
301 | 0 | return NS_OK; |
302 | 0 | } |
303 | | |
304 | | //----------------------------------------------------------------------------- |
305 | | // nsPrefetchNode::nsIInterfaceRequestor |
306 | | //----------------------------------------------------------------------------- |
307 | | |
308 | | NS_IMETHODIMP |
309 | | nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult) |
310 | 0 | { |
311 | 0 | if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { |
312 | 0 | NS_ADDREF_THIS(); |
313 | 0 | *aResult = static_cast<nsIChannelEventSink *>(this); |
314 | 0 | return NS_OK; |
315 | 0 | } |
316 | 0 |
|
317 | 0 | if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) { |
318 | 0 | NS_ADDREF_THIS(); |
319 | 0 | *aResult = static_cast<nsIRedirectResultListener *>(this); |
320 | 0 | return NS_OK; |
321 | 0 | } |
322 | 0 |
|
323 | 0 | return NS_ERROR_NO_INTERFACE; |
324 | 0 | } |
325 | | |
326 | | //----------------------------------------------------------------------------- |
327 | | // nsPrefetchNode::nsIChannelEventSink |
328 | | //----------------------------------------------------------------------------- |
329 | | |
330 | | NS_IMETHODIMP |
331 | | nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel, |
332 | | nsIChannel *aNewChannel, |
333 | | uint32_t aFlags, |
334 | | nsIAsyncVerifyRedirectCallback *callback) |
335 | 0 | { |
336 | 0 | nsCOMPtr<nsIURI> newURI; |
337 | 0 | nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI)); |
338 | 0 | if (NS_FAILED(rv)) |
339 | 0 | return rv; |
340 | 0 | |
341 | 0 | bool match; |
342 | 0 | rv = newURI->SchemeIs("http", &match); |
343 | 0 | if (NS_FAILED(rv) || !match) { |
344 | 0 | rv = newURI->SchemeIs("https", &match); |
345 | 0 | if (NS_FAILED(rv) || !match) { |
346 | 0 | LOG(("rejected: URL is not of type http/https\n")); |
347 | 0 | return NS_ERROR_ABORT; |
348 | 0 | } |
349 | 0 | } |
350 | 0 |
|
351 | 0 | // HTTP request headers are not automatically forwarded to the new channel. |
352 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel); |
353 | 0 | NS_ENSURE_STATE(httpChannel); |
354 | 0 |
|
355 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), |
356 | 0 | NS_LITERAL_CSTRING("prefetch"), |
357 | 0 | false); |
358 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
359 | 0 |
|
360 | 0 | // Assign to mChannel after we get notification about success of the |
361 | 0 | // redirect in OnRedirectResult. |
362 | 0 | mRedirectChannel = aNewChannel; |
363 | 0 |
|
364 | 0 | callback->OnRedirectVerifyCallback(NS_OK); |
365 | 0 | return NS_OK; |
366 | 0 | } |
367 | | |
368 | | //----------------------------------------------------------------------------- |
369 | | // nsPrefetchNode::nsIRedirectResultListener |
370 | | //----------------------------------------------------------------------------- |
371 | | |
372 | | NS_IMETHODIMP |
373 | | nsPrefetchNode::OnRedirectResult(bool proceeding) |
374 | 0 | { |
375 | 0 | if (proceeding && mRedirectChannel) |
376 | 0 | mChannel = mRedirectChannel; |
377 | 0 |
|
378 | 0 | mRedirectChannel = nullptr; |
379 | 0 |
|
380 | 0 | return NS_OK; |
381 | 0 | } |
382 | | |
383 | | //----------------------------------------------------------------------------- |
384 | | // nsPrefetchService <public> |
385 | | //----------------------------------------------------------------------------- |
386 | | |
387 | | nsPrefetchService::nsPrefetchService() |
388 | | : mMaxParallelism(6) |
389 | | , mStopCount(0) |
390 | | , mHaveProcessed(false) |
391 | | , mPrefetchDisabled(true) |
392 | | , mPreloadDisabled(true) |
393 | | , mAggressive(false) |
394 | 0 | { |
395 | 0 | } |
396 | | |
397 | | nsPrefetchService::~nsPrefetchService() |
398 | 0 | { |
399 | 0 | Preferences::RemoveObserver(this, PREFETCH_PREF); |
400 | 0 | Preferences::RemoveObserver(this, PRELOAD_PREF); |
401 | 0 | Preferences::RemoveObserver(this, PARALLELISM_PREF); |
402 | 0 | Preferences::RemoveObserver(this, AGGRESSIVE_PREF); |
403 | 0 | // cannot reach destructor if prefetch in progress (listener owns reference |
404 | 0 | // to this service) |
405 | 0 | EmptyPrefetchQueue(); |
406 | 0 | } |
407 | | |
408 | | nsresult |
409 | | nsPrefetchService::Init() |
410 | 0 | { |
411 | 0 | nsresult rv; |
412 | 0 |
|
413 | 0 | // read prefs and hook up pref observer |
414 | 0 | mPrefetchDisabled = !Preferences::GetBool(PREFETCH_PREF, !mPrefetchDisabled); |
415 | 0 | Preferences::AddWeakObserver(this, PREFETCH_PREF); |
416 | 0 |
|
417 | 0 | mPreloadDisabled = !Preferences::GetBool(PRELOAD_PREF, !mPreloadDisabled); |
418 | 0 | Preferences::AddWeakObserver(this, PRELOAD_PREF); |
419 | 0 |
|
420 | 0 | mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism); |
421 | 0 | if (mMaxParallelism < 1) { |
422 | 0 | mMaxParallelism = 1; |
423 | 0 | } |
424 | 0 | Preferences::AddWeakObserver(this, PARALLELISM_PREF); |
425 | 0 |
|
426 | 0 | mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false); |
427 | 0 | Preferences::AddWeakObserver(this, AGGRESSIVE_PREF); |
428 | 0 |
|
429 | 0 | // Observe xpcom-shutdown event |
430 | 0 | nsCOMPtr<nsIObserverService> observerService = |
431 | 0 | mozilla::services::GetObserverService(); |
432 | 0 | if (!observerService) |
433 | 0 | return NS_ERROR_FAILURE; |
434 | 0 | |
435 | 0 | rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true); |
436 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
437 | 0 |
|
438 | 0 | if (!mPrefetchDisabled || !mPreloadDisabled) { |
439 | 0 | AddProgressListener(); |
440 | 0 | } |
441 | 0 |
|
442 | 0 | return NS_OK; |
443 | 0 | } |
444 | | |
445 | | void |
446 | | nsPrefetchService::RemoveNodeAndMaybeStartNextPrefetchURI(nsPrefetchNode *aFinished) |
447 | 0 | { |
448 | 0 | if (aFinished) { |
449 | 0 | mCurrentNodes.RemoveElement(aFinished); |
450 | 0 | } |
451 | 0 |
|
452 | 0 | if ((!mStopCount && mHaveProcessed) || mAggressive) { |
453 | 0 | ProcessNextPrefetchURI(); |
454 | 0 | } |
455 | 0 | } |
456 | | |
457 | | void |
458 | | nsPrefetchService::ProcessNextPrefetchURI() |
459 | 0 | { |
460 | 0 | if (mCurrentNodes.Length() >= static_cast<uint32_t>(mMaxParallelism)) { |
461 | 0 | // We already have enough prefetches going on, so hold off |
462 | 0 | // for now. |
463 | 0 | return; |
464 | 0 | } |
465 | 0 | |
466 | 0 | nsresult rv; |
467 | 0 |
|
468 | 0 | do { |
469 | 0 | if (mPrefetchQueue.empty()) { |
470 | 0 | break; |
471 | 0 | } |
472 | 0 | RefPtr<nsPrefetchNode> node = mPrefetchQueue.front().forget(); |
473 | 0 | mPrefetchQueue.pop_front(); |
474 | 0 |
|
475 | 0 | if (LOG_ENABLED()) { |
476 | 0 | LOG(("ProcessNextPrefetchURI [%s]\n", |
477 | 0 | node->mURI->GetSpecOrDefault().get())); } |
478 | 0 |
|
479 | 0 | // |
480 | 0 | // if opening the channel fails (e.g. security check returns an error), |
481 | 0 | // send an error event and then just skip to the next uri |
482 | 0 | // |
483 | 0 | rv = node->OpenChannel(); |
484 | 0 | if (NS_SUCCEEDED(rv)) { |
485 | 0 | mCurrentNodes.AppendElement(node); |
486 | 0 | } else { |
487 | 0 | DispatchEvent(node, false); |
488 | 0 | } |
489 | 0 | } |
490 | 0 | while (NS_FAILED(rv)); |
491 | 0 | } |
492 | | |
493 | | void |
494 | | nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node) |
495 | 0 | { |
496 | 0 | nsCOMPtr<nsIObserverService> observerService = |
497 | 0 | mozilla::services::GetObserverService(); |
498 | 0 | if (!observerService) |
499 | 0 | return; |
500 | 0 | |
501 | 0 | observerService->NotifyObservers(static_cast<nsIStreamListener*>(node), |
502 | 0 | (node->mPreload) ? "preload-load-requested" |
503 | 0 | : "prefetch-load-requested", |
504 | 0 | nullptr); |
505 | 0 | } |
506 | | |
507 | | void |
508 | | nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node) |
509 | 0 | { |
510 | 0 | nsCOMPtr<nsIObserverService> observerService = |
511 | 0 | mozilla::services::GetObserverService(); |
512 | 0 | if (!observerService) |
513 | 0 | return; |
514 | 0 | |
515 | 0 | observerService->NotifyObservers(static_cast<nsIStreamListener*>(node), |
516 | 0 | (node->mPreload) ? "preload-load-completed" |
517 | 0 | : "prefetch-load-completed", |
518 | 0 | nullptr); |
519 | 0 | } |
520 | | |
521 | | void |
522 | | nsPrefetchService::DispatchEvent(nsPrefetchNode *node, bool aSuccess) |
523 | 0 | { |
524 | 0 | for (uint32_t i = 0; i < node->mSources.Length(); i++) { |
525 | 0 | nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i)); |
526 | 0 | if (domNode && domNode->IsInComposedDoc()) { |
527 | 0 | // We don't dispatch synchronously since |node| might be in a DocGroup |
528 | 0 | // that we're not allowed to touch. (Our network request happens in the |
529 | 0 | // DocGroup of one of the mSources nodes--not necessarily this one). |
530 | 0 | RefPtr<AsyncEventDispatcher> dispatcher = |
531 | 0 | new AsyncEventDispatcher(domNode, |
532 | 0 | aSuccess ? |
533 | 0 | NS_LITERAL_STRING("load") : |
534 | 0 | NS_LITERAL_STRING("error"), |
535 | 0 | CanBubble::eNo); |
536 | 0 | dispatcher->RequireNodeInDocument(); |
537 | 0 | dispatcher->PostDOMEvent(); |
538 | 0 | } |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | //----------------------------------------------------------------------------- |
543 | | // nsPrefetchService <private> |
544 | | //----------------------------------------------------------------------------- |
545 | | |
546 | | void |
547 | | nsPrefetchService::AddProgressListener() |
548 | 0 | { |
549 | 0 | // Register as an observer for the document loader |
550 | 0 | nsCOMPtr<nsIWebProgress> progress = |
551 | 0 | do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); |
552 | 0 | if (progress) |
553 | 0 | progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); |
554 | 0 | } |
555 | | |
556 | | void |
557 | | nsPrefetchService::RemoveProgressListener() |
558 | 0 | { |
559 | 0 | // Register as an observer for the document loader |
560 | 0 | nsCOMPtr<nsIWebProgress> progress = |
561 | 0 | do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); |
562 | 0 | if (progress) |
563 | 0 | progress->RemoveProgressListener(this); |
564 | 0 | } |
565 | | |
566 | | nsresult |
567 | | nsPrefetchService::EnqueueURI(nsIURI *aURI, |
568 | | nsIURI *aReferrerURI, |
569 | | nsINode *aSource, |
570 | | nsPrefetchNode **aNode) |
571 | 0 | { |
572 | 0 | RefPtr<nsPrefetchNode> node = new nsPrefetchNode(this, aURI, aReferrerURI, |
573 | 0 | aSource, |
574 | 0 | nsIContentPolicy::TYPE_OTHER, |
575 | 0 | false); |
576 | 0 | mPrefetchQueue.push_back(node); |
577 | 0 | node.forget(aNode); |
578 | 0 | return NS_OK; |
579 | 0 | } |
580 | | |
581 | | void |
582 | | nsPrefetchService::EmptyPrefetchQueue() |
583 | 0 | { |
584 | 0 | while (!mPrefetchQueue.empty()) { |
585 | 0 | mPrefetchQueue.pop_back(); |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | void |
590 | | nsPrefetchService::StartPrefetching() |
591 | 0 | { |
592 | 0 | // |
593 | 0 | // at initialization time we might miss the first DOCUMENT START |
594 | 0 | // notification, so we have to be careful to avoid letting our |
595 | 0 | // stop count go negative. |
596 | 0 | // |
597 | 0 | if (mStopCount > 0) |
598 | 0 | mStopCount--; |
599 | 0 |
|
600 | 0 | LOG(("StartPrefetching [stopcount=%d]\n", mStopCount)); |
601 | 0 |
|
602 | 0 | // only start prefetching after we've received enough DOCUMENT |
603 | 0 | // STOP notifications. we do this inorder to defer prefetching |
604 | 0 | // until after all sub-frames have finished loading. |
605 | 0 | if (!mStopCount) { |
606 | 0 | mHaveProcessed = true; |
607 | 0 | while (!mPrefetchQueue.empty() && |
608 | 0 | mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) { |
609 | 0 | ProcessNextPrefetchURI(); |
610 | 0 | } |
611 | 0 | } |
612 | 0 | } |
613 | | |
614 | | void |
615 | | nsPrefetchService::StopPrefetching() |
616 | 0 | { |
617 | 0 | mStopCount++; |
618 | 0 |
|
619 | 0 | LOG(("StopPrefetching [stopcount=%d]\n", mStopCount)); |
620 | 0 |
|
621 | 0 | // When we start a load, we need to stop all prefetches that has been |
622 | 0 | // added by the old load, therefore call StopAll only at the moment we |
623 | 0 | // switch to a new page load (i.e. mStopCount == 1). |
624 | 0 | // TODO: do not stop prefetches that are relevant for the new load. |
625 | 0 | if (mStopCount == 1) { |
626 | 0 | StopAll(); |
627 | 0 | } |
628 | 0 | } |
629 | | |
630 | | void |
631 | | nsPrefetchService::StopCurrentPrefetchsPreloads(bool aPreload) |
632 | 0 | { |
633 | 0 | for (int32_t i = mCurrentNodes.Length() - 1; i >= 0; --i) { |
634 | 0 | if (mCurrentNodes[i]->mPreload == aPreload) { |
635 | 0 | mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED); |
636 | 0 | mCurrentNodes.RemoveElementAt(i); |
637 | 0 | } |
638 | 0 | } |
639 | 0 |
|
640 | 0 | if (!aPreload) { |
641 | 0 | EmptyPrefetchQueue(); |
642 | 0 | } |
643 | 0 | } |
644 | | |
645 | | void |
646 | | nsPrefetchService::StopAll() |
647 | 0 | { |
648 | 0 | for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) { |
649 | 0 | mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED); |
650 | 0 | } |
651 | 0 | mCurrentNodes.Clear(); |
652 | 0 | EmptyPrefetchQueue(); |
653 | 0 | } |
654 | | |
655 | | nsresult |
656 | | nsPrefetchService::CheckURIScheme(nsIURI *aURI, nsIURI *aReferrerURI) |
657 | 0 | { |
658 | 0 | // |
659 | 0 | // XXX we should really be asking the protocol handler if it supports |
660 | 0 | // caching, so we can determine if there is any value to prefetching. |
661 | 0 | // for now, we'll only prefetch http and https links since we know that's |
662 | 0 | // the most common case. |
663 | 0 | // |
664 | 0 | bool match; |
665 | 0 | nsresult rv = aURI->SchemeIs("http", &match); |
666 | 0 | if (NS_FAILED(rv) || !match) { |
667 | 0 | rv = aURI->SchemeIs("https", &match); |
668 | 0 | if (NS_FAILED(rv) || !match) { |
669 | 0 | LOG(("rejected: URL is not of type http/https\n")); |
670 | 0 | return NS_ERROR_ABORT; |
671 | 0 | } |
672 | 0 | } |
673 | 0 |
|
674 | 0 | // |
675 | 0 | // the referrer URI must be http: |
676 | 0 | // |
677 | 0 | rv = aReferrerURI->SchemeIs("http", &match); |
678 | 0 | if (NS_FAILED(rv) || !match) { |
679 | 0 | rv = aReferrerURI->SchemeIs("https", &match); |
680 | 0 | if (NS_FAILED(rv) || !match) { |
681 | 0 | LOG(("rejected: referrer URL is neither http nor https\n")); |
682 | 0 | return NS_ERROR_ABORT; |
683 | 0 | } |
684 | 0 | } |
685 | 0 |
|
686 | 0 | return NS_OK; |
687 | 0 | } |
688 | | |
689 | | //----------------------------------------------------------------------------- |
690 | | // nsPrefetchService::nsISupports |
691 | | //----------------------------------------------------------------------------- |
692 | | |
693 | | NS_IMPL_ISUPPORTS(nsPrefetchService, |
694 | | nsIPrefetchService, |
695 | | nsIWebProgressListener, |
696 | | nsIObserver, |
697 | | nsISupportsWeakReference) |
698 | | |
699 | | //----------------------------------------------------------------------------- |
700 | | // nsPrefetchService::nsIPrefetchService |
701 | | //----------------------------------------------------------------------------- |
702 | | |
703 | | nsresult |
704 | | nsPrefetchService::Preload(nsIURI *aURI, |
705 | | nsIURI *aReferrerURI, |
706 | | nsINode *aSource, |
707 | | nsContentPolicyType aPolicyType) |
708 | 0 | { |
709 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
710 | 0 | NS_ENSURE_ARG_POINTER(aReferrerURI); |
711 | 0 | if (LOG_ENABLED()) { |
712 | 0 | LOG(("PreloadURI [%s]\n", aURI->GetSpecOrDefault().get())); |
713 | 0 | } |
714 | 0 |
|
715 | 0 | if (mPreloadDisabled) { |
716 | 0 | LOG(("rejected: preload service is disabled\n")); |
717 | 0 | return NS_ERROR_ABORT; |
718 | 0 | } |
719 | 0 |
|
720 | 0 | nsresult rv = CheckURIScheme(aURI, aReferrerURI); |
721 | 0 | if (NS_FAILED(rv)) { |
722 | 0 | return rv; |
723 | 0 | } |
724 | 0 | |
725 | 0 | // XXX we might want to either leverage nsIProtocolHandler::protocolFlags |
726 | 0 | // or possibly nsIRequest::loadFlags to determine if this URI should be |
727 | 0 | // prefetched. |
728 | 0 | // |
729 | 0 | |
730 | 0 | if (aPolicyType == nsIContentPolicy::TYPE_INVALID) { |
731 | 0 | if (aSource && aSource->IsInComposedDoc()) { |
732 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
733 | 0 | new AsyncEventDispatcher(aSource, |
734 | 0 | NS_LITERAL_STRING("error"), |
735 | 0 | CanBubble::eNo, |
736 | 0 | ChromeOnlyDispatch::eNo); |
737 | 0 | asyncDispatcher->RunDOMEventWhenSafe(); |
738 | 0 | } |
739 | 0 | return NS_OK; |
740 | 0 | } |
741 | 0 |
|
742 | 0 | // |
743 | 0 | // Check whether it is being preloaded. |
744 | 0 | // |
745 | 0 | for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) { |
746 | 0 | bool equals; |
747 | 0 | if ((mCurrentNodes[i]->mPolicyType == aPolicyType) && |
748 | 0 | NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && |
749 | 0 | equals) { |
750 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
751 | 0 | if (mCurrentNodes[i]->mSources.IndexOf(source) == |
752 | 0 | mCurrentNodes[i]->mSources.NoIndex) { |
753 | 0 | LOG(("URL is already being preloaded, add a new reference " |
754 | 0 | "document\n")); |
755 | 0 | mCurrentNodes[i]->mSources.AppendElement(source); |
756 | 0 | return NS_OK; |
757 | 0 | } else { |
758 | 0 | LOG(("URL is already being preloaded by this document")); |
759 | 0 | return NS_ERROR_ABORT; |
760 | 0 | } |
761 | 0 | } |
762 | 0 | } |
763 | 0 |
|
764 | 0 | LOG(("This is a preload, so start loading immediately.\n")); |
765 | 0 | RefPtr<nsPrefetchNode> enqueuedNode; |
766 | 0 | enqueuedNode = new nsPrefetchNode(this, aURI, aReferrerURI, |
767 | 0 | aSource, aPolicyType, true); |
768 | 0 |
|
769 | 0 | NotifyLoadRequested(enqueuedNode); |
770 | 0 | rv = enqueuedNode->OpenChannel(); |
771 | 0 | if (NS_SUCCEEDED(rv)) { |
772 | 0 | mCurrentNodes.AppendElement(enqueuedNode); |
773 | 0 | } else { |
774 | 0 | if (aSource && aSource->IsInComposedDoc()) { |
775 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
776 | 0 | new AsyncEventDispatcher(aSource, |
777 | 0 | NS_LITERAL_STRING("error"), |
778 | 0 | CanBubble::eNo, |
779 | 0 | ChromeOnlyDispatch::eNo); |
780 | 0 | asyncDispatcher->RunDOMEventWhenSafe(); |
781 | 0 | } |
782 | 0 | } |
783 | 0 | return NS_OK; |
784 | 0 | } |
785 | | |
786 | | nsresult |
787 | | nsPrefetchService::Prefetch(nsIURI *aURI, |
788 | | nsIURI *aReferrerURI, |
789 | | nsINode *aSource, |
790 | | bool aExplicit) |
791 | 0 | { |
792 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
793 | 0 | NS_ENSURE_ARG_POINTER(aReferrerURI); |
794 | 0 |
|
795 | 0 | if (LOG_ENABLED()) { |
796 | 0 | LOG(("PrefetchURI [%s]\n", aURI->GetSpecOrDefault().get())); |
797 | 0 | } |
798 | 0 |
|
799 | 0 | if (mPrefetchDisabled) { |
800 | 0 | LOG(("rejected: prefetch service is disabled\n")); |
801 | 0 | return NS_ERROR_ABORT; |
802 | 0 | } |
803 | 0 |
|
804 | 0 | nsresult rv = CheckURIScheme(aURI, aReferrerURI); |
805 | 0 | if (NS_FAILED(rv)) { |
806 | 0 | return rv; |
807 | 0 | } |
808 | 0 | |
809 | 0 | // XXX we might want to either leverage nsIProtocolHandler::protocolFlags |
810 | 0 | // or possibly nsIRequest::loadFlags to determine if this URI should be |
811 | 0 | // prefetched. |
812 | 0 | // |
813 | 0 | |
814 | 0 | // skip URLs that contain query strings, except URLs for which prefetching |
815 | 0 | // has been explicitly requested. |
816 | 0 | if (!aExplicit) { |
817 | 0 | nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv)); |
818 | 0 | if (NS_FAILED(rv)) return rv; |
819 | 0 | nsAutoCString query; |
820 | 0 | rv = url->GetQuery(query); |
821 | 0 | if (NS_FAILED(rv) || !query.IsEmpty()) { |
822 | 0 | LOG(("rejected: URL has a query string\n")); |
823 | 0 | return NS_ERROR_ABORT; |
824 | 0 | } |
825 | 0 | } |
826 | 0 |
|
827 | 0 | // |
828 | 0 | // Check whether it is being prefetched. |
829 | 0 | // |
830 | 0 | for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) { |
831 | 0 | bool equals; |
832 | 0 | if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && |
833 | 0 | equals) { |
834 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
835 | 0 | if (mCurrentNodes[i]->mSources.IndexOf(source) == |
836 | 0 | mCurrentNodes[i]->mSources.NoIndex) { |
837 | 0 | LOG(("URL is already being prefetched, add a new reference " |
838 | 0 | "document\n")); |
839 | 0 | mCurrentNodes[i]->mSources.AppendElement(source); |
840 | 0 | return NS_OK; |
841 | 0 | } else { |
842 | 0 | LOG(("URL is already being prefetched by this document")); |
843 | 0 | return NS_ERROR_ABORT; |
844 | 0 | } |
845 | 0 | } |
846 | 0 | } |
847 | 0 |
|
848 | 0 | // |
849 | 0 | // Check whether it is on the prefetch queue. |
850 | 0 | // |
851 | 0 | for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mPrefetchQueue.begin(); |
852 | 0 | nodeIt != mPrefetchQueue.end(); nodeIt++) { |
853 | 0 | bool equals; |
854 | 0 | RefPtr<nsPrefetchNode> node = nodeIt->get(); |
855 | 0 | if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) { |
856 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
857 | 0 | if (node->mSources.IndexOf(source) == |
858 | 0 | node->mSources.NoIndex) { |
859 | 0 | LOG(("URL is already being prefetched, add a new reference " |
860 | 0 | "document\n")); |
861 | 0 | node->mSources.AppendElement(do_GetWeakReference(aSource)); |
862 | 0 | return NS_OK; |
863 | 0 | } else { |
864 | 0 | LOG(("URL is already being prefetched by this document")); |
865 | 0 | return NS_ERROR_ABORT; |
866 | 0 | } |
867 | 0 |
|
868 | 0 | } |
869 | 0 | } |
870 | 0 |
|
871 | 0 | RefPtr<nsPrefetchNode> enqueuedNode; |
872 | 0 | rv = EnqueueURI(aURI, aReferrerURI, aSource, |
873 | 0 | getter_AddRefs(enqueuedNode)); |
874 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
875 | 0 |
|
876 | 0 | NotifyLoadRequested(enqueuedNode); |
877 | 0 |
|
878 | 0 | // if there are no pages loading, kick off the request immediately |
879 | 0 | if ((!mStopCount && mHaveProcessed) || mAggressive) { |
880 | 0 | ProcessNextPrefetchURI(); |
881 | 0 | } |
882 | 0 |
|
883 | 0 | return NS_OK; |
884 | 0 | } |
885 | | |
886 | | NS_IMETHODIMP |
887 | | nsPrefetchService::CancelPrefetchPreloadURI(nsIURI* aURI, |
888 | | nsINode* aSource) |
889 | 0 | { |
890 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
891 | 0 |
|
892 | 0 | if (LOG_ENABLED()) { |
893 | 0 | LOG(("CancelPrefetchURI [%s]\n", aURI->GetSpecOrDefault().get())); |
894 | 0 | } |
895 | 0 |
|
896 | 0 | // |
897 | 0 | // look in current prefetches |
898 | 0 | // |
899 | 0 | for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) { |
900 | 0 | bool equals; |
901 | 0 | if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && |
902 | 0 | equals) { |
903 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
904 | 0 | if (mCurrentNodes[i]->mSources.IndexOf(source) != |
905 | 0 | mCurrentNodes[i]->mSources.NoIndex) { |
906 | 0 | mCurrentNodes[i]->mSources.RemoveElement(source); |
907 | 0 | if (mCurrentNodes[i]->mSources.IsEmpty()) { |
908 | 0 | mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED); |
909 | 0 | mCurrentNodes.RemoveElementAt(i); |
910 | 0 | } |
911 | 0 | return NS_OK; |
912 | 0 | } |
913 | 0 | return NS_ERROR_FAILURE; |
914 | 0 | } |
915 | 0 | } |
916 | 0 |
|
917 | 0 | // |
918 | 0 | // look into the prefetch queue |
919 | 0 | // |
920 | 0 | for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mPrefetchQueue.begin(); |
921 | 0 | nodeIt != mPrefetchQueue.end(); nodeIt++) { |
922 | 0 | bool equals; |
923 | 0 | RefPtr<nsPrefetchNode> node = nodeIt->get(); |
924 | 0 | if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) { |
925 | 0 | nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource); |
926 | 0 | if (node->mSources.IndexOf(source) != |
927 | 0 | node->mSources.NoIndex) { |
928 | 0 |
|
929 | | #ifdef DEBUG |
930 | | int32_t inx = node->mSources.IndexOf(source); |
931 | | nsCOMPtr<nsINode> domNode = |
932 | | do_QueryReferent(node->mSources.ElementAt(inx)); |
933 | | MOZ_ASSERT(domNode); |
934 | | #endif |
935 | |
|
936 | 0 | node->mSources.RemoveElement(source); |
937 | 0 | if (node->mSources.IsEmpty()) { |
938 | 0 | mPrefetchQueue.erase(nodeIt); |
939 | 0 | } |
940 | 0 | return NS_OK; |
941 | 0 | } |
942 | 0 | return NS_ERROR_FAILURE; |
943 | 0 | } |
944 | 0 | } |
945 | 0 |
|
946 | 0 | // not found! |
947 | 0 | return NS_ERROR_FAILURE; |
948 | 0 | } |
949 | | |
950 | | NS_IMETHODIMP |
951 | | nsPrefetchService::PreloadURI(nsIURI *aURI, |
952 | | nsIURI *aReferrerURI, |
953 | | nsINode *aSource, |
954 | | nsContentPolicyType aPolicyType) |
955 | 0 | { |
956 | 0 | return Preload(aURI, aReferrerURI, aSource, aPolicyType); |
957 | 0 | } |
958 | | |
959 | | NS_IMETHODIMP |
960 | | nsPrefetchService::PrefetchURI(nsIURI *aURI, |
961 | | nsIURI *aReferrerURI, |
962 | | nsINode *aSource, |
963 | | bool aExplicit) |
964 | 0 | { |
965 | 0 | return Prefetch(aURI, aReferrerURI, aSource, aExplicit); |
966 | 0 | } |
967 | | |
968 | | NS_IMETHODIMP |
969 | | nsPrefetchService::HasMoreElements(bool *aHasMore) |
970 | 0 | { |
971 | 0 | *aHasMore = (mCurrentNodes.Length() || !mPrefetchQueue.empty()); |
972 | 0 | return NS_OK; |
973 | 0 | } |
974 | | |
975 | | //----------------------------------------------------------------------------- |
976 | | // nsPrefetchService::nsIWebProgressListener |
977 | | //----------------------------------------------------------------------------- |
978 | | |
979 | | NS_IMETHODIMP |
980 | | nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress, |
981 | | nsIRequest *aRequest, |
982 | | int32_t curSelfProgress, |
983 | | int32_t maxSelfProgress, |
984 | | int32_t curTotalProgress, |
985 | | int32_t maxTotalProgress) |
986 | 0 | { |
987 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
988 | 0 | return NS_OK; |
989 | 0 | } |
990 | | |
991 | | NS_IMETHODIMP |
992 | | nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress, |
993 | | nsIRequest *aRequest, |
994 | | uint32_t progressStateFlags, |
995 | | nsresult aStatus) |
996 | 0 | { |
997 | 0 | if (progressStateFlags & STATE_IS_DOCUMENT) { |
998 | 0 | if (progressStateFlags & STATE_STOP) |
999 | 0 | StartPrefetching(); |
1000 | 0 | else if (progressStateFlags & STATE_START) |
1001 | 0 | StopPrefetching(); |
1002 | 0 | } |
1003 | 0 | |
1004 | 0 | return NS_OK; |
1005 | 0 | } |
1006 | | |
1007 | | |
1008 | | NS_IMETHODIMP |
1009 | | nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress, |
1010 | | nsIRequest* aRequest, |
1011 | | nsIURI *location, |
1012 | | uint32_t aFlags) |
1013 | 0 | { |
1014 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
1015 | 0 | return NS_OK; |
1016 | 0 | } |
1017 | | |
1018 | | NS_IMETHODIMP |
1019 | | nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress, |
1020 | | nsIRequest* aRequest, |
1021 | | nsresult aStatus, |
1022 | | const char16_t* aMessage) |
1023 | 0 | { |
1024 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
1025 | 0 | return NS_OK; |
1026 | 0 | } |
1027 | | |
1028 | | NS_IMETHODIMP |
1029 | | nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress, |
1030 | | nsIRequest *aRequest, |
1031 | | uint32_t state) |
1032 | 0 | { |
1033 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
1034 | 0 | return NS_OK; |
1035 | 0 | } |
1036 | | |
1037 | | //----------------------------------------------------------------------------- |
1038 | | // nsPrefetchService::nsIObserver |
1039 | | //----------------------------------------------------------------------------- |
1040 | | |
1041 | | NS_IMETHODIMP |
1042 | | nsPrefetchService::Observe(nsISupports *aSubject, |
1043 | | const char *aTopic, |
1044 | | const char16_t *aData) |
1045 | 0 | { |
1046 | 0 | LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic)); |
1047 | 0 |
|
1048 | 0 | if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { |
1049 | 0 | StopAll(); |
1050 | 0 | mPrefetchDisabled = true; |
1051 | 0 | mPreloadDisabled = true; |
1052 | 0 | } |
1053 | 0 | else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { |
1054 | 0 | const nsCString converted = NS_ConvertUTF16toUTF8(aData); |
1055 | 0 | const char* pref = converted.get(); |
1056 | 0 | if (!strcmp(pref, PREFETCH_PREF)) { |
1057 | 0 | if (Preferences::GetBool(PREFETCH_PREF, false)) { |
1058 | 0 | if (mPrefetchDisabled) { |
1059 | 0 | LOG(("enabling prefetching\n")); |
1060 | 0 | mPrefetchDisabled = false; |
1061 | 0 | if (mPreloadDisabled) { |
1062 | 0 | AddProgressListener(); |
1063 | 0 | } |
1064 | 0 | } |
1065 | 0 | } else { |
1066 | 0 | if (!mPrefetchDisabled) { |
1067 | 0 | LOG(("disabling prefetching\n")); |
1068 | 0 | StopCurrentPrefetchsPreloads(false); |
1069 | 0 | mPrefetchDisabled = true; |
1070 | 0 | if (mPreloadDisabled) { |
1071 | 0 | RemoveProgressListener(); |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | } |
1075 | 0 | } else if (!strcmp(pref, PRELOAD_PREF)) { |
1076 | 0 | if (Preferences::GetBool(PRELOAD_PREF, false)) { |
1077 | 0 | if (mPreloadDisabled) { |
1078 | 0 | LOG(("enabling preloading\n")); |
1079 | 0 | mPreloadDisabled = false; |
1080 | 0 | if (mPrefetchDisabled) { |
1081 | 0 | AddProgressListener(); |
1082 | 0 | } |
1083 | 0 | } |
1084 | 0 | } else { |
1085 | 0 | if (!mPreloadDisabled) { |
1086 | 0 | LOG(("disabling preloading\n")); |
1087 | 0 | StopCurrentPrefetchsPreloads(true); |
1088 | 0 | mPreloadDisabled = true; |
1089 | 0 | if (mPrefetchDisabled) { |
1090 | 0 | RemoveProgressListener(); |
1091 | 0 | } |
1092 | 0 | } |
1093 | 0 | } |
1094 | 0 | } else if (!strcmp(pref, PARALLELISM_PREF)) { |
1095 | 0 | mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism); |
1096 | 0 | if (mMaxParallelism < 1) { |
1097 | 0 | mMaxParallelism = 1; |
1098 | 0 | } |
1099 | 0 | // If our parallelism has increased, go ahead and kick off enough |
1100 | 0 | // prefetches to fill up our allowance. If we're now over our |
1101 | 0 | // allowance, we'll just silently let some of them finish to get |
1102 | 0 | // back below our limit. |
1103 | 0 | while (((!mStopCount && mHaveProcessed) || mAggressive) && |
1104 | 0 | !mPrefetchQueue.empty() && |
1105 | 0 | mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) { |
1106 | 0 | ProcessNextPrefetchURI(); |
1107 | 0 | } |
1108 | 0 | } else if (!strcmp(pref, AGGRESSIVE_PREF)) { |
1109 | 0 | mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false); |
1110 | 0 | // in aggressive mode, start prefetching immediately |
1111 | 0 | if (mAggressive) { |
1112 | 0 | while (mStopCount && !mPrefetchQueue.empty() && |
1113 | 0 | mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) { |
1114 | 0 | ProcessNextPrefetchURI(); |
1115 | 0 | } |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | } |
1119 | 0 |
|
1120 | 0 | return NS_OK; |
1121 | 0 | } |
1122 | | |
1123 | | // vim: ts=4 sw=4 expandtab |