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