/src/mozilla-central/uriloader/prefetch/nsOfflineCacheUpdate.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
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 "nsOfflineCacheUpdate.h" |
7 | | |
8 | | #include "nsCPrefetchService.h" |
9 | | #include "nsCURILoader.h" |
10 | | #include "nsIApplicationCacheContainer.h" |
11 | | #include "nsIApplicationCacheChannel.h" |
12 | | #include "nsIApplicationCacheService.h" |
13 | | #include "nsICachingChannel.h" |
14 | | #include "nsIContent.h" |
15 | | #include "mozilla/dom/Element.h" |
16 | | #include "mozilla/dom/OfflineResourceListBinding.h" |
17 | | #include "nsIDocumentLoader.h" |
18 | | #include "nsIDOMWindow.h" |
19 | | #include "nsIDocument.h" |
20 | | #include "nsIObserverService.h" |
21 | | #include "nsIURL.h" |
22 | | #include "nsIURIMutator.h" |
23 | | #include "nsIWebProgress.h" |
24 | | #include "nsICryptoHash.h" |
25 | | #include "nsICacheEntry.h" |
26 | | #include "nsIPermissionManager.h" |
27 | | #include "nsIPrincipal.h" |
28 | | #include "nsNetCID.h" |
29 | | #include "nsNetUtil.h" |
30 | | #include "nsServiceManagerUtils.h" |
31 | | #include "nsStreamUtils.h" |
32 | | #include "nsThreadUtils.h" |
33 | | #include "nsProxyRelease.h" |
34 | | #include "nsIConsoleService.h" |
35 | | #include "mozilla/Logging.h" |
36 | | #include "nsIAsyncVerifyRedirectCallback.h" |
37 | | #include "mozilla/Preferences.h" |
38 | | #include "mozilla/Attributes.h" |
39 | | #include "nsContentUtils.h" |
40 | | #include "nsIPrincipal.h" |
41 | | #include "nsDiskCacheDeviceSQL.h" |
42 | | |
43 | | #include "nsXULAppAPI.h" |
44 | | |
45 | | using namespace mozilla; |
46 | | |
47 | | static const uint32_t kRescheduleLimit = 3; |
48 | | // Max number of retries for every entry of pinned app. |
49 | | static const uint32_t kPinnedEntryRetriesLimit = 3; |
50 | | // Maximum number of parallel items loads |
51 | | static const uint32_t kParallelLoadLimit = 15; |
52 | | |
53 | | // Quota for offline apps when preloading |
54 | | static const int32_t kCustomProfileQuota = 512000; |
55 | | |
56 | | // |
57 | | // To enable logging (see mozilla/Logging.h for full details): |
58 | | // |
59 | | // set MOZ_LOG=nsOfflineCacheUpdate:5 |
60 | | // set MOZ_LOG_FILE=offlineupdate.log |
61 | | // |
62 | | // this enables LogLevel::Debug level information and places all output in |
63 | | // the file offlineupdate.log |
64 | | // |
65 | | extern LazyLogModule gOfflineCacheUpdateLog; |
66 | | |
67 | | #undef LOG |
68 | 0 | #define LOG(args) MOZ_LOG(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug, args) |
69 | | |
70 | | #undef LOG_ENABLED |
71 | 0 | #define LOG_ENABLED() MOZ_LOG_TEST(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug) |
72 | | |
73 | | class AutoFreeArray { |
74 | | public: |
75 | | AutoFreeArray(uint32_t count, char **values) |
76 | 0 | : mCount(count), mValues(values) {}; |
77 | 0 | ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); } |
78 | | private: |
79 | | uint32_t mCount; |
80 | | char **mValues; |
81 | | }; |
82 | | |
83 | | namespace { |
84 | | |
85 | | nsresult |
86 | | DropReferenceFromURL(nsCOMPtr<nsIURI>& aURI) |
87 | 0 | { |
88 | 0 | // XXXdholbert If this SetRef fails, callers of this method probably |
89 | 0 | // want to call aURI->CloneIgnoringRef() and use the result of that. |
90 | 0 | nsCOMPtr<nsIURI> uri(aURI); |
91 | 0 | return NS_GetURIWithoutRef(uri, getter_AddRefs(aURI)); |
92 | 0 | } |
93 | | |
94 | | void |
95 | | LogToConsole(const char * message, nsOfflineCacheUpdateItem * item = nullptr) |
96 | 0 | { |
97 | 0 | nsCOMPtr<nsIConsoleService> consoleService = |
98 | 0 | do_GetService(NS_CONSOLESERVICE_CONTRACTID); |
99 | 0 | if (consoleService) |
100 | 0 | { |
101 | 0 | nsAutoString messageUTF16 = NS_ConvertUTF8toUTF16(message); |
102 | 0 | if (item && item->mURI) { |
103 | 0 | messageUTF16.AppendLiteral(", URL="); |
104 | 0 | messageUTF16.Append( |
105 | 0 | NS_ConvertUTF8toUTF16(item->mURI->GetSpecOrDefault())); |
106 | 0 | } |
107 | 0 | consoleService->LogStringMessage(messageUTF16.get()); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | } // namespace |
112 | | |
113 | | //----------------------------------------------------------------------------- |
114 | | // nsManifestCheck |
115 | | //----------------------------------------------------------------------------- |
116 | | |
117 | | class nsManifestCheck final : public nsIStreamListener |
118 | | , public nsIChannelEventSink |
119 | | , public nsIInterfaceRequestor |
120 | | { |
121 | | public: |
122 | | nsManifestCheck(nsOfflineCacheUpdate *aUpdate, |
123 | | nsIURI *aURI, |
124 | | nsIURI *aReferrerURI, |
125 | | nsIPrincipal* aLoadingPrincipal) |
126 | | : mUpdate(aUpdate) |
127 | | , mURI(aURI) |
128 | | , mReferrerURI(aReferrerURI) |
129 | | , mLoadingPrincipal(aLoadingPrincipal) |
130 | 0 | {} |
131 | | |
132 | | NS_DECL_ISUPPORTS |
133 | | NS_DECL_NSIREQUESTOBSERVER |
134 | | NS_DECL_NSISTREAMLISTENER |
135 | | NS_DECL_NSICHANNELEVENTSINK |
136 | | NS_DECL_NSIINTERFACEREQUESTOR |
137 | | |
138 | | nsresult Begin(); |
139 | | |
140 | | private: |
141 | | |
142 | 0 | ~nsManifestCheck() {} |
143 | | |
144 | | static nsresult ReadManifest(nsIInputStream *aInputStream, |
145 | | void *aClosure, |
146 | | const char *aFromSegment, |
147 | | uint32_t aOffset, |
148 | | uint32_t aCount, |
149 | | uint32_t *aBytesConsumed); |
150 | | |
151 | | RefPtr<nsOfflineCacheUpdate> mUpdate; |
152 | | nsCOMPtr<nsIURI> mURI; |
153 | | nsCOMPtr<nsIURI> mReferrerURI; |
154 | | nsCOMPtr<nsIPrincipal> mLoadingPrincipal; |
155 | | nsCOMPtr<nsICryptoHash> mManifestHash; |
156 | | nsCOMPtr<nsIChannel> mChannel; |
157 | | }; |
158 | | |
159 | | //----------------------------------------------------------------------------- |
160 | | // nsManifestCheck::nsISupports |
161 | | //----------------------------------------------------------------------------- |
162 | | NS_IMPL_ISUPPORTS(nsManifestCheck, |
163 | | nsIRequestObserver, |
164 | | nsIStreamListener, |
165 | | nsIChannelEventSink, |
166 | | nsIInterfaceRequestor) |
167 | | |
168 | | //----------------------------------------------------------------------------- |
169 | | // nsManifestCheck <public> |
170 | | //----------------------------------------------------------------------------- |
171 | | |
172 | | nsresult |
173 | | nsManifestCheck::Begin() |
174 | 0 | { |
175 | 0 | nsresult rv; |
176 | 0 | mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); |
177 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
178 | 0 |
|
179 | 0 | rv = mManifestHash->Init(nsICryptoHash::MD5); |
180 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
181 | 0 | rv = NS_NewChannel(getter_AddRefs(mChannel), |
182 | 0 | mURI, |
183 | 0 | mLoadingPrincipal, |
184 | 0 | nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, |
185 | 0 | nsIContentPolicy::TYPE_OTHER, |
186 | 0 | nullptr, // PerformanceStorage |
187 | 0 | nullptr, // loadGroup |
188 | 0 | nullptr, // aCallbacks |
189 | 0 | nsIRequest::LOAD_BYPASS_CACHE); |
190 | 0 |
|
191 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
192 | 0 |
|
193 | 0 | // configure HTTP specific stuff |
194 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = |
195 | 0 | do_QueryInterface(mChannel); |
196 | 0 | if (httpChannel) { |
197 | 0 | rv = httpChannel->SetReferrer(mReferrerURI); |
198 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
199 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), |
200 | 0 | NS_LITERAL_CSTRING("offline-resource"), |
201 | 0 | false); |
202 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
203 | 0 | } |
204 | 0 |
|
205 | 0 | return mChannel->AsyncOpen2(this); |
206 | 0 | } |
207 | | |
208 | | //----------------------------------------------------------------------------- |
209 | | // nsManifestCheck <public> |
210 | | //----------------------------------------------------------------------------- |
211 | | |
212 | | /* static */ nsresult |
213 | | nsManifestCheck::ReadManifest(nsIInputStream *aInputStream, |
214 | | void *aClosure, |
215 | | const char *aFromSegment, |
216 | | uint32_t aOffset, |
217 | | uint32_t aCount, |
218 | | uint32_t *aBytesConsumed) |
219 | 0 | { |
220 | 0 | nsManifestCheck *manifestCheck = |
221 | 0 | static_cast<nsManifestCheck*>(aClosure); |
222 | 0 |
|
223 | 0 | nsresult rv; |
224 | 0 | *aBytesConsumed = aCount; |
225 | 0 |
|
226 | 0 | rv = manifestCheck->mManifestHash->Update( |
227 | 0 | reinterpret_cast<const uint8_t *>(aFromSegment), aCount); |
228 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
229 | 0 |
|
230 | 0 | return NS_OK; |
231 | 0 | } |
232 | | |
233 | | //----------------------------------------------------------------------------- |
234 | | // nsManifestCheck::nsIStreamListener |
235 | | //----------------------------------------------------------------------------- |
236 | | |
237 | | NS_IMETHODIMP |
238 | | nsManifestCheck::OnStartRequest(nsIRequest *aRequest, |
239 | | nsISupports *aContext) |
240 | 0 | { |
241 | 0 | return NS_OK; |
242 | 0 | } |
243 | | |
244 | | NS_IMETHODIMP |
245 | | nsManifestCheck::OnDataAvailable(nsIRequest *aRequest, |
246 | | nsISupports *aContext, |
247 | | nsIInputStream *aStream, |
248 | | uint64_t aOffset, |
249 | | uint32_t aCount) |
250 | 0 | { |
251 | 0 | uint32_t bytesRead; |
252 | 0 | aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead); |
253 | 0 | return NS_OK; |
254 | 0 | } |
255 | | |
256 | | NS_IMETHODIMP |
257 | | nsManifestCheck::OnStopRequest(nsIRequest *aRequest, |
258 | | nsISupports *aContext, |
259 | | nsresult aStatus) |
260 | 0 | { |
261 | 0 | nsAutoCString manifestHash; |
262 | 0 | if (NS_SUCCEEDED(aStatus)) { |
263 | 0 | mManifestHash->Finish(true, manifestHash); |
264 | 0 | } |
265 | 0 |
|
266 | 0 | mUpdate->ManifestCheckCompleted(aStatus, manifestHash); |
267 | 0 |
|
268 | 0 | return NS_OK; |
269 | 0 | } |
270 | | |
271 | | //----------------------------------------------------------------------------- |
272 | | // nsManifestCheck::nsIInterfaceRequestor |
273 | | //----------------------------------------------------------------------------- |
274 | | |
275 | | NS_IMETHODIMP |
276 | | nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult) |
277 | 0 | { |
278 | 0 | if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { |
279 | 0 | NS_ADDREF_THIS(); |
280 | 0 | *aResult = static_cast<nsIChannelEventSink *>(this); |
281 | 0 | return NS_OK; |
282 | 0 | } |
283 | 0 |
|
284 | 0 | return NS_ERROR_NO_INTERFACE; |
285 | 0 | } |
286 | | |
287 | | //----------------------------------------------------------------------------- |
288 | | // nsManifestCheck::nsIChannelEventSink |
289 | | //----------------------------------------------------------------------------- |
290 | | |
291 | | NS_IMETHODIMP |
292 | | nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel, |
293 | | nsIChannel *aNewChannel, |
294 | | uint32_t aFlags, |
295 | | nsIAsyncVerifyRedirectCallback *callback) |
296 | 0 | { |
297 | 0 | // Redirects should cause the load (and therefore the update) to fail. |
298 | 0 | if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) { |
299 | 0 | callback->OnRedirectVerifyCallback(NS_OK); |
300 | 0 | return NS_OK; |
301 | 0 | } |
302 | 0 | |
303 | 0 | LogToConsole("Manifest check failed because its response is a redirect"); |
304 | 0 |
|
305 | 0 | aOldChannel->Cancel(NS_ERROR_ABORT); |
306 | 0 | return NS_ERROR_ABORT; |
307 | 0 | } |
308 | | |
309 | | //----------------------------------------------------------------------------- |
310 | | // nsOfflineCacheUpdateItem::nsISupports |
311 | | //----------------------------------------------------------------------------- |
312 | | |
313 | | NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateItem, |
314 | | nsIRequestObserver, |
315 | | nsIStreamListener, |
316 | | nsIRunnable, |
317 | | nsIInterfaceRequestor, |
318 | | nsIChannelEventSink) |
319 | | |
320 | | //----------------------------------------------------------------------------- |
321 | | // nsOfflineCacheUpdateItem <public> |
322 | | //----------------------------------------------------------------------------- |
323 | | |
324 | | nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsIURI *aURI, |
325 | | nsIURI *aReferrerURI, |
326 | | nsIPrincipal* aLoadingPrincipal, |
327 | | nsIApplicationCache *aApplicationCache, |
328 | | nsIApplicationCache *aPreviousApplicationCache, |
329 | | uint32_t type, |
330 | | uint32_t loadFlags) |
331 | | : mURI(aURI) |
332 | | , mReferrerURI(aReferrerURI) |
333 | | , mLoadingPrincipal(aLoadingPrincipal) |
334 | | , mApplicationCache(aApplicationCache) |
335 | | , mPreviousApplicationCache(aPreviousApplicationCache) |
336 | | , mItemType(type) |
337 | | , mLoadFlags(loadFlags) |
338 | | , mChannel(nullptr) |
339 | | , mState(LoadStatus::UNINITIALIZED) |
340 | | , mBytesRead(0) |
341 | 0 | { |
342 | 0 | } |
343 | | |
344 | | nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem() |
345 | 0 | { |
346 | 0 | } |
347 | | |
348 | | nsresult |
349 | | nsOfflineCacheUpdateItem::OpenChannel(nsOfflineCacheUpdate *aUpdate) |
350 | 0 | { |
351 | 0 | if (LOG_ENABLED()) { |
352 | 0 | LOG(("%p: Opening channel for %s", this, |
353 | 0 | mURI->GetSpecOrDefault().get())); |
354 | 0 | } |
355 | 0 |
|
356 | 0 | if (mUpdate) { |
357 | 0 | // Holding a reference to the update means this item is already |
358 | 0 | // in progress (has a channel, or is just in between OnStopRequest() |
359 | 0 | // and its Run() call. We must never open channel on this item again. |
360 | 0 | LOG((" %p is already running! ignoring", this)); |
361 | 0 | return NS_ERROR_ALREADY_OPENED; |
362 | 0 | } |
363 | 0 |
|
364 | 0 | nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey); |
365 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
366 | 0 |
|
367 | 0 | uint32_t flags = nsIRequest::LOAD_BACKGROUND | |
368 | 0 | nsICachingChannel::LOAD_ONLY_IF_MODIFIED; |
369 | 0 |
|
370 | 0 | if (mApplicationCache == mPreviousApplicationCache) { |
371 | 0 | // Same app cache to read from and to write to is used during |
372 | 0 | // an only-update-check procedure. Here we protect the existing |
373 | 0 | // cache from being modified. |
374 | 0 | flags |= nsIRequest::INHIBIT_CACHING; |
375 | 0 | } |
376 | 0 |
|
377 | 0 | flags |= mLoadFlags; |
378 | 0 |
|
379 | 0 | rv = NS_NewChannel(getter_AddRefs(mChannel), |
380 | 0 | mURI, |
381 | 0 | mLoadingPrincipal, |
382 | 0 | nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, |
383 | 0 | nsIContentPolicy::TYPE_OTHER, |
384 | 0 | nullptr, // PerformanceStorage |
385 | 0 | nullptr, // aLoadGroup |
386 | 0 | this, // aCallbacks |
387 | 0 | flags); |
388 | 0 |
|
389 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
390 | 0 |
|
391 | 0 | nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = |
392 | 0 | do_QueryInterface(mChannel, &rv); |
393 | 0 |
|
394 | 0 | // Support for nsIApplicationCacheChannel is required. |
395 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
396 | 0 |
|
397 | 0 | // Use the existing application cache as the cache to check. |
398 | 0 | rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache); |
399 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
400 | 0 |
|
401 | 0 | // Set the new application cache as the target for write. |
402 | 0 | rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache); |
403 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
404 | 0 |
|
405 | 0 | // configure HTTP specific stuff |
406 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = |
407 | 0 | do_QueryInterface(mChannel); |
408 | 0 | if (httpChannel) { |
409 | 0 | rv = httpChannel->SetReferrer(mReferrerURI); |
410 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
411 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), |
412 | 0 | NS_LITERAL_CSTRING("offline-resource"), |
413 | 0 | false); |
414 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
415 | 0 | } |
416 | 0 |
|
417 | 0 | rv = mChannel->AsyncOpen2(this); |
418 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
419 | 0 |
|
420 | 0 | mUpdate = aUpdate; |
421 | 0 |
|
422 | 0 | mState = LoadStatus::REQUESTED; |
423 | 0 |
|
424 | 0 | return NS_OK; |
425 | 0 | } |
426 | | |
427 | | nsresult |
428 | | nsOfflineCacheUpdateItem::Cancel() |
429 | 0 | { |
430 | 0 | if (mChannel) { |
431 | 0 | mChannel->Cancel(NS_ERROR_ABORT); |
432 | 0 | mChannel = nullptr; |
433 | 0 | } |
434 | 0 |
|
435 | 0 | mState = LoadStatus::UNINITIALIZED; |
436 | 0 |
|
437 | 0 | return NS_OK; |
438 | 0 | } |
439 | | |
440 | | //----------------------------------------------------------------------------- |
441 | | // nsOfflineCacheUpdateItem::nsIStreamListener |
442 | | //----------------------------------------------------------------------------- |
443 | | |
444 | | NS_IMETHODIMP |
445 | | nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest, |
446 | | nsISupports *aContext) |
447 | 0 | { |
448 | 0 | mState = LoadStatus::RECEIVING; |
449 | 0 |
|
450 | 0 | return NS_OK; |
451 | 0 | } |
452 | | |
453 | | NS_IMETHODIMP |
454 | | nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest, |
455 | | nsISupports *aContext, |
456 | | nsIInputStream *aStream, |
457 | | uint64_t aOffset, |
458 | | uint32_t aCount) |
459 | 0 | { |
460 | 0 | uint32_t bytesRead = 0; |
461 | 0 | aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead); |
462 | 0 | mBytesRead += bytesRead; |
463 | 0 | LOG(("loaded %u bytes into offline cache [offset=%" PRIu64 "]\n", |
464 | 0 | bytesRead, aOffset)); |
465 | 0 |
|
466 | 0 | mUpdate->OnByteProgress(bytesRead); |
467 | 0 |
|
468 | 0 | return NS_OK; |
469 | 0 | } |
470 | | |
471 | | NS_IMETHODIMP |
472 | | nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest, |
473 | | nsISupports *aContext, |
474 | | nsresult aStatus) |
475 | 0 | { |
476 | 0 | if (LOG_ENABLED()) { |
477 | 0 | LOG(("%p: Done fetching offline item %s [status=%" PRIx32 "]\n", |
478 | 0 | this, mURI->GetSpecOrDefault().get(), static_cast<uint32_t>(aStatus))); |
479 | 0 | } |
480 | 0 |
|
481 | 0 | if (mBytesRead == 0 && aStatus == NS_OK) { |
482 | 0 | // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was |
483 | 0 | // specified), but the object should report loadedSize as if it |
484 | 0 | // did. |
485 | 0 | mChannel->GetContentLength(&mBytesRead); |
486 | 0 | mUpdate->OnByteProgress(mBytesRead); |
487 | 0 | } |
488 | 0 |
|
489 | 0 | if (NS_FAILED(aStatus)) { |
490 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); |
491 | 0 | if (httpChannel) { |
492 | 0 | bool isNoStore; |
493 | 0 | if (NS_SUCCEEDED(httpChannel->IsNoStoreResponse(&isNoStore)) |
494 | 0 | && isNoStore) { |
495 | 0 | LogToConsole("Offline cache manifest item has Cache-control: no-store header", |
496 | 0 | this); |
497 | 0 | } |
498 | 0 | } |
499 | 0 | } |
500 | 0 |
|
501 | 0 | // We need to notify the update that the load is complete, but we |
502 | 0 | // want to give the channel a chance to close the cache entries. |
503 | 0 | NS_DispatchToCurrentThread(this); |
504 | 0 |
|
505 | 0 | return NS_OK; |
506 | 0 | } |
507 | | |
508 | | //----------------------------------------------------------------------------- |
509 | | // nsOfflineCacheUpdateItem::nsIRunnable |
510 | | //----------------------------------------------------------------------------- |
511 | | NS_IMETHODIMP |
512 | | nsOfflineCacheUpdateItem::Run() |
513 | 0 | { |
514 | 0 | // Set mState to LOADED here rather than in OnStopRequest to prevent |
515 | 0 | // race condition when checking state of all mItems in ProcessNextURI(). |
516 | 0 | // If state would have been set in OnStopRequest we could mistakenly |
517 | 0 | // take this item as already finished and finish the update process too |
518 | 0 | // early when ProcessNextURI() would get called between OnStopRequest() |
519 | 0 | // and Run() of this item. Finish() would then have been called twice. |
520 | 0 | mState = LoadStatus::LOADED; |
521 | 0 |
|
522 | 0 | RefPtr<nsOfflineCacheUpdate> update; |
523 | 0 | update.swap(mUpdate); |
524 | 0 | update->LoadCompleted(this); |
525 | 0 |
|
526 | 0 | return NS_OK; |
527 | 0 | } |
528 | | |
529 | | //----------------------------------------------------------------------------- |
530 | | // nsOfflineCacheUpdateItem::nsIInterfaceRequestor |
531 | | //----------------------------------------------------------------------------- |
532 | | |
533 | | NS_IMETHODIMP |
534 | | nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult) |
535 | 0 | { |
536 | 0 | if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { |
537 | 0 | NS_ADDREF_THIS(); |
538 | 0 | *aResult = static_cast<nsIChannelEventSink *>(this); |
539 | 0 | return NS_OK; |
540 | 0 | } |
541 | 0 |
|
542 | 0 | return NS_ERROR_NO_INTERFACE; |
543 | 0 | } |
544 | | |
545 | | //----------------------------------------------------------------------------- |
546 | | // nsOfflineCacheUpdateItem::nsIChannelEventSink |
547 | | //----------------------------------------------------------------------------- |
548 | | |
549 | | NS_IMETHODIMP |
550 | | nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel, |
551 | | nsIChannel *aNewChannel, |
552 | | uint32_t aFlags, |
553 | | nsIAsyncVerifyRedirectCallback *cb) |
554 | 0 | { |
555 | 0 | if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { |
556 | 0 | // Don't allow redirect in case of non-internal redirect and cancel |
557 | 0 | // the channel to clean the cache entry. |
558 | 0 | LogToConsole("Offline cache manifest failed because an item redirects", this); |
559 | 0 |
|
560 | 0 | aOldChannel->Cancel(NS_ERROR_ABORT); |
561 | 0 | return NS_ERROR_ABORT; |
562 | 0 | } |
563 | 0 | |
564 | 0 | nsCOMPtr<nsIURI> newURI; |
565 | 0 | nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI)); |
566 | 0 | if (NS_FAILED(rv)) |
567 | 0 | return rv; |
568 | 0 | |
569 | 0 | nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = |
570 | 0 | do_QueryInterface(aNewChannel); |
571 | 0 | if (appCacheChannel) { |
572 | 0 | rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache); |
573 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
574 | 0 | } |
575 | 0 |
|
576 | 0 | nsAutoCString oldScheme; |
577 | 0 | mURI->GetScheme(oldScheme); |
578 | 0 |
|
579 | 0 | bool match; |
580 | 0 | if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) { |
581 | 0 | LOG(("rejected: redirected to a different scheme\n")); |
582 | 0 | return NS_ERROR_ABORT; |
583 | 0 | } |
584 | 0 |
|
585 | 0 | // HTTP request headers are not automatically forwarded to the new channel. |
586 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel); |
587 | 0 | NS_ENSURE_STATE(httpChannel); |
588 | 0 |
|
589 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), |
590 | 0 | NS_LITERAL_CSTRING("offline-resource"), |
591 | 0 | false); |
592 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
593 | 0 |
|
594 | 0 | mChannel = aNewChannel; |
595 | 0 |
|
596 | 0 | cb->OnRedirectVerifyCallback(NS_OK); |
597 | 0 | return NS_OK; |
598 | 0 | } |
599 | | |
600 | | nsresult |
601 | | nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded) |
602 | 0 | { |
603 | 0 | *succeeded = false; |
604 | 0 |
|
605 | 0 | if (!mChannel) |
606 | 0 | return NS_OK; |
607 | 0 | |
608 | 0 | nsresult rv; |
609 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv); |
610 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
611 | 0 |
|
612 | 0 | bool reqSucceeded; |
613 | 0 | rv = httpChannel->GetRequestSucceeded(&reqSucceeded); |
614 | 0 | if (NS_ERROR_NOT_AVAILABLE == rv) |
615 | 0 | return NS_OK; |
616 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
617 | 0 |
|
618 | 0 | if (!reqSucceeded) { |
619 | 0 | LOG(("Request failed")); |
620 | 0 | return NS_OK; |
621 | 0 | } |
622 | 0 |
|
623 | 0 | nsresult channelStatus; |
624 | 0 | rv = httpChannel->GetStatus(&channelStatus); |
625 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
626 | 0 |
|
627 | 0 | if (NS_FAILED(channelStatus)) { |
628 | 0 | LOG(("Channel status=0x%08" PRIx32, static_cast<uint32_t>(channelStatus))); |
629 | 0 | return NS_OK; |
630 | 0 | } |
631 | 0 |
|
632 | 0 | *succeeded = true; |
633 | 0 | return NS_OK; |
634 | 0 | } |
635 | | |
636 | | bool |
637 | | nsOfflineCacheUpdateItem::IsScheduled() |
638 | 0 | { |
639 | 0 | return mState == LoadStatus::UNINITIALIZED; |
640 | 0 | } |
641 | | |
642 | | bool |
643 | | nsOfflineCacheUpdateItem::IsInProgress() |
644 | 0 | { |
645 | 0 | return mState == LoadStatus::REQUESTED || |
646 | 0 | mState == LoadStatus::RECEIVING; |
647 | 0 | } |
648 | | |
649 | | bool |
650 | | nsOfflineCacheUpdateItem::IsCompleted() |
651 | 0 | { |
652 | 0 | return mState == LoadStatus::LOADED; |
653 | 0 | } |
654 | | |
655 | | nsresult |
656 | | nsOfflineCacheUpdateItem::GetStatus(uint16_t *aStatus) |
657 | 0 | { |
658 | 0 | if (!mChannel) { |
659 | 0 | *aStatus = 0; |
660 | 0 | return NS_OK; |
661 | 0 | } |
662 | 0 | |
663 | 0 | nsresult rv; |
664 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv); |
665 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
666 | 0 |
|
667 | 0 | uint32_t httpStatus; |
668 | 0 | rv = httpChannel->GetResponseStatus(&httpStatus); |
669 | 0 | if (rv == NS_ERROR_NOT_AVAILABLE) { |
670 | 0 | *aStatus = 0; |
671 | 0 | return NS_OK; |
672 | 0 | } |
673 | 0 | |
674 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
675 | 0 | *aStatus = uint16_t(httpStatus); |
676 | 0 | return NS_OK; |
677 | 0 | } |
678 | | |
679 | | //----------------------------------------------------------------------------- |
680 | | // nsOfflineManifestItem |
681 | | //----------------------------------------------------------------------------- |
682 | | |
683 | | //----------------------------------------------------------------------------- |
684 | | // nsOfflineManifestItem <public> |
685 | | //----------------------------------------------------------------------------- |
686 | | |
687 | | nsOfflineManifestItem::nsOfflineManifestItem(nsIURI *aURI, |
688 | | nsIURI *aReferrerURI, |
689 | | nsIPrincipal* aLoadingPrincipal, |
690 | | nsIApplicationCache *aApplicationCache, |
691 | | nsIApplicationCache *aPreviousApplicationCache) |
692 | | : nsOfflineCacheUpdateItem(aURI, aReferrerURI, aLoadingPrincipal, |
693 | | aApplicationCache, aPreviousApplicationCache, |
694 | | nsIApplicationCache::ITEM_MANIFEST, 0) |
695 | | , mParserState(PARSE_INIT) |
696 | | , mNeedsUpdate(true) |
697 | | , mStrictFileOriginPolicy(false) |
698 | | , mManifestHashInitialized(false) |
699 | 0 | { |
700 | 0 | ReadStrictFileOriginPolicyPref(); |
701 | 0 | } |
702 | | |
703 | | nsOfflineManifestItem::~nsOfflineManifestItem() |
704 | 0 | { |
705 | 0 | } |
706 | | |
707 | | //----------------------------------------------------------------------------- |
708 | | // nsOfflineManifestItem <private> |
709 | | //----------------------------------------------------------------------------- |
710 | | |
711 | | /* static */ |
712 | | nsresult |
713 | | nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream, |
714 | | void *aClosure, |
715 | | const char *aFromSegment, |
716 | | uint32_t aOffset, |
717 | | uint32_t aCount, |
718 | | uint32_t *aBytesConsumed) |
719 | 0 | { |
720 | 0 | nsOfflineManifestItem *manifest = |
721 | 0 | static_cast<nsOfflineManifestItem*>(aClosure); |
722 | 0 |
|
723 | 0 | nsresult rv; |
724 | 0 |
|
725 | 0 | *aBytesConsumed = aCount; |
726 | 0 |
|
727 | 0 | if (manifest->mParserState == PARSE_ERROR) { |
728 | 0 | // parse already failed, ignore this |
729 | 0 | return NS_OK; |
730 | 0 | } |
731 | 0 | |
732 | 0 | if (!manifest->mManifestHashInitialized) { |
733 | 0 | // Avoid re-creation of crypto hash when it fails from some reason the first time |
734 | 0 | manifest->mManifestHashInitialized = true; |
735 | 0 |
|
736 | 0 | manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); |
737 | 0 | if (NS_SUCCEEDED(rv)) { |
738 | 0 | rv = manifest->mManifestHash->Init(nsICryptoHash::MD5); |
739 | 0 | if (NS_FAILED(rv)) { |
740 | 0 | manifest->mManifestHash = nullptr; |
741 | 0 | LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08" PRIx32, |
742 | 0 | static_cast<uint32_t>(rv))); |
743 | 0 | } |
744 | 0 | } |
745 | 0 | } |
746 | 0 |
|
747 | 0 | if (manifest->mManifestHash) { |
748 | 0 | rv = manifest->mManifestHash->Update(reinterpret_cast<const uint8_t *>(aFromSegment), aCount); |
749 | 0 | if (NS_FAILED(rv)) { |
750 | 0 | manifest->mManifestHash = nullptr; |
751 | 0 | LOG(("Could not update manifest hash, rv=%08" PRIx32, static_cast<uint32_t>(rv))); |
752 | 0 | } |
753 | 0 | } |
754 | 0 |
|
755 | 0 | manifest->mReadBuf.Append(aFromSegment, aCount); |
756 | 0 |
|
757 | 0 | nsCString::const_iterator begin, iter, end; |
758 | 0 | manifest->mReadBuf.BeginReading(begin); |
759 | 0 | manifest->mReadBuf.EndReading(end); |
760 | 0 |
|
761 | 0 | for (iter = begin; iter != end; iter++) { |
762 | 0 | if (*iter == '\r' || *iter == '\n') { |
763 | 0 | rv = manifest->HandleManifestLine(begin, iter); |
764 | 0 |
|
765 | 0 | if (NS_FAILED(rv)) { |
766 | 0 | LOG(("HandleManifestLine failed with 0x%08" PRIx32, static_cast<uint32_t>(rv))); |
767 | 0 | *aBytesConsumed = 0; // Avoid assertion failure in stream tee |
768 | 0 | return NS_ERROR_ABORT; |
769 | 0 | } |
770 | 0 |
|
771 | 0 | begin = iter; |
772 | 0 | begin++; |
773 | 0 | } |
774 | 0 | } |
775 | 0 |
|
776 | 0 | // any leftovers are saved for next time |
777 | 0 | manifest->mReadBuf = Substring(begin, end); |
778 | 0 |
|
779 | 0 | return NS_OK; |
780 | 0 | } |
781 | | |
782 | | nsresult |
783 | | nsOfflineManifestItem::AddNamespace(uint32_t namespaceType, |
784 | | const nsCString &namespaceSpec, |
785 | | const nsCString &data) |
786 | | |
787 | 0 | { |
788 | 0 | nsresult rv; |
789 | 0 | if (!mNamespaces) { |
790 | 0 | mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); |
791 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
792 | 0 | } |
793 | 0 |
|
794 | 0 | nsCOMPtr<nsIApplicationCacheNamespace> ns = |
795 | 0 | new nsApplicationCacheNamespace(); |
796 | 0 |
|
797 | 0 | rv = ns->Init(namespaceType, namespaceSpec, data); |
798 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
799 | 0 |
|
800 | 0 | rv = mNamespaces->AppendElement(ns); |
801 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
802 | 0 |
|
803 | 0 | return NS_OK; |
804 | 0 | } |
805 | | |
806 | | static nsresult |
807 | | GetURIDirectory(nsIURI* uri, nsACString &directory) |
808 | 0 | { |
809 | 0 | nsresult rv; |
810 | 0 |
|
811 | 0 | nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv)); |
812 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
813 | 0 |
|
814 | 0 | rv = url->GetDirectory(directory); |
815 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
816 | 0 |
|
817 | 0 | return NS_OK; |
818 | 0 | } |
819 | | |
820 | | static nsresult |
821 | | CheckFileContainedInPath(nsIURI* file, nsACString const &masterDirectory) |
822 | 0 | { |
823 | 0 | nsresult rv; |
824 | 0 |
|
825 | 0 | nsAutoCString directory; |
826 | 0 | rv = GetURIDirectory(file, directory); |
827 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
828 | 0 |
|
829 | 0 | bool contains = StringBeginsWith(directory, masterDirectory); |
830 | 0 | if (!contains) { |
831 | 0 | return NS_ERROR_DOM_BAD_URI; |
832 | 0 | } |
833 | 0 | |
834 | 0 | return NS_OK; |
835 | 0 | } |
836 | | |
837 | | nsresult |
838 | | nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin, |
839 | | const nsCString::const_iterator &aEnd) |
840 | 0 | { |
841 | 0 | nsCString::const_iterator begin = aBegin; |
842 | 0 | nsCString::const_iterator end = aEnd; |
843 | 0 |
|
844 | 0 | // all lines ignore trailing spaces and tabs |
845 | 0 | nsCString::const_iterator last = end; |
846 | 0 | --last; |
847 | 0 | while (end != begin && (*last == ' ' || *last == '\t')) { |
848 | 0 | --end; |
849 | 0 | --last; |
850 | 0 | } |
851 | 0 |
|
852 | 0 | if (mParserState == PARSE_INIT) { |
853 | 0 | // Allow a UTF-8 BOM |
854 | 0 | if (begin != end && static_cast<unsigned char>(*begin) == 0xef) { |
855 | 0 | if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb || |
856 | 0 | ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) { |
857 | 0 | mParserState = PARSE_ERROR; |
858 | 0 | LogToConsole("Offline cache manifest BOM error", this); |
859 | 0 | return NS_OK; |
860 | 0 | } |
861 | 0 | ++begin; |
862 | 0 | } |
863 | 0 |
|
864 | 0 | const nsACString& magic = Substring(begin, end); |
865 | 0 |
|
866 | 0 | if (!magic.EqualsLiteral("CACHE MANIFEST")) { |
867 | 0 | mParserState = PARSE_ERROR; |
868 | 0 | LogToConsole("Offline cache manifest magic incorrect", this); |
869 | 0 | return NS_OK; |
870 | 0 | } |
871 | 0 | |
872 | 0 | mParserState = PARSE_CACHE_ENTRIES; |
873 | 0 | return NS_OK; |
874 | 0 | } |
875 | 0 | |
876 | 0 | // lines other than the first ignore leading spaces and tabs |
877 | 0 | while (begin != end && (*begin == ' ' || *begin == '\t')) |
878 | 0 | begin++; |
879 | 0 |
|
880 | 0 | // ignore blank lines and comments |
881 | 0 | if (begin == end || *begin == '#') |
882 | 0 | return NS_OK; |
883 | 0 | |
884 | 0 | const nsACString& line = Substring(begin, end); |
885 | 0 |
|
886 | 0 | if (line.EqualsLiteral("CACHE:")) { |
887 | 0 | mParserState = PARSE_CACHE_ENTRIES; |
888 | 0 | return NS_OK; |
889 | 0 | } |
890 | 0 | |
891 | 0 | if (line.EqualsLiteral("FALLBACK:")) { |
892 | 0 | mParserState = PARSE_FALLBACK_ENTRIES; |
893 | 0 | return NS_OK; |
894 | 0 | } |
895 | 0 | |
896 | 0 | if (line.EqualsLiteral("NETWORK:")) { |
897 | 0 | mParserState = PARSE_BYPASS_ENTRIES; |
898 | 0 | return NS_OK; |
899 | 0 | } |
900 | 0 | |
901 | 0 | // Every other section type we don't know must be silently ignored. |
902 | 0 | nsCString::const_iterator lastChar = end; |
903 | 0 | if (*(--lastChar) == ':') { |
904 | 0 | mParserState = PARSE_UNKNOWN_SECTION; |
905 | 0 | return NS_OK; |
906 | 0 | } |
907 | 0 | |
908 | 0 | nsresult rv; |
909 | 0 |
|
910 | 0 | switch(mParserState) { |
911 | 0 | case PARSE_INIT: |
912 | 0 | case PARSE_ERROR: { |
913 | 0 | // this should have been dealt with earlier |
914 | 0 | return NS_ERROR_FAILURE; |
915 | 0 | } |
916 | 0 |
|
917 | 0 | case PARSE_UNKNOWN_SECTION: { |
918 | 0 | // just jump over |
919 | 0 | return NS_OK; |
920 | 0 | } |
921 | 0 |
|
922 | 0 | case PARSE_CACHE_ENTRIES: { |
923 | 0 | nsCOMPtr<nsIURI> uri; |
924 | 0 | rv = NS_NewURI(getter_AddRefs(uri), line, nullptr, mURI); |
925 | 0 | if (NS_FAILED(rv)) |
926 | 0 | break; |
927 | 0 | if (NS_FAILED(DropReferenceFromURL(uri))) |
928 | 0 | break; |
929 | 0 | |
930 | 0 | nsAutoCString scheme; |
931 | 0 | uri->GetScheme(scheme); |
932 | 0 |
|
933 | 0 | // Manifest URIs must have the same scheme as the manifest. |
934 | 0 | bool match; |
935 | 0 | if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match) |
936 | 0 | break; |
937 | 0 | |
938 | 0 | mExplicitURIs.AppendObject(uri); |
939 | 0 |
|
940 | 0 | if (!NS_SecurityCompareURIs(mURI, uri, |
941 | 0 | mStrictFileOriginPolicy)) { |
942 | 0 | mAnonymousURIs.AppendObject(uri); |
943 | 0 | } |
944 | 0 |
|
945 | 0 | break; |
946 | 0 | } |
947 | 0 |
|
948 | 0 | case PARSE_FALLBACK_ENTRIES: { |
949 | 0 | int32_t separator = line.FindChar(' '); |
950 | 0 | if (separator == kNotFound) { |
951 | 0 | separator = line.FindChar('\t'); |
952 | 0 | if (separator == kNotFound) |
953 | 0 | break; |
954 | 0 | } |
955 | 0 | |
956 | 0 | nsCString namespaceSpec(Substring(line, 0, separator)); |
957 | 0 | nsCString fallbackSpec(Substring(line, separator + 1)); |
958 | 0 | namespaceSpec.CompressWhitespace(); |
959 | 0 | fallbackSpec.CompressWhitespace(); |
960 | 0 |
|
961 | 0 | nsCOMPtr<nsIURI> namespaceURI; |
962 | 0 | rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nullptr, mURI); |
963 | 0 | if (NS_FAILED(rv)) |
964 | 0 | break; |
965 | 0 | if (NS_FAILED(DropReferenceFromURL(namespaceURI))) |
966 | 0 | break; |
967 | 0 | rv = namespaceURI->GetAsciiSpec(namespaceSpec); |
968 | 0 | if (NS_FAILED(rv)) |
969 | 0 | break; |
970 | 0 | |
971 | 0 | nsCOMPtr<nsIURI> fallbackURI; |
972 | 0 | rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nullptr, mURI); |
973 | 0 | if (NS_FAILED(rv)) |
974 | 0 | break; |
975 | 0 | if (NS_FAILED(DropReferenceFromURL(fallbackURI))) |
976 | 0 | break; |
977 | 0 | rv = fallbackURI->GetAsciiSpec(fallbackSpec); |
978 | 0 | if (NS_FAILED(rv)) |
979 | 0 | break; |
980 | 0 | |
981 | 0 | // The following set of checks is preventing a website under |
982 | 0 | // a subdirectory to add fallback pages for the whole origin |
983 | 0 | // (or a parent directory) to prevent fallback attacks. |
984 | 0 | nsAutoCString manifestDirectory; |
985 | 0 | rv = GetURIDirectory(mURI, manifestDirectory); |
986 | 0 | if (NS_FAILED(rv)) { |
987 | 0 | break; |
988 | 0 | } |
989 | 0 | |
990 | 0 | rv = CheckFileContainedInPath(namespaceURI, manifestDirectory); |
991 | 0 | if (NS_FAILED(rv)) { |
992 | 0 | break; |
993 | 0 | } |
994 | 0 | |
995 | 0 | rv = CheckFileContainedInPath(fallbackURI, manifestDirectory); |
996 | 0 | if (NS_FAILED(rv)) { |
997 | 0 | break; |
998 | 0 | } |
999 | 0 | |
1000 | 0 | // Manifest and namespace must be same origin |
1001 | 0 | if (!NS_SecurityCompareURIs(mURI, namespaceURI, |
1002 | 0 | mStrictFileOriginPolicy)) |
1003 | 0 | break; |
1004 | 0 | |
1005 | 0 | // Fallback and namespace must be same origin |
1006 | 0 | if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI, |
1007 | 0 | mStrictFileOriginPolicy)) |
1008 | 0 | break; |
1009 | 0 | |
1010 | 0 | mFallbackURIs.AppendObject(fallbackURI); |
1011 | 0 |
|
1012 | 0 | AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK, |
1013 | 0 | namespaceSpec, fallbackSpec); |
1014 | 0 | break; |
1015 | 0 | } |
1016 | 0 |
|
1017 | 0 | case PARSE_BYPASS_ENTRIES: { |
1018 | 0 | if (line[0] == '*' && (line.Length() == 1 || line[1] == ' ' || line[1] == '\t')) |
1019 | 0 | { |
1020 | 0 | // '*' indicates to make the online whitelist wildcard flag open, |
1021 | 0 | // i.e. do allow load of resources not present in the offline cache |
1022 | 0 | // or not conforming any namespace. |
1023 | 0 | // We achive that simply by adding an 'empty' - i.e. universal |
1024 | 0 | // namespace of BYPASS type into the cache. |
1025 | 0 | AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS, |
1026 | 0 | EmptyCString(), EmptyCString()); |
1027 | 0 | break; |
1028 | 0 | } |
1029 | 0 | |
1030 | 0 | nsCOMPtr<nsIURI> bypassURI; |
1031 | 0 | rv = NS_NewURI(getter_AddRefs(bypassURI), line, nullptr, mURI); |
1032 | 0 | if (NS_FAILED(rv)) |
1033 | 0 | break; |
1034 | 0 | |
1035 | 0 | nsAutoCString scheme; |
1036 | 0 | bypassURI->GetScheme(scheme); |
1037 | 0 | bool equals; |
1038 | 0 | if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals) |
1039 | 0 | break; |
1040 | 0 | if (NS_FAILED(DropReferenceFromURL(bypassURI))) |
1041 | 0 | break; |
1042 | 0 | nsCString spec; |
1043 | 0 | if (NS_FAILED(bypassURI->GetAsciiSpec(spec))) |
1044 | 0 | break; |
1045 | 0 | |
1046 | 0 | AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS, |
1047 | 0 | spec, EmptyCString()); |
1048 | 0 | break; |
1049 | 0 | } |
1050 | 0 | } |
1051 | 0 | |
1052 | 0 | return NS_OK; |
1053 | 0 | } |
1054 | | |
1055 | | nsresult |
1056 | | nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest) |
1057 | 0 | { |
1058 | 0 | nsresult rv; |
1059 | 0 |
|
1060 | 0 | nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv); |
1061 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1062 | 0 |
|
1063 | 0 | // load the main cache token that is actually the old offline cache token and |
1064 | 0 | // read previous manifest content hash value |
1065 | 0 | nsCOMPtr<nsISupports> cacheToken; |
1066 | 0 | cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); |
1067 | 0 | if (cacheToken) { |
1068 | 0 | nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv)); |
1069 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1070 | 0 |
|
1071 | 0 | rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue)); |
1072 | 0 | if (NS_FAILED(rv)) |
1073 | 0 | mOldManifestHashValue.Truncate(); |
1074 | 0 | } |
1075 | 0 |
|
1076 | 0 | return NS_OK; |
1077 | 0 | } |
1078 | | |
1079 | | nsresult |
1080 | | nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest) |
1081 | 0 | { |
1082 | 0 | nsresult rv; |
1083 | 0 |
|
1084 | 0 | if (!mManifestHash) { |
1085 | 0 | // Nothing to compare against... |
1086 | 0 | return NS_OK; |
1087 | 0 | } |
1088 | 0 | |
1089 | 0 | nsCString newManifestHashValue; |
1090 | 0 | rv = mManifestHash->Finish(true, mManifestHashValue); |
1091 | 0 | mManifestHash = nullptr; |
1092 | 0 |
|
1093 | 0 | if (NS_FAILED(rv)) { |
1094 | 0 | LOG(("Could not finish manifest hash, rv=%08" PRIx32, static_cast<uint32_t>(rv))); |
1095 | 0 | // This is not critical error |
1096 | 0 | return NS_OK; |
1097 | 0 | } |
1098 | 0 |
|
1099 | 0 | if (!ParseSucceeded()) { |
1100 | 0 | // Parsing failed, the hash is not valid |
1101 | 0 | return NS_OK; |
1102 | 0 | } |
1103 | 0 | |
1104 | 0 | if (mOldManifestHashValue == mManifestHashValue) { |
1105 | 0 | LOG(("Update not needed, downloaded manifest content is byte-for-byte identical")); |
1106 | 0 | mNeedsUpdate = false; |
1107 | 0 | } |
1108 | 0 |
|
1109 | 0 | // Store the manifest content hash value to the new |
1110 | 0 | // offline cache token |
1111 | 0 | nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv); |
1112 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1113 | 0 |
|
1114 | 0 | nsCOMPtr<nsISupports> cacheToken; |
1115 | 0 | cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken)); |
1116 | 0 | if (cacheToken) { |
1117 | 0 | nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv)); |
1118 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1119 | 0 |
|
1120 | 0 | rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get()); |
1121 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1122 | 0 | } |
1123 | 0 |
|
1124 | 0 | return NS_OK; |
1125 | 0 | } |
1126 | | |
1127 | | void |
1128 | | nsOfflineManifestItem::ReadStrictFileOriginPolicyPref() |
1129 | 0 | { |
1130 | 0 | mStrictFileOriginPolicy = |
1131 | 0 | Preferences::GetBool("security.fileuri.strict_origin_policy", true); |
1132 | 0 | } |
1133 | | |
1134 | | NS_IMETHODIMP |
1135 | | nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest, |
1136 | | nsISupports *aContext) |
1137 | 0 | { |
1138 | 0 | nsresult rv; |
1139 | 0 |
|
1140 | 0 | nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv); |
1141 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1142 | 0 |
|
1143 | 0 | bool succeeded; |
1144 | 0 | rv = channel->GetRequestSucceeded(&succeeded); |
1145 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1146 | 0 |
|
1147 | 0 | if (!succeeded) { |
1148 | 0 | LOG(("HTTP request failed")); |
1149 | 0 | LogToConsole("Offline cache manifest HTTP request failed", this); |
1150 | 0 | mParserState = PARSE_ERROR; |
1151 | 0 | return NS_ERROR_ABORT; |
1152 | 0 | } |
1153 | 0 |
|
1154 | 0 | rv = GetOldManifestContentHash(aRequest); |
1155 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1156 | 0 |
|
1157 | 0 | return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext); |
1158 | 0 | } |
1159 | | |
1160 | | NS_IMETHODIMP |
1161 | | nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest, |
1162 | | nsISupports *aContext, |
1163 | | nsIInputStream *aStream, |
1164 | | uint64_t aOffset, |
1165 | | uint32_t aCount) |
1166 | 0 | { |
1167 | 0 | uint32_t bytesRead = 0; |
1168 | 0 | aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead); |
1169 | 0 | mBytesRead += bytesRead; |
1170 | 0 |
|
1171 | 0 | if (mParserState == PARSE_ERROR) { |
1172 | 0 | LOG(("OnDataAvailable is canceling the request due a parse error\n")); |
1173 | 0 | return NS_ERROR_ABORT; |
1174 | 0 | } |
1175 | 0 |
|
1176 | 0 | LOG(("loaded %u bytes into offline cache [offset=%" PRIu64 "]\n", |
1177 | 0 | bytesRead, aOffset)); |
1178 | 0 |
|
1179 | 0 | // All the parent method does is read and discard, don't bother |
1180 | 0 | // chaining up. |
1181 | 0 |
|
1182 | 0 | return NS_OK; |
1183 | 0 | } |
1184 | | |
1185 | | NS_IMETHODIMP |
1186 | | nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest, |
1187 | | nsISupports *aContext, |
1188 | | nsresult aStatus) |
1189 | 0 | { |
1190 | 0 | if (mBytesRead == 0) { |
1191 | 0 | // We didn't need to read (because LOAD_ONLY_IF_MODIFIED was |
1192 | 0 | // specified). |
1193 | 0 | mNeedsUpdate = false; |
1194 | 0 | } else { |
1195 | 0 | // Handle any leftover manifest data. |
1196 | 0 | nsCString::const_iterator begin, end; |
1197 | 0 | mReadBuf.BeginReading(begin); |
1198 | 0 | mReadBuf.EndReading(end); |
1199 | 0 | nsresult rv = HandleManifestLine(begin, end); |
1200 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1201 | 0 |
|
1202 | 0 | rv = CheckNewManifestContentHash(aRequest); |
1203 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1204 | 0 | } |
1205 | 0 |
|
1206 | 0 | return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus); |
1207 | 0 | } |
1208 | | |
1209 | | //----------------------------------------------------------------------------- |
1210 | | // nsOfflineCacheUpdate::nsISupports |
1211 | | //----------------------------------------------------------------------------- |
1212 | | |
1213 | | NS_IMPL_ISUPPORTS(nsOfflineCacheUpdate, |
1214 | | nsIOfflineCacheUpdateObserver, |
1215 | | nsIOfflineCacheUpdate, |
1216 | | nsIRunnable) |
1217 | | |
1218 | | //----------------------------------------------------------------------------- |
1219 | | // nsOfflineCacheUpdate <public> |
1220 | | //----------------------------------------------------------------------------- |
1221 | | |
1222 | | nsOfflineCacheUpdate::nsOfflineCacheUpdate() |
1223 | | : mState(STATE_UNINITIALIZED) |
1224 | | , mAddedItems(false) |
1225 | | , mPartialUpdate(false) |
1226 | | , mOnlyCheckUpdate(false) |
1227 | | , mSucceeded(true) |
1228 | | , mObsolete(false) |
1229 | | , mItemsInProgress(0) |
1230 | | , mRescheduleCount(0) |
1231 | | , mPinnedEntryRetriesCount(0) |
1232 | | , mPinned(false) |
1233 | | , mByteProgress(0) |
1234 | 0 | { |
1235 | 0 | } |
1236 | | |
1237 | | nsOfflineCacheUpdate::~nsOfflineCacheUpdate() |
1238 | 0 | { |
1239 | 0 | LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this)); |
1240 | 0 | } |
1241 | | |
1242 | | /* static */ |
1243 | | nsresult |
1244 | | nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey) |
1245 | 0 | { |
1246 | 0 | aKey.Truncate(); |
1247 | 0 |
|
1248 | 0 | nsCOMPtr<nsIURI> newURI; |
1249 | 0 | nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(newURI)); |
1250 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1251 | 0 |
|
1252 | 0 | rv = newURI->GetAsciiSpec(aKey); |
1253 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1254 | 0 |
|
1255 | 0 | return NS_OK; |
1256 | 0 | } |
1257 | | |
1258 | | nsresult |
1259 | | nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI, |
1260 | | nsIPrincipal* aLoadingPrincipal) |
1261 | 0 | { |
1262 | 0 | nsresult rv; |
1263 | 0 |
|
1264 | 0 | // Only http and https applications are supported. |
1265 | 0 | bool match; |
1266 | 0 | rv = aManifestURI->SchemeIs("http", &match); |
1267 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1268 | 0 |
|
1269 | 0 | if (!match) { |
1270 | 0 | rv = aManifestURI->SchemeIs("https", &match); |
1271 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1272 | 0 | if (!match) |
1273 | 0 | return NS_ERROR_ABORT; |
1274 | 0 | } |
1275 | 0 | |
1276 | 0 | mManifestURI = aManifestURI; |
1277 | 0 | mLoadingPrincipal = aLoadingPrincipal; |
1278 | 0 |
|
1279 | 0 | rv = mManifestURI->GetAsciiHost(mUpdateDomain); |
1280 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1281 | 0 |
|
1282 | 0 | mPartialUpdate = false; |
1283 | 0 |
|
1284 | 0 | return NS_OK; |
1285 | 0 | } |
1286 | | |
1287 | | nsresult |
1288 | | nsOfflineCacheUpdate::Init(nsIURI *aManifestURI, |
1289 | | nsIURI *aDocumentURI, |
1290 | | nsIPrincipal* aLoadingPrincipal, |
1291 | | nsIDocument *aDocument, |
1292 | | nsIFile *aCustomProfileDir) |
1293 | 0 | { |
1294 | 0 | nsresult rv; |
1295 | 0 |
|
1296 | 0 | // Make sure the service has been initialized |
1297 | 0 | nsOfflineCacheUpdateService* service = |
1298 | 0 | nsOfflineCacheUpdateService::EnsureService(); |
1299 | 0 | if (!service) |
1300 | 0 | return NS_ERROR_FAILURE; |
1301 | 0 | |
1302 | 0 | LOG(("nsOfflineCacheUpdate::Init [%p]", this)); |
1303 | 0 |
|
1304 | 0 | rv = InitInternal(aManifestURI, aLoadingPrincipal); |
1305 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1306 | 0 |
|
1307 | 0 | nsCOMPtr<nsIApplicationCacheService> cacheService = |
1308 | 0 | do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); |
1309 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1310 | 0 |
|
1311 | 0 | nsAutoCString originSuffix; |
1312 | 0 | rv = aLoadingPrincipal->GetOriginSuffix(originSuffix); |
1313 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1314 | 0 |
|
1315 | 0 | mDocumentURI = aDocumentURI; |
1316 | 0 |
|
1317 | 0 | if (aCustomProfileDir) { |
1318 | 0 | rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID); |
1319 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1320 | 0 |
|
1321 | 0 | // Create only a new offline application cache in the custom profile |
1322 | 0 | // This is a preload of a new cache. |
1323 | 0 |
|
1324 | 0 | // XXX Custom updates don't support "updating" of an existing cache |
1325 | 0 | // in the custom profile at the moment. This support can be, though, |
1326 | 0 | // simply added as well when needed. |
1327 | 0 | mPreviousApplicationCache = nullptr; |
1328 | 0 |
|
1329 | 0 | rv = cacheService->CreateCustomApplicationCache(mGroupID, |
1330 | 0 | aCustomProfileDir, |
1331 | 0 | kCustomProfileQuota, |
1332 | 0 | getter_AddRefs(mApplicationCache)); |
1333 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1334 | 0 |
|
1335 | 0 | mCustomProfileDir = aCustomProfileDir; |
1336 | 0 | } |
1337 | 0 | else { |
1338 | 0 | rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID); |
1339 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1340 | 0 |
|
1341 | 0 | rv = cacheService->GetActiveCache(mGroupID, |
1342 | 0 | getter_AddRefs(mPreviousApplicationCache)); |
1343 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1344 | 0 |
|
1345 | 0 | rv = cacheService->CreateApplicationCache(mGroupID, |
1346 | 0 | getter_AddRefs(mApplicationCache)); |
1347 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1348 | 0 | } |
1349 | 0 |
|
1350 | 0 | rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI, |
1351 | 0 | nullptr, |
1352 | 0 | &mPinned); |
1353 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1354 | 0 |
|
1355 | 0 | mState = STATE_INITIALIZED; |
1356 | 0 | return NS_OK; |
1357 | 0 | } |
1358 | | |
1359 | | nsresult |
1360 | | nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI, |
1361 | | nsIPrincipal* aLoadingPrincipal, |
1362 | | nsIObserver *aObserver) |
1363 | 0 | { |
1364 | 0 | nsresult rv; |
1365 | 0 |
|
1366 | 0 | // Make sure the service has been initialized |
1367 | 0 | nsOfflineCacheUpdateService* service = |
1368 | 0 | nsOfflineCacheUpdateService::EnsureService(); |
1369 | 0 | if (!service) |
1370 | 0 | return NS_ERROR_FAILURE; |
1371 | 0 | |
1372 | 0 | LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this)); |
1373 | 0 |
|
1374 | 0 | rv = InitInternal(aManifestURI, aLoadingPrincipal); |
1375 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1376 | 0 |
|
1377 | 0 | nsCOMPtr<nsIApplicationCacheService> cacheService = |
1378 | 0 | do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); |
1379 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1380 | 0 |
|
1381 | 0 | nsAutoCString originSuffix; |
1382 | 0 | rv = aLoadingPrincipal->GetOriginSuffix(originSuffix); |
1383 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1384 | 0 |
|
1385 | 0 | rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID); |
1386 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1387 | 0 |
|
1388 | 0 | rv = cacheService->GetActiveCache(mGroupID, |
1389 | 0 | getter_AddRefs(mPreviousApplicationCache)); |
1390 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1391 | 0 |
|
1392 | 0 | // To load the manifest properly using current app cache to satisfy and |
1393 | 0 | // also to compare the cached content hash value we have to set 'some' |
1394 | 0 | // app cache to write to on the channel. Otherwise the cached version will |
1395 | 0 | // be used and no actual network request will be made. We use the same |
1396 | 0 | // app cache here. OpenChannel prevents caching in this case using |
1397 | 0 | // INHIBIT_CACHING load flag. |
1398 | 0 | mApplicationCache = mPreviousApplicationCache; |
1399 | 0 |
|
1400 | 0 | rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI, |
1401 | 0 | nullptr, |
1402 | 0 | &mPinned); |
1403 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1404 | 0 |
|
1405 | 0 | mUpdateAvailableObserver = aObserver; |
1406 | 0 | mOnlyCheckUpdate = true; |
1407 | 0 |
|
1408 | 0 | mState = STATE_INITIALIZED; |
1409 | 0 | return NS_OK; |
1410 | 0 | } |
1411 | | |
1412 | | nsresult |
1413 | | nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI, |
1414 | | const nsACString& clientID, |
1415 | | nsIURI *aDocumentURI, |
1416 | | nsIPrincipal *aLoadingPrincipal) |
1417 | 0 | { |
1418 | 0 | nsresult rv; |
1419 | 0 |
|
1420 | 0 | // Make sure the service has been initialized |
1421 | 0 | nsOfflineCacheUpdateService* service = |
1422 | 0 | nsOfflineCacheUpdateService::EnsureService(); |
1423 | 0 | if (!service) |
1424 | 0 | return NS_ERROR_FAILURE; |
1425 | 0 | |
1426 | 0 | LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this)); |
1427 | 0 |
|
1428 | 0 | mPartialUpdate = true; |
1429 | 0 | mDocumentURI = aDocumentURI; |
1430 | 0 | mLoadingPrincipal = aLoadingPrincipal; |
1431 | 0 |
|
1432 | 0 | mManifestURI = aManifestURI; |
1433 | 0 | rv = mManifestURI->GetAsciiHost(mUpdateDomain); |
1434 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1435 | 0 |
|
1436 | 0 | nsCOMPtr<nsIApplicationCacheService> cacheService = |
1437 | 0 | do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); |
1438 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1439 | 0 |
|
1440 | 0 | rv = cacheService->GetApplicationCache(clientID, |
1441 | 0 | getter_AddRefs(mApplicationCache)); |
1442 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1443 | 0 |
|
1444 | 0 | if (!mApplicationCache) { |
1445 | 0 | nsAutoCString manifestSpec; |
1446 | 0 | rv = GetCacheKey(mManifestURI, manifestSpec); |
1447 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1448 | 0 |
|
1449 | 0 | rv = cacheService->CreateApplicationCache |
1450 | 0 | (manifestSpec, getter_AddRefs(mApplicationCache)); |
1451 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1452 | 0 | } |
1453 | 0 |
|
1454 | 0 | rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI)); |
1455 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1456 | 0 |
|
1457 | 0 | nsAutoCString groupID; |
1458 | 0 | rv = mApplicationCache->GetGroupID(groupID); |
1459 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1460 | 0 |
|
1461 | 0 | rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI, |
1462 | 0 | nullptr, |
1463 | 0 | &mPinned); |
1464 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1465 | 0 |
|
1466 | 0 | mState = STATE_INITIALIZED; |
1467 | 0 | return NS_OK; |
1468 | 0 | } |
1469 | | |
1470 | | nsresult |
1471 | | nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate) |
1472 | 0 | { |
1473 | 0 | // Be pessimistic |
1474 | 0 | *aDoUpdate = false; |
1475 | 0 |
|
1476 | 0 | bool succeeded; |
1477 | 0 | nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded); |
1478 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1479 | 0 |
|
1480 | 0 | if (!succeeded || !mManifestItem->ParseSucceeded()) { |
1481 | 0 | return NS_ERROR_FAILURE; |
1482 | 0 | } |
1483 | 0 | |
1484 | 0 | if (!mManifestItem->NeedsUpdate()) { |
1485 | 0 | return NS_OK; |
1486 | 0 | } |
1487 | 0 | |
1488 | 0 | // Add items requested by the manifest. |
1489 | 0 | const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs(); |
1490 | 0 | for (int32_t i = 0; i < manifestURIs.Count(); i++) { |
1491 | 0 | rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT); |
1492 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1493 | 0 | } |
1494 | 0 |
|
1495 | 0 | const nsCOMArray<nsIURI> &anonURIs = mManifestItem->GetAnonymousURIs(); |
1496 | 0 | for (int32_t i = 0; i < anonURIs.Count(); i++) { |
1497 | 0 | rv = AddURI(anonURIs[i], nsIApplicationCache::ITEM_EXPLICIT, |
1498 | 0 | nsIRequest::LOAD_ANONYMOUS); |
1499 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1500 | 0 | } |
1501 | 0 |
|
1502 | 0 | const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs(); |
1503 | 0 | for (int32_t i = 0; i < fallbackURIs.Count(); i++) { |
1504 | 0 | rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK); |
1505 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1506 | 0 | } |
1507 | 0 |
|
1508 | 0 | // The document that requested the manifest is implicitly included |
1509 | 0 | // as part of that manifest update. |
1510 | 0 | rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT); |
1511 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1512 | 0 |
|
1513 | 0 | // Add items previously cached implicitly |
1514 | 0 | rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT); |
1515 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1516 | 0 |
|
1517 | 0 | // Add items requested by the script API |
1518 | 0 | rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC); |
1519 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1520 | 0 |
|
1521 | 0 | // Add opportunistically cached items conforming current opportunistic |
1522 | 0 | // namespace list |
1523 | 0 | rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC, |
1524 | 0 | &mManifestItem->GetOpportunisticNamespaces()); |
1525 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1526 | 0 |
|
1527 | 0 | *aDoUpdate = true; |
1528 | 0 |
|
1529 | 0 | return NS_OK; |
1530 | 0 | } |
1531 | | |
1532 | | bool |
1533 | | nsOfflineCacheUpdate::CheckUpdateAvailability() |
1534 | 0 | { |
1535 | 0 | nsresult rv; |
1536 | 0 |
|
1537 | 0 | bool succeeded; |
1538 | 0 | rv = mManifestItem->GetRequestSucceeded(&succeeded); |
1539 | 0 | NS_ENSURE_SUCCESS(rv, false); |
1540 | 0 |
|
1541 | 0 | if (!succeeded || !mManifestItem->ParseSucceeded()) { |
1542 | 0 | return false; |
1543 | 0 | } |
1544 | 0 | |
1545 | 0 | if (!mPinned) { |
1546 | 0 | uint16_t status; |
1547 | 0 | rv = mManifestItem->GetStatus(&status); |
1548 | 0 | NS_ENSURE_SUCCESS(rv, false); |
1549 | 0 |
|
1550 | 0 | // Treat these as there would be an update available, |
1551 | 0 | // since this is indication of demand to remove this |
1552 | 0 | // offline cache. |
1553 | 0 | if (status == 404 || status == 410) { |
1554 | 0 | return true; |
1555 | 0 | } |
1556 | 0 | } |
1557 | 0 | |
1558 | 0 | return mManifestItem->NeedsUpdate(); |
1559 | 0 | } |
1560 | | |
1561 | | void |
1562 | | nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem) |
1563 | 0 | { |
1564 | 0 | nsresult rv; |
1565 | 0 |
|
1566 | 0 | LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this)); |
1567 | 0 |
|
1568 | 0 | if (mState == STATE_FINISHED) { |
1569 | 0 | LOG((" after completion, ignoring")); |
1570 | 0 | return; |
1571 | 0 | } |
1572 | 0 |
|
1573 | 0 | // Keep the object alive through a Finish() call. |
1574 | 0 | nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); |
1575 | 0 |
|
1576 | 0 | if (mState == STATE_CANCELLED) { |
1577 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1578 | 0 | Finish(); |
1579 | 0 | return; |
1580 | 0 | } |
1581 | 0 | |
1582 | 0 | if (mState == STATE_CHECKING) { |
1583 | 0 | // Manifest load finished. |
1584 | 0 |
|
1585 | 0 | if (mOnlyCheckUpdate) { |
1586 | 0 | Finish(); |
1587 | 0 | NotifyUpdateAvailability(CheckUpdateAvailability()); |
1588 | 0 | return; |
1589 | 0 | } |
1590 | 0 | |
1591 | 0 | NS_ASSERTION(mManifestItem, |
1592 | 0 | "Must have a manifest item in STATE_CHECKING."); |
1593 | 0 | NS_ASSERTION(mManifestItem == aItem, |
1594 | 0 | "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted"); |
1595 | 0 |
|
1596 | 0 | // A 404 or 410 is interpreted as an intentional removal of |
1597 | 0 | // the manifest file, rather than a transient server error. |
1598 | 0 | // Obsolete this cache group if one of these is returned. |
1599 | 0 | uint16_t status; |
1600 | 0 | rv = mManifestItem->GetStatus(&status); |
1601 | 0 | if (status == 404 || status == 410) { |
1602 | 0 | LogToConsole("Offline cache manifest removed, cache cleared", mManifestItem); |
1603 | 0 | mSucceeded = false; |
1604 | 0 | if (mPreviousApplicationCache) { |
1605 | 0 | if (mPinned) { |
1606 | 0 | // Do not obsolete a pinned application. |
1607 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); |
1608 | 0 | } else { |
1609 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE); |
1610 | 0 | mObsolete = true; |
1611 | 0 | } |
1612 | 0 | } else { |
1613 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1614 | 0 | mObsolete = true; |
1615 | 0 | } |
1616 | 0 | Finish(); |
1617 | 0 | return; |
1618 | 0 | } |
1619 | 0 |
|
1620 | 0 | bool doUpdate; |
1621 | 0 | if (NS_FAILED(HandleManifest(&doUpdate))) { |
1622 | 0 | mSucceeded = false; |
1623 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1624 | 0 | Finish(); |
1625 | 0 | return; |
1626 | 0 | } |
1627 | 0 | |
1628 | 0 | if (!doUpdate) { |
1629 | 0 | LogToConsole("Offline cache doesn't need to update", mManifestItem); |
1630 | 0 |
|
1631 | 0 | mSucceeded = false; |
1632 | 0 |
|
1633 | 0 | AssociateDocuments(mPreviousApplicationCache); |
1634 | 0 |
|
1635 | 0 | ScheduleImplicit(); |
1636 | 0 |
|
1637 | 0 | // If we didn't need an implicit update, we can |
1638 | 0 | // send noupdate and end the update now. |
1639 | 0 | if (!mImplicitUpdate) { |
1640 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); |
1641 | 0 | Finish(); |
1642 | 0 | } |
1643 | 0 | return; |
1644 | 0 | } |
1645 | 0 |
|
1646 | 0 | rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey, |
1647 | 0 | mManifestItem->mItemType); |
1648 | 0 | if (NS_FAILED(rv)) { |
1649 | 0 | mSucceeded = false; |
1650 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1651 | 0 | Finish(); |
1652 | 0 | return; |
1653 | 0 | } |
1654 | 0 | |
1655 | 0 | mState = STATE_DOWNLOADING; |
1656 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); |
1657 | 0 |
|
1658 | 0 | // Start fetching resources. |
1659 | 0 | ProcessNextURI(); |
1660 | 0 |
|
1661 | 0 | return; |
1662 | 0 | } |
1663 | 0 | |
1664 | 0 | // Normal load finished. |
1665 | 0 | if (mItemsInProgress) // Just to be safe here! |
1666 | 0 | --mItemsInProgress; |
1667 | 0 |
|
1668 | 0 | bool succeeded; |
1669 | 0 | rv = aItem->GetRequestSucceeded(&succeeded); |
1670 | 0 |
|
1671 | 0 | if (mPinned && NS_SUCCEEDED(rv) && succeeded) { |
1672 | 0 | uint32_t dummy_cache_type; |
1673 | 0 | rv = mApplicationCache->GetTypes(aItem->mCacheKey, &dummy_cache_type); |
1674 | 0 | bool item_doomed = NS_FAILED(rv); // can not find it? -> doomed |
1675 | 0 |
|
1676 | 0 | if (item_doomed && |
1677 | 0 | mPinnedEntryRetriesCount < kPinnedEntryRetriesLimit && |
1678 | 0 | (aItem->mItemType & (nsIApplicationCache::ITEM_EXPLICIT | |
1679 | 0 | nsIApplicationCache::ITEM_FALLBACK))) { |
1680 | 0 | rv = EvictOneNonPinned(); |
1681 | 0 | if (NS_FAILED(rv)) { |
1682 | 0 | mSucceeded = false; |
1683 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1684 | 0 | Finish(); |
1685 | 0 | return; |
1686 | 0 | } |
1687 | 0 | |
1688 | 0 | // This reverts the item state to UNINITIALIZED that makes it to |
1689 | 0 | // be scheduled for download again. |
1690 | 0 | rv = aItem->Cancel(); |
1691 | 0 | if (NS_FAILED(rv)) { |
1692 | 0 | mSucceeded = false; |
1693 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1694 | 0 | Finish(); |
1695 | 0 | return; |
1696 | 0 | } |
1697 | 0 | |
1698 | 0 | mPinnedEntryRetriesCount++; |
1699 | 0 |
|
1700 | 0 | LogToConsole("An unpinned offline cache deleted"); |
1701 | 0 |
|
1702 | 0 | // Retry this item. |
1703 | 0 | ProcessNextURI(); |
1704 | 0 | return; |
1705 | 0 | } |
1706 | 0 | } |
1707 | 0 |
|
1708 | 0 | // According to parallelism this may imply more pinned retries count, |
1709 | 0 | // but that is not critical, since at one moment the algorithm will |
1710 | 0 | // stop anyway. Also, this code may soon be completely removed |
1711 | 0 | // after we have a separate storage for pinned apps. |
1712 | 0 | mPinnedEntryRetriesCount = 0; |
1713 | 0 |
|
1714 | 0 | // Check for failures. 3XX, 4XX and 5XX errors on items explicitly |
1715 | 0 | // listed in the manifest will cause the update to fail. |
1716 | 0 | if (NS_FAILED(rv) || !succeeded) { |
1717 | 0 | if (aItem->mItemType & |
1718 | 0 | (nsIApplicationCache::ITEM_EXPLICIT | |
1719 | 0 | nsIApplicationCache::ITEM_FALLBACK)) { |
1720 | 0 | LogToConsole("Offline cache manifest item failed to load", aItem); |
1721 | 0 | mSucceeded = false; |
1722 | 0 | } |
1723 | 0 | } else { |
1724 | 0 | rv = mApplicationCache->MarkEntry(aItem->mCacheKey, aItem->mItemType); |
1725 | 0 | if (NS_FAILED(rv)) { |
1726 | 0 | mSucceeded = false; |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 |
|
1730 | 0 | if (!mSucceeded) { |
1731 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1732 | 0 | Finish(); |
1733 | 0 | return; |
1734 | 0 | } |
1735 | 0 | |
1736 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED); |
1737 | 0 |
|
1738 | 0 | ProcessNextURI(); |
1739 | 0 | } |
1740 | | |
1741 | | void |
1742 | | nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus, |
1743 | | const nsCString &aManifestHash) |
1744 | 0 | { |
1745 | 0 | // Keep the object alive through a Finish() call. |
1746 | 0 | nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); |
1747 | 0 |
|
1748 | 0 | if (NS_SUCCEEDED(aStatus)) { |
1749 | 0 | nsAutoCString firstManifestHash; |
1750 | 0 | mManifestItem->GetManifestHash(firstManifestHash); |
1751 | 0 | if (aManifestHash != firstManifestHash) { |
1752 | 0 | LOG(("Manifest has changed during cache items download [%p]", this)); |
1753 | 0 | LogToConsole("Offline cache manifest changed during update", mManifestItem); |
1754 | 0 | aStatus = NS_ERROR_FAILURE; |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 |
|
1758 | 0 | if (NS_FAILED(aStatus)) { |
1759 | 0 | mSucceeded = false; |
1760 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1761 | 0 | } |
1762 | 0 |
|
1763 | 0 | if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) { |
1764 | 0 | // Do the final stuff but prevent notification of STATE_FINISHED. |
1765 | 0 | // That would disconnect listeners that are responsible for document |
1766 | 0 | // association after a successful update. Forwarding notifications |
1767 | 0 | // from a new update through this dead update to them is absolutely |
1768 | 0 | // correct. |
1769 | 0 | FinishNoNotify(); |
1770 | 0 |
|
1771 | 0 | RefPtr<nsOfflineCacheUpdate> newUpdate = |
1772 | 0 | new nsOfflineCacheUpdate(); |
1773 | 0 | // Leave aDocument argument null. Only glues and children keep |
1774 | 0 | // document instances. |
1775 | 0 | newUpdate->Init(mManifestURI, mDocumentURI, mLoadingPrincipal, nullptr, |
1776 | 0 | mCustomProfileDir); |
1777 | 0 |
|
1778 | 0 | // In a rare case the manifest will not be modified on the next refetch |
1779 | 0 | // transfer all master document URIs to the new update to ensure that |
1780 | 0 | // all documents refering it will be properly cached. |
1781 | 0 | for (int32_t i = 0; i < mDocumentURIs.Count(); i++) { |
1782 | 0 | newUpdate->StickDocument(mDocumentURIs[i]); |
1783 | 0 | } |
1784 | 0 |
|
1785 | 0 | newUpdate->mRescheduleCount = mRescheduleCount + 1; |
1786 | 0 | newUpdate->AddObserver(this, false); |
1787 | 0 | newUpdate->Schedule(); |
1788 | 0 | } |
1789 | 0 | else { |
1790 | 0 | LogToConsole("Offline cache update done", mManifestItem); |
1791 | 0 | Finish(); |
1792 | 0 | } |
1793 | 0 | } |
1794 | | |
1795 | | nsresult |
1796 | | nsOfflineCacheUpdate::Begin() |
1797 | 0 | { |
1798 | 0 | LOG(("nsOfflineCacheUpdate::Begin [%p]", this)); |
1799 | 0 |
|
1800 | 0 | // Keep the object alive through a ProcessNextURI()/Finish() call. |
1801 | 0 | nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); |
1802 | 0 |
|
1803 | 0 | mItemsInProgress = 0; |
1804 | 0 |
|
1805 | 0 | if (mState == STATE_CANCELLED) { |
1806 | 0 | nsresult rv = NS_DispatchToMainThread( |
1807 | 0 | NewRunnableMethod("nsOfflineCacheUpdate::AsyncFinishWithError", |
1808 | 0 | this, |
1809 | 0 | &nsOfflineCacheUpdate::AsyncFinishWithError)); |
1810 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1811 | 0 |
|
1812 | 0 | return NS_OK; |
1813 | 0 | } |
1814 | 0 | |
1815 | 0 | if (mPartialUpdate) { |
1816 | 0 | mState = STATE_DOWNLOADING; |
1817 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); |
1818 | 0 | ProcessNextURI(); |
1819 | 0 | return NS_OK; |
1820 | 0 | } |
1821 | 0 | |
1822 | 0 | // Start checking the manifest. |
1823 | 0 | mManifestItem = new nsOfflineManifestItem(mManifestURI, |
1824 | 0 | mDocumentURI, |
1825 | 0 | mLoadingPrincipal, |
1826 | 0 | mApplicationCache, |
1827 | 0 | mPreviousApplicationCache); |
1828 | 0 | if (!mManifestItem) { |
1829 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1830 | 0 | } |
1831 | 0 | |
1832 | 0 | mState = STATE_CHECKING; |
1833 | 0 | mByteProgress = 0; |
1834 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING); |
1835 | 0 |
|
1836 | 0 | nsresult rv = mManifestItem->OpenChannel(this); |
1837 | 0 | if (NS_FAILED(rv)) { |
1838 | 0 | LoadCompleted(mManifestItem); |
1839 | 0 | } |
1840 | 0 |
|
1841 | 0 | return NS_OK; |
1842 | 0 | } |
1843 | | |
1844 | | //----------------------------------------------------------------------------- |
1845 | | // nsOfflineCacheUpdate <private> |
1846 | | //----------------------------------------------------------------------------- |
1847 | | |
1848 | | nsresult |
1849 | | nsOfflineCacheUpdate::AddExistingItems(uint32_t aType, |
1850 | | nsTArray<nsCString>* namespaceFilter) |
1851 | 0 | { |
1852 | 0 | if (!mPreviousApplicationCache) { |
1853 | 0 | return NS_OK; |
1854 | 0 | } |
1855 | 0 | |
1856 | 0 | if (namespaceFilter && namespaceFilter->Length() == 0) { |
1857 | 0 | // Don't bother to walk entries when there are no namespaces |
1858 | 0 | // defined. |
1859 | 0 | return NS_OK; |
1860 | 0 | } |
1861 | 0 | |
1862 | 0 | uint32_t count = 0; |
1863 | 0 | char **keys = nullptr; |
1864 | 0 | nsresult rv = mPreviousApplicationCache->GatherEntries(aType, |
1865 | 0 | &count, &keys); |
1866 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1867 | 0 |
|
1868 | 0 | AutoFreeArray autoFree(count, keys); |
1869 | 0 |
|
1870 | 0 | for (uint32_t i = 0; i < count; i++) { |
1871 | 0 | if (namespaceFilter) { |
1872 | 0 | bool found = false; |
1873 | 0 | for (uint32_t j = 0; j < namespaceFilter->Length() && !found; j++) { |
1874 | 0 | found = StringBeginsWith(nsDependentCString(keys[i]), |
1875 | 0 | namespaceFilter->ElementAt(j)); |
1876 | 0 | } |
1877 | 0 |
|
1878 | 0 | if (!found) |
1879 | 0 | continue; |
1880 | 0 | } |
1881 | 0 | |
1882 | 0 | nsCOMPtr<nsIURI> uri; |
1883 | 0 | if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) { |
1884 | 0 | rv = AddURI(uri, aType); |
1885 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1886 | 0 | } |
1887 | 0 | } |
1888 | 0 |
|
1889 | 0 | return NS_OK; |
1890 | 0 | } |
1891 | | |
1892 | | nsresult |
1893 | | nsOfflineCacheUpdate::ProcessNextURI() |
1894 | 0 | { |
1895 | 0 | // Keep the object alive through a Finish() call. |
1896 | 0 | nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); |
1897 | 0 |
|
1898 | 0 | LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, inprogress=%d, numItems=%zu]", |
1899 | 0 | this, mItemsInProgress, mItems.Length())); |
1900 | 0 |
|
1901 | 0 | if (mState != STATE_DOWNLOADING) { |
1902 | 0 | LOG((" should only be called from the DOWNLOADING state, ignoring")); |
1903 | 0 | return NS_ERROR_UNEXPECTED; |
1904 | 0 | } |
1905 | 0 |
|
1906 | 0 | nsOfflineCacheUpdateItem * runItem = nullptr; |
1907 | 0 | uint32_t completedItems = 0; |
1908 | 0 | for (uint32_t i = 0; i < mItems.Length(); ++i) { |
1909 | 0 | nsOfflineCacheUpdateItem * item = mItems[i]; |
1910 | 0 |
|
1911 | 0 | if (item->IsScheduled()) { |
1912 | 0 | runItem = item; |
1913 | 0 | break; |
1914 | 0 | } |
1915 | 0 | |
1916 | 0 | if (item->IsCompleted()) |
1917 | 0 | ++completedItems; |
1918 | 0 | } |
1919 | 0 |
|
1920 | 0 | if (completedItems == mItems.Length()) { |
1921 | 0 | LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]: all items loaded", this)); |
1922 | 0 |
|
1923 | 0 | if (mPartialUpdate) { |
1924 | 0 | return Finish(); |
1925 | 0 | } else { |
1926 | 0 | // Verify that the manifest wasn't changed during the |
1927 | 0 | // update, to prevent capturing a cache while the server |
1928 | 0 | // is being updated. The check will call |
1929 | 0 | // ManifestCheckCompleted() when it's done. |
1930 | 0 | RefPtr<nsManifestCheck> manifestCheck = |
1931 | 0 | new nsManifestCheck(this, mManifestURI, mDocumentURI, mLoadingPrincipal); |
1932 | 0 | if (NS_FAILED(manifestCheck->Begin())) { |
1933 | 0 | mSucceeded = false; |
1934 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
1935 | 0 | return Finish(); |
1936 | 0 | } |
1937 | 0 | |
1938 | 0 | return NS_OK; |
1939 | 0 | } |
1940 | 0 | } |
1941 | 0 |
|
1942 | 0 | if (!runItem) { |
1943 | 0 | LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:" |
1944 | 0 | " No more items to include in parallel load", this)); |
1945 | 0 | return NS_OK; |
1946 | 0 | } |
1947 | 0 |
|
1948 | 0 | if (LOG_ENABLED()) { |
1949 | 0 | LOG(("%p: Opening channel for %s", this, |
1950 | 0 | runItem->mURI->GetSpecOrDefault().get())); |
1951 | 0 | } |
1952 | 0 |
|
1953 | 0 | ++mItemsInProgress; |
1954 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED); |
1955 | 0 |
|
1956 | 0 | nsresult rv = runItem->OpenChannel(this); |
1957 | 0 | if (NS_FAILED(rv)) { |
1958 | 0 | LoadCompleted(runItem); |
1959 | 0 | return rv; |
1960 | 0 | } |
1961 | 0 | |
1962 | 0 | if (mItemsInProgress >= kParallelLoadLimit) { |
1963 | 0 | LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:" |
1964 | 0 | " At parallel load limit", this)); |
1965 | 0 | return NS_OK; |
1966 | 0 | } |
1967 | 0 |
|
1968 | 0 | // This calls this method again via a post triggering |
1969 | 0 | // a parallel item load |
1970 | 0 | return NS_DispatchToCurrentThread(this); |
1971 | 0 | } |
1972 | | |
1973 | | void |
1974 | | nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers) |
1975 | 0 | { |
1976 | 0 | for (int32_t i = 0; i < mWeakObservers.Count(); i++) { |
1977 | 0 | nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = |
1978 | 0 | do_QueryReferent(mWeakObservers[i]); |
1979 | 0 | if (observer) |
1980 | 0 | aObservers.AppendObject(observer); |
1981 | 0 | else |
1982 | 0 | mWeakObservers.RemoveObjectAt(i--); |
1983 | 0 | } |
1984 | 0 |
|
1985 | 0 | for (int32_t i = 0; i < mObservers.Count(); i++) { |
1986 | 0 | aObservers.AppendObject(mObservers[i]); |
1987 | 0 | } |
1988 | 0 | } |
1989 | | |
1990 | | void |
1991 | | nsOfflineCacheUpdate::NotifyState(uint32_t state) |
1992 | 0 | { |
1993 | 0 | LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state)); |
1994 | 0 |
|
1995 | 0 | if (state == STATE_ERROR) { |
1996 | 0 | LogToConsole("Offline cache update error", mManifestItem); |
1997 | 0 | } |
1998 | 0 |
|
1999 | 0 | nsCOMArray<nsIOfflineCacheUpdateObserver> observers; |
2000 | 0 | GatherObservers(observers); |
2001 | 0 |
|
2002 | 0 | for (int32_t i = 0; i < observers.Count(); i++) { |
2003 | 0 | observers[i]->UpdateStateChanged(this, state); |
2004 | 0 | } |
2005 | 0 | } |
2006 | | |
2007 | | void |
2008 | | nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable) |
2009 | 0 | { |
2010 | 0 | if (!mUpdateAvailableObserver) |
2011 | 0 | return; |
2012 | 0 | |
2013 | 0 | LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]", |
2014 | 0 | this, updateAvailable)); |
2015 | 0 |
|
2016 | 0 | const char* topic = updateAvailable |
2017 | 0 | ? "offline-cache-update-available" |
2018 | 0 | : "offline-cache-update-unavailable"; |
2019 | 0 |
|
2020 | 0 | nsCOMPtr<nsIObserver> observer; |
2021 | 0 | observer.swap(mUpdateAvailableObserver); |
2022 | 0 | observer->Observe(mManifestURI, topic, nullptr); |
2023 | 0 | } |
2024 | | |
2025 | | void |
2026 | | nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache) |
2027 | 0 | { |
2028 | 0 | if (!cache) { |
2029 | 0 | LOG(("nsOfflineCacheUpdate::AssociateDocuments bypassed" |
2030 | 0 | ", no cache provided [this=%p]", this)); |
2031 | 0 | return; |
2032 | 0 | } |
2033 | 0 |
|
2034 | 0 | nsCOMArray<nsIOfflineCacheUpdateObserver> observers; |
2035 | 0 | GatherObservers(observers); |
2036 | 0 |
|
2037 | 0 | for (int32_t i = 0; i < observers.Count(); i++) { |
2038 | 0 | observers[i]->ApplicationCacheAvailable(cache); |
2039 | 0 | } |
2040 | 0 | } |
2041 | | |
2042 | | void |
2043 | | nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI) |
2044 | 0 | { |
2045 | 0 | if (!aDocumentURI) |
2046 | 0 | return; |
2047 | 0 | |
2048 | 0 | mDocumentURIs.AppendObject(aDocumentURI); |
2049 | 0 | } |
2050 | | |
2051 | | void |
2052 | | nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner) |
2053 | 0 | { |
2054 | 0 | NS_ASSERTION(!mOwner, "Tried to set cache update owner twice."); |
2055 | 0 | mOwner = aOwner; |
2056 | 0 | } |
2057 | | |
2058 | | bool |
2059 | | nsOfflineCacheUpdate::IsForGroupID(const nsACString& groupID) |
2060 | 0 | { |
2061 | 0 | return mGroupID == groupID; |
2062 | 0 | } |
2063 | | |
2064 | | bool |
2065 | | nsOfflineCacheUpdate::IsForProfile(nsIFile* aCustomProfileDir) |
2066 | 0 | { |
2067 | 0 | if (!mCustomProfileDir && !aCustomProfileDir) |
2068 | 0 | return true; |
2069 | 0 | if (!mCustomProfileDir || !aCustomProfileDir) |
2070 | 0 | return false; |
2071 | 0 | |
2072 | 0 | bool equals; |
2073 | 0 | nsresult rv = mCustomProfileDir->Equals(aCustomProfileDir, &equals); |
2074 | 0 |
|
2075 | 0 | return NS_SUCCEEDED(rv) && equals; |
2076 | 0 | } |
2077 | | |
2078 | | nsresult |
2079 | | nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate) |
2080 | 0 | { |
2081 | 0 | // Keep the object alive through a Finish() call. |
2082 | 0 | nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); |
2083 | 0 |
|
2084 | 0 | mImplicitUpdate = nullptr; |
2085 | 0 |
|
2086 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); |
2087 | 0 | Finish(); |
2088 | 0 |
|
2089 | 0 | return NS_OK; |
2090 | 0 | } |
2091 | | |
2092 | | void |
2093 | | nsOfflineCacheUpdate::OnByteProgress(uint64_t byteIncrement) |
2094 | 0 | { |
2095 | 0 | mByteProgress += byteIncrement; |
2096 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMPROGRESS); |
2097 | 0 | } |
2098 | | |
2099 | | nsresult |
2100 | | nsOfflineCacheUpdate::ScheduleImplicit() |
2101 | 0 | { |
2102 | 0 | if (mDocumentURIs.Count() == 0) |
2103 | 0 | return NS_OK; |
2104 | 0 | |
2105 | 0 | nsresult rv; |
2106 | 0 |
|
2107 | 0 | RefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate(); |
2108 | 0 | NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); |
2109 | 0 |
|
2110 | 0 | nsAutoCString clientID; |
2111 | 0 | if (mPreviousApplicationCache) { |
2112 | 0 | rv = mPreviousApplicationCache->GetClientID(clientID); |
2113 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2114 | 0 | } |
2115 | 0 | else if (mApplicationCache) { |
2116 | 0 | rv = mApplicationCache->GetClientID(clientID); |
2117 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2118 | 0 | } |
2119 | 0 | else { |
2120 | 0 | NS_ERROR("Offline cache update not having set mApplicationCache?"); |
2121 | 0 | } |
2122 | 0 |
|
2123 | 0 | rv = update->InitPartial(mManifestURI, clientID, mDocumentURI, mLoadingPrincipal); |
2124 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2125 | 0 |
|
2126 | 0 | for (int32_t i = 0; i < mDocumentURIs.Count(); i++) { |
2127 | 0 | rv = update->AddURI(mDocumentURIs[i], |
2128 | 0 | nsIApplicationCache::ITEM_IMPLICIT); |
2129 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2130 | 0 | } |
2131 | 0 |
|
2132 | 0 | update->SetOwner(this); |
2133 | 0 | rv = update->Begin(); |
2134 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2135 | 0 |
|
2136 | 0 | mImplicitUpdate = update; |
2137 | 0 |
|
2138 | 0 | return NS_OK; |
2139 | 0 | } |
2140 | | |
2141 | | nsresult |
2142 | | nsOfflineCacheUpdate::FinishNoNotify() |
2143 | 0 | { |
2144 | 0 | LOG(("nsOfflineCacheUpdate::Finish [%p]", this)); |
2145 | 0 |
|
2146 | 0 | mState = STATE_FINISHED; |
2147 | 0 |
|
2148 | 0 | if (!mPartialUpdate && !mOnlyCheckUpdate) { |
2149 | 0 | if (mSucceeded) { |
2150 | 0 | nsIArray *namespaces = mManifestItem->GetNamespaces(); |
2151 | 0 | nsresult rv = mApplicationCache->AddNamespaces(namespaces); |
2152 | 0 | if (NS_FAILED(rv)) { |
2153 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
2154 | 0 | mSucceeded = false; |
2155 | 0 | } |
2156 | 0 |
|
2157 | 0 | rv = mApplicationCache->Activate(); |
2158 | 0 | if (NS_FAILED(rv)) { |
2159 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); |
2160 | 0 | mSucceeded = false; |
2161 | 0 | } |
2162 | 0 |
|
2163 | 0 | AssociateDocuments(mApplicationCache); |
2164 | 0 | } |
2165 | 0 |
|
2166 | 0 | if (mObsolete) { |
2167 | 0 | nsCOMPtr<nsIApplicationCacheService> appCacheService = |
2168 | 0 | do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID); |
2169 | 0 | if (appCacheService) { |
2170 | 0 | nsAutoCString groupID; |
2171 | 0 | mApplicationCache->GetGroupID(groupID); |
2172 | 0 | appCacheService->DeactivateGroup(groupID); |
2173 | 0 | } |
2174 | 0 | } |
2175 | 0 |
|
2176 | 0 | if (!mSucceeded) { |
2177 | 0 | // Update was not merged, mark all the loads as failures |
2178 | 0 | for (uint32_t i = 0; i < mItems.Length(); i++) { |
2179 | 0 | mItems[i]->Cancel(); |
2180 | 0 | } |
2181 | 0 |
|
2182 | 0 | mApplicationCache->Discard(); |
2183 | 0 | } |
2184 | 0 | } |
2185 | 0 |
|
2186 | 0 | nsresult rv = NS_OK; |
2187 | 0 |
|
2188 | 0 | if (mOwner) { |
2189 | 0 | rv = mOwner->UpdateFinished(this); |
2190 | 0 | // mozilla::WeakPtr is missing some key features, like setting it to |
2191 | 0 | // null explicitly. |
2192 | 0 | mOwner = mozilla::WeakPtr<nsOfflineCacheUpdateOwner>(); |
2193 | 0 | } |
2194 | 0 |
|
2195 | 0 | return rv; |
2196 | 0 | } |
2197 | | |
2198 | | nsresult |
2199 | | nsOfflineCacheUpdate::Finish() |
2200 | 0 | { |
2201 | 0 | nsresult rv = FinishNoNotify(); |
2202 | 0 |
|
2203 | 0 | NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED); |
2204 | 0 |
|
2205 | 0 | return rv; |
2206 | 0 | } |
2207 | | |
2208 | | void |
2209 | | nsOfflineCacheUpdate::AsyncFinishWithError() |
2210 | 0 | { |
2211 | 0 | NotifyState(nsOfflineCacheUpdate::STATE_ERROR); |
2212 | 0 | Finish(); |
2213 | 0 | } |
2214 | | |
2215 | | static nsresult |
2216 | | EvictOneOfCacheGroups(nsIApplicationCacheService *cacheService, |
2217 | | uint32_t count, const char * const *groups) |
2218 | 0 | { |
2219 | 0 | nsresult rv; |
2220 | 0 | unsigned int i; |
2221 | 0 |
|
2222 | 0 | for (i = 0; i < count; i++) { |
2223 | 0 | nsCOMPtr<nsIURI> uri; |
2224 | 0 | rv = NS_NewURI(getter_AddRefs(uri), groups[i]); |
2225 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2226 | 0 |
|
2227 | 0 | nsDependentCString group_name(groups[i]); |
2228 | 0 | nsCOMPtr<nsIApplicationCache> cache; |
2229 | 0 | rv = cacheService->GetActiveCache(group_name, getter_AddRefs(cache)); |
2230 | 0 | // Maybe someone in another thread or process have deleted it. |
2231 | 0 | if (NS_FAILED(rv) || !cache) |
2232 | 0 | continue; |
2233 | 0 | |
2234 | 0 | bool pinned; |
2235 | 0 | rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(uri, |
2236 | 0 | nullptr, |
2237 | 0 | &pinned); |
2238 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2239 | 0 |
|
2240 | 0 | if (!pinned) { |
2241 | 0 | rv = cache->Discard(); |
2242 | 0 | return NS_OK; |
2243 | 0 | } |
2244 | 0 | } |
2245 | 0 |
|
2246 | 0 | return NS_ERROR_FILE_NOT_FOUND; |
2247 | 0 | } |
2248 | | |
2249 | | nsresult |
2250 | | nsOfflineCacheUpdate::EvictOneNonPinned() |
2251 | 0 | { |
2252 | 0 | nsresult rv; |
2253 | 0 |
|
2254 | 0 | nsCOMPtr<nsIApplicationCacheService> cacheService = |
2255 | 0 | do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); |
2256 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2257 | 0 |
|
2258 | 0 | uint32_t count; |
2259 | 0 | char **groups; |
2260 | 0 | rv = cacheService->GetGroupsTimeOrdered(&count, &groups); |
2261 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2262 | 0 |
|
2263 | 0 | rv = EvictOneOfCacheGroups(cacheService, count, groups); |
2264 | 0 |
|
2265 | 0 | NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, groups); |
2266 | 0 | return rv; |
2267 | 0 | } |
2268 | | |
2269 | | //----------------------------------------------------------------------------- |
2270 | | // nsOfflineCacheUpdate::nsIOfflineCacheUpdate |
2271 | | //----------------------------------------------------------------------------- |
2272 | | |
2273 | | NS_IMETHODIMP |
2274 | | nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain) |
2275 | 0 | { |
2276 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2277 | 0 |
|
2278 | 0 | aUpdateDomain = mUpdateDomain; |
2279 | 0 | return NS_OK; |
2280 | 0 | } |
2281 | | |
2282 | | NS_IMETHODIMP |
2283 | | nsOfflineCacheUpdate::GetStatus(uint16_t *aStatus) |
2284 | 0 | { |
2285 | 0 | switch (mState) { |
2286 | 0 | case STATE_CHECKING : |
2287 | 0 | *aStatus = dom::OfflineResourceList_Binding::CHECKING; |
2288 | 0 | return NS_OK; |
2289 | 0 | case STATE_DOWNLOADING : |
2290 | 0 | *aStatus = dom::OfflineResourceList_Binding::DOWNLOADING; |
2291 | 0 | return NS_OK; |
2292 | 0 | default : |
2293 | 0 | *aStatus = dom::OfflineResourceList_Binding::IDLE; |
2294 | 0 | return NS_OK; |
2295 | 0 | } |
2296 | 0 | |
2297 | 0 | return NS_ERROR_FAILURE; |
2298 | 0 | } |
2299 | | |
2300 | | NS_IMETHODIMP |
2301 | | nsOfflineCacheUpdate::GetPartial(bool *aPartial) |
2302 | 0 | { |
2303 | 0 | *aPartial = mPartialUpdate || mOnlyCheckUpdate; |
2304 | 0 | return NS_OK; |
2305 | 0 | } |
2306 | | |
2307 | | NS_IMETHODIMP |
2308 | | nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI) |
2309 | 0 | { |
2310 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2311 | 0 |
|
2312 | 0 | NS_IF_ADDREF(*aManifestURI = mManifestURI); |
2313 | 0 | return NS_OK; |
2314 | 0 | } |
2315 | | |
2316 | | NS_IMETHODIMP |
2317 | | nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded) |
2318 | 0 | { |
2319 | 0 | NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); |
2320 | 0 |
|
2321 | 0 | *aSucceeded = mSucceeded; |
2322 | 0 |
|
2323 | 0 | return NS_OK; |
2324 | 0 | } |
2325 | | |
2326 | | NS_IMETHODIMP |
2327 | | nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade) |
2328 | 0 | { |
2329 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2330 | 0 |
|
2331 | 0 | *aIsUpgrade = (mPreviousApplicationCache != nullptr); |
2332 | 0 |
|
2333 | 0 | return NS_OK; |
2334 | 0 | } |
2335 | | |
2336 | | nsresult |
2337 | | nsOfflineCacheUpdate::AddURI(nsIURI *aURI, uint32_t aType, uint32_t aLoadFlags) |
2338 | 0 | { |
2339 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2340 | 0 |
|
2341 | 0 | if (mState >= STATE_DOWNLOADING) |
2342 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2343 | 0 | |
2344 | 0 | // Resource URIs must have the same scheme as the manifest. |
2345 | 0 | nsAutoCString scheme; |
2346 | 0 | aURI->GetScheme(scheme); |
2347 | 0 |
|
2348 | 0 | bool match; |
2349 | 0 | if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match) |
2350 | 0 | return NS_ERROR_FAILURE; |
2351 | 0 | |
2352 | 0 | // Don't fetch the same URI twice. |
2353 | 0 | for (uint32_t i = 0; i < mItems.Length(); i++) { |
2354 | 0 | bool equals; |
2355 | 0 | if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals && |
2356 | 0 | mItems[i]->mLoadFlags == aLoadFlags) { |
2357 | 0 | // retain both types. |
2358 | 0 | mItems[i]->mItemType |= aType; |
2359 | 0 | return NS_OK; |
2360 | 0 | } |
2361 | 0 | } |
2362 | 0 |
|
2363 | 0 | RefPtr<nsOfflineCacheUpdateItem> item = |
2364 | 0 | new nsOfflineCacheUpdateItem(aURI, |
2365 | 0 | mDocumentURI, |
2366 | 0 | mLoadingPrincipal, |
2367 | 0 | mApplicationCache, |
2368 | 0 | mPreviousApplicationCache, |
2369 | 0 | aType, |
2370 | 0 | aLoadFlags); |
2371 | 0 | if (!item) return NS_ERROR_OUT_OF_MEMORY; |
2372 | 0 | |
2373 | 0 | mItems.AppendElement(item); |
2374 | 0 | mAddedItems = true; |
2375 | 0 |
|
2376 | 0 | return NS_OK; |
2377 | 0 | } |
2378 | | |
2379 | | NS_IMETHODIMP |
2380 | | nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI) |
2381 | 0 | { |
2382 | 0 | if (GeckoProcessType_Default != XRE_GetProcessType()) |
2383 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
2384 | 0 | |
2385 | 0 | // If this is a partial update and the resource is already in the |
2386 | 0 | // cache, we should only mark the entry, not fetch it again. |
2387 | 0 | if (mPartialUpdate) { |
2388 | 0 | nsAutoCString key; |
2389 | 0 | GetCacheKey(aURI, key); |
2390 | 0 |
|
2391 | 0 | uint32_t types; |
2392 | 0 | nsresult rv = mApplicationCache->GetTypes(key, &types); |
2393 | 0 | if (NS_SUCCEEDED(rv)) { |
2394 | 0 | if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) { |
2395 | 0 | mApplicationCache->MarkEntry |
2396 | 0 | (key, nsIApplicationCache::ITEM_DYNAMIC); |
2397 | 0 | } |
2398 | 0 | return NS_OK; |
2399 | 0 | } |
2400 | 0 | } |
2401 | 0 |
|
2402 | 0 | return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC); |
2403 | 0 | } |
2404 | | |
2405 | | NS_IMETHODIMP |
2406 | | nsOfflineCacheUpdate::Cancel() |
2407 | 0 | { |
2408 | 0 | LOG(("nsOfflineCacheUpdate::Cancel [%p]", this)); |
2409 | 0 |
|
2410 | 0 | if ((mState == STATE_FINISHED) || (mState == STATE_CANCELLED)) { |
2411 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2412 | 0 | } |
2413 | 0 | |
2414 | 0 | mState = STATE_CANCELLED; |
2415 | 0 | mSucceeded = false; |
2416 | 0 |
|
2417 | 0 | // Cancel all running downloads |
2418 | 0 | for (uint32_t i = 0; i < mItems.Length(); ++i) { |
2419 | 0 | nsOfflineCacheUpdateItem * item = mItems[i]; |
2420 | 0 |
|
2421 | 0 | if (item->IsInProgress()) |
2422 | 0 | item->Cancel(); |
2423 | 0 | } |
2424 | 0 |
|
2425 | 0 | return NS_OK; |
2426 | 0 | } |
2427 | | |
2428 | | NS_IMETHODIMP |
2429 | | nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, |
2430 | | bool aHoldWeak) |
2431 | 0 | { |
2432 | 0 | LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this)); |
2433 | 0 |
|
2434 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2435 | 0 |
|
2436 | 0 | if (aHoldWeak) { |
2437 | 0 | nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver); |
2438 | 0 | mWeakObservers.AppendObject(weakRef); |
2439 | 0 | } else { |
2440 | 0 | mObservers.AppendObject(aObserver); |
2441 | 0 | } |
2442 | 0 |
|
2443 | 0 | return NS_OK; |
2444 | 0 | } |
2445 | | |
2446 | | NS_IMETHODIMP |
2447 | | nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) |
2448 | 0 | { |
2449 | 0 | LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this)); |
2450 | 0 |
|
2451 | 0 | NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
2452 | 0 |
|
2453 | 0 | for (int32_t i = 0; i < mWeakObservers.Count(); i++) { |
2454 | 0 | nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = |
2455 | 0 | do_QueryReferent(mWeakObservers[i]); |
2456 | 0 | if (observer == aObserver) { |
2457 | 0 | mWeakObservers.RemoveObjectAt(i); |
2458 | 0 | return NS_OK; |
2459 | 0 | } |
2460 | 0 | } |
2461 | 0 |
|
2462 | 0 | for (int32_t i = 0; i < mObservers.Count(); i++) { |
2463 | 0 | if (mObservers[i] == aObserver) { |
2464 | 0 | mObservers.RemoveObjectAt(i); |
2465 | 0 | return NS_OK; |
2466 | 0 | } |
2467 | 0 | } |
2468 | 0 |
|
2469 | 0 | return NS_OK; |
2470 | 0 | } |
2471 | | |
2472 | | NS_IMETHODIMP |
2473 | | nsOfflineCacheUpdate::GetByteProgress(uint64_t * _result) |
2474 | 0 | { |
2475 | 0 | NS_ENSURE_ARG(_result); |
2476 | 0 |
|
2477 | 0 | *_result = mByteProgress; |
2478 | 0 | return NS_OK; |
2479 | 0 | } |
2480 | | |
2481 | | NS_IMETHODIMP |
2482 | | nsOfflineCacheUpdate::Schedule() |
2483 | 0 | { |
2484 | 0 | LOG(("nsOfflineCacheUpdate::Schedule [%p]", this)); |
2485 | 0 |
|
2486 | 0 | nsOfflineCacheUpdateService* service = |
2487 | 0 | nsOfflineCacheUpdateService::EnsureService(); |
2488 | 0 |
|
2489 | 0 | if (!service) { |
2490 | 0 | return NS_ERROR_FAILURE; |
2491 | 0 | } |
2492 | 0 | |
2493 | 0 | return service->ScheduleUpdate(this); |
2494 | 0 | } |
2495 | | |
2496 | | NS_IMETHODIMP |
2497 | | nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, |
2498 | | uint32_t aState) |
2499 | 0 | { |
2500 | 0 | if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) { |
2501 | 0 | // Take the mSucceeded flag from the underlying update, we will be |
2502 | 0 | // queried for it soon. mSucceeded of this update is false (manifest |
2503 | 0 | // check failed) but the subsequent re-fetch update might succeed |
2504 | 0 | bool succeeded; |
2505 | 0 | aUpdate->GetSucceeded(&succeeded); |
2506 | 0 | mSucceeded = succeeded; |
2507 | 0 | } |
2508 | 0 |
|
2509 | 0 | NotifyState(aState); |
2510 | 0 | if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) |
2511 | 0 | aUpdate->RemoveObserver(this); |
2512 | 0 |
|
2513 | 0 | return NS_OK; |
2514 | 0 | } |
2515 | | |
2516 | | NS_IMETHODIMP |
2517 | | nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache) |
2518 | 0 | { |
2519 | 0 | AssociateDocuments(applicationCache); |
2520 | 0 | return NS_OK; |
2521 | 0 | } |
2522 | | |
2523 | | //----------------------------------------------------------------------------- |
2524 | | // nsOfflineCacheUpdate::nsIRunable |
2525 | | //----------------------------------------------------------------------------- |
2526 | | |
2527 | | NS_IMETHODIMP |
2528 | | nsOfflineCacheUpdate::Run() |
2529 | 0 | { |
2530 | 0 | ProcessNextURI(); |
2531 | 0 | return NS_OK; |
2532 | 0 | } |