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