uriloader/prefetch/nsOfflineCacheUpdate.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     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/. */
     6 #if defined(MOZ_LOGGING)
     7 #define FORCE_PR_LOG
     8 #endif
    10 #include "nsOfflineCacheUpdate.h"
    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"
    47 #include "nsXULAppAPI.h"
    49 using namespace mozilla;
    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;
    57 // Quota for offline apps when preloading
    58 static const int32_t  kCustomProfileQuota = 512000;
    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
    73 #undef LOG
    74 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
    76 #undef LOG_ENABLED
    77 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
    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 };
    89 namespace { // anon
    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 }
    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);
   111             messageUTF16.Append(NS_LITERAL_STRING(", URL="));
   112             messageUTF16.Append(NS_ConvertUTF8toUTF16(uriSpec));
   113         }
   114         consoleService->LogStringMessage(messageUTF16.get());
   115     }
   116 }
   118 } // anon namespace
   120 //-----------------------------------------------------------------------------
   121 // nsManifestCheck
   122 //-----------------------------------------------------------------------------
   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         {}
   137     NS_DECL_ISUPPORTS
   138     NS_DECL_NSIREQUESTOBSERVER
   139     NS_DECL_NSISTREAMLISTENER
   140     NS_DECL_NSICHANNELEVENTSINK
   141     NS_DECL_NSIINTERFACEREQUESTOR
   143     nsresult Begin();
   145 private:
   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);
   154     nsRefPtr<nsOfflineCacheUpdate> mUpdate;
   155     nsCOMPtr<nsIURI> mURI;
   156     nsCOMPtr<nsIURI> mReferrerURI;
   157     nsCOMPtr<nsICryptoHash> mManifestHash;
   158     nsCOMPtr<nsIChannel> mChannel;
   159 };
   161 //-----------------------------------------------------------------------------
   162 // nsManifestCheck::nsISupports
   163 //-----------------------------------------------------------------------------
   164 NS_IMPL_ISUPPORTS(nsManifestCheck,
   165                   nsIRequestObserver,
   166                   nsIStreamListener,
   167                   nsIChannelEventSink,
   168                   nsIInterfaceRequestor)
   170 //-----------------------------------------------------------------------------
   171 // nsManifestCheck <public>
   172 //-----------------------------------------------------------------------------
   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);
   181     rv = mManifestHash->Init(nsICryptoHash::MD5);
   182     NS_ENSURE_SUCCESS(rv, rv);
   184     rv = NS_NewChannel(getter_AddRefs(mChannel),
   185                        mURI,
   186                        nullptr, nullptr, nullptr,
   187                        nsIRequest::LOAD_BYPASS_CACHE);
   188     NS_ENSURE_SUCCESS(rv, rv);
   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     }
   200     rv = mChannel->AsyncOpen(this, nullptr);
   201     NS_ENSURE_SUCCESS(rv, rv);
   203     return NS_OK;
   204 }
   206 //-----------------------------------------------------------------------------
   207 // nsManifestCheck <public>
   208 //-----------------------------------------------------------------------------
   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);
   222     nsresult rv;
   223     *aBytesConsumed = aCount;
   225     rv = manifestCheck->mManifestHash->Update(
   226         reinterpret_cast<const uint8_t *>(aFromSegment), aCount);
   227     NS_ENSURE_SUCCESS(rv, rv);
   229     return NS_OK;
   230 }
   232 //-----------------------------------------------------------------------------
   233 // nsManifestCheck::nsIStreamListener
   234 //-----------------------------------------------------------------------------
   236 NS_IMETHODIMP
   237 nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
   238                                 nsISupports *aContext)
   239 {
   240     return NS_OK;
   241 }
   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 }
   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     }
   265     mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
   267     return NS_OK;
   268 }
   270 //-----------------------------------------------------------------------------
   271 // nsManifestCheck::nsIInterfaceRequestor
   272 //-----------------------------------------------------------------------------
   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     }
   283     return NS_ERROR_NO_INTERFACE;
   284 }
   286 //-----------------------------------------------------------------------------
   287 // nsManifestCheck::nsIChannelEventSink
   288 //-----------------------------------------------------------------------------
   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     }
   302     LogToConsole("Manifest check failed because its response is a redirect");
   304     aOldChannel->Cancel(NS_ERROR_ABORT);
   305     return NS_ERROR_ABORT;
   306 }
   308 //-----------------------------------------------------------------------------
   309 // nsOfflineCacheUpdateItem::nsISupports
   310 //-----------------------------------------------------------------------------
   312 NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateItem,
   313                   nsIRequestObserver,
   314                   nsIStreamListener,
   315                   nsIRunnable,
   316                   nsIInterfaceRequestor,
   317                   nsIChannelEventSink)
   319 //-----------------------------------------------------------------------------
   320 // nsOfflineCacheUpdateItem <public>
   321 //-----------------------------------------------------------------------------
   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 }
   339 nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
   340 {
   341 }
   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
   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     }
   362     nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
   363     NS_ENSURE_SUCCESS(rv, rv);
   365     uint32_t flags = nsIRequest::LOAD_BACKGROUND |
   366                      nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
   367                      nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
   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     }
   376     rv = NS_NewChannel(getter_AddRefs(mChannel),
   377                        mURI,
   378                        nullptr, nullptr, this,
   379                        flags);
   380     NS_ENSURE_SUCCESS(rv, rv);
   382     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
   383         do_QueryInterface(mChannel, &rv);
   385     // Support for nsIApplicationCacheChannel is required.
   386     NS_ENSURE_SUCCESS(rv, rv);
   388     // Use the existing application cache as the cache to check.
   389     rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
   390     NS_ENSURE_SUCCESS(rv, rv);
   392     // Set the new application cache as the target for write.
   393     rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
   394     NS_ENSURE_SUCCESS(rv, rv);
   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     }
   406     rv = mChannel->AsyncOpen(this, nullptr);
   407     NS_ENSURE_SUCCESS(rv, rv);
   409     mUpdate = aUpdate;
   411     mState = LoadStatus::REQUESTED;
   413     return NS_OK;
   414 }
   416 nsresult
   417 nsOfflineCacheUpdateItem::Cancel()
   418 {
   419     if (mChannel) {
   420         mChannel->Cancel(NS_ERROR_ABORT);
   421         mChannel = nullptr;
   422     }
   424     mState = LoadStatus::UNINITIALIZED;
   426     return NS_OK;
   427 }
   429 //-----------------------------------------------------------------------------
   430 // nsOfflineCacheUpdateItem::nsIStreamListener
   431 //-----------------------------------------------------------------------------
   433 NS_IMETHODIMP
   434 nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
   435                                          nsISupports *aContext)
   436 {
   437     mState = LoadStatus::RECEIVING;
   439     return NS_OK;
   440 }
   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));
   455     mUpdate->OnByteProgress(bytesRead);
   457     return NS_OK;
   458 }
   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
   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     }
   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     }
   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);
   498     return NS_OK;
   499 }
   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;
   516     nsRefPtr<nsOfflineCacheUpdate> update;
   517     update.swap(mUpdate);
   518     update->LoadCompleted(this);
   520     return NS_OK;
   521 }
   523 //-----------------------------------------------------------------------------
   524 // nsOfflineCacheUpdateItem::nsIInterfaceRequestor
   525 //-----------------------------------------------------------------------------
   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     }
   536     return NS_ERROR_NO_INTERFACE;
   537 }
   539 //-----------------------------------------------------------------------------
   540 // nsOfflineCacheUpdateItem::nsIChannelEventSink
   541 //-----------------------------------------------------------------------------
   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);
   554         aOldChannel->Cancel(NS_ERROR_ABORT);
   555         return NS_ERROR_ABORT;
   556     }
   558     nsCOMPtr<nsIURI> newURI;
   559     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
   560     if (NS_FAILED(rv))
   561         return rv;
   563     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
   564         do_QueryInterface(aNewChannel);
   565     if (appCacheChannel) {
   566         rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
   567         NS_ENSURE_SUCCESS(rv, rv);
   568     }
   570     nsAutoCString oldScheme;
   571     mURI->GetScheme(oldScheme);
   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     }
   579     // HTTP request headers are not automatically forwarded to the new channel.
   580     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
   581     NS_ENSURE_STATE(httpChannel);
   583     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
   584                                   NS_LITERAL_CSTRING("offline-resource"),
   585                                   false);
   587     mChannel = aNewChannel;
   589     cb->OnRedirectVerifyCallback(NS_OK);
   590     return NS_OK;
   591 }
   593 nsresult
   594 nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded)
   595 {
   596     *succeeded = false;
   598     if (!mChannel)
   599         return NS_OK;
   601     nsresult rv;
   602     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
   603     NS_ENSURE_SUCCESS(rv, rv);
   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);
   611     if (!reqSucceeded) {
   612         LOG(("Request failed"));
   613         return NS_OK;
   614     }
   616     nsresult channelStatus;
   617     rv = httpChannel->GetStatus(&channelStatus);
   618     NS_ENSURE_SUCCESS(rv, rv);
   620     if (NS_FAILED(channelStatus)) {
   621         LOG(("Channel status=0x%08x", channelStatus));
   622         return NS_OK;
   623     }
   625     *succeeded = true;
   626     return NS_OK;
   627 }
   629 bool
   630 nsOfflineCacheUpdateItem::IsScheduled()
   631 {
   632     return mState == LoadStatus::UNINITIALIZED;
   633 }
   635 bool
   636 nsOfflineCacheUpdateItem::IsInProgress()
   637 {
   638     return mState == LoadStatus::REQUESTED ||
   639            mState == LoadStatus::RECEIVING;
   640 }
   642 bool
   643 nsOfflineCacheUpdateItem::IsCompleted()
   644 {
   645     return mState == LoadStatus::LOADED;
   646 }
   648 nsresult
   649 nsOfflineCacheUpdateItem::GetStatus(uint16_t *aStatus)
   650 {
   651     if (!mChannel) {
   652         *aStatus = 0;
   653         return NS_OK;
   654     }
   656     nsresult rv;
   657     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
   658     NS_ENSURE_SUCCESS(rv, rv);
   660     uint32_t httpStatus;
   661     rv = httpChannel->GetResponseStatus(&httpStatus);
   662     if (rv == NS_ERROR_NOT_AVAILABLE) {
   663         *aStatus = 0;
   664         return NS_OK;
   665     }
   667     NS_ENSURE_SUCCESS(rv, rv);
   668     *aStatus = uint16_t(httpStatus);
   669     return NS_OK;
   670 }
   672 //-----------------------------------------------------------------------------
   673 // nsOfflineManifestItem
   674 //-----------------------------------------------------------------------------
   676 //-----------------------------------------------------------------------------
   677 // nsOfflineManifestItem <public>
   678 //-----------------------------------------------------------------------------
   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 }
   694 nsOfflineManifestItem::~nsOfflineManifestItem()
   695 {
   696 }
   698 //-----------------------------------------------------------------------------
   699 // nsOfflineManifestItem <private>
   700 //-----------------------------------------------------------------------------
   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);
   714     nsresult rv;
   716     *aBytesConsumed = aCount;
   718     if (manifest->mParserState == PARSE_ERROR) {
   719         // parse already failed, ignore this
   720         return NS_OK;
   721     }
   723     if (!manifest->mManifestHashInitialized) {
   724         // Avoid re-creation of crypto hash when it fails from some reason the first time
   725         manifest->mManifestHashInitialized = true;
   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     }
   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     }
   745     manifest->mReadBuf.Append(aFromSegment, aCount);
   747     nsCString::const_iterator begin, iter, end;
   748     manifest->mReadBuf.BeginReading(begin);
   749     manifest->mReadBuf.EndReading(end);
   751     for (iter = begin; iter != end; iter++) {
   752         if (*iter == '\r' || *iter == '\n') {
   753             nsresult rv = manifest->HandleManifestLine(begin, iter);
   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             }
   761             begin = iter;
   762             begin++;
   763         }
   764     }
   766     // any leftovers are saved for next time
   767     manifest->mReadBuf = Substring(begin, end);
   769     return NS_OK;
   770 }
   772 nsresult
   773 nsOfflineManifestItem::AddNamespace(uint32_t namespaceType,
   774                                     const nsCString &namespaceSpec,
   775                                     const nsCString &data)
   777 {
   778     nsresult rv;
   779     if (!mNamespaces) {
   780         mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
   781         NS_ENSURE_SUCCESS(rv, rv);
   782     }
   784     nsCOMPtr<nsIApplicationCacheNamespace> ns =
   785         do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv);
   786     NS_ENSURE_SUCCESS(rv, rv);
   788     rv = ns->Init(namespaceType, namespaceSpec, data);
   789     NS_ENSURE_SUCCESS(rv, rv);
   791     rv = mNamespaces->AppendElement(ns, false);
   792     NS_ENSURE_SUCCESS(rv, rv);
   794     return NS_OK;
   795 }
   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;
   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     }
   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         }
   824         const nsCSubstring &magic = Substring(begin, end);
   826         if (!magic.EqualsLiteral("CACHE MANIFEST")) {
   827             mParserState = PARSE_ERROR;
   828             LogToConsole("Offline cache manifest magic incorect", this);
   829             return NS_OK;
   830         }
   832         mParserState = PARSE_CACHE_ENTRIES;
   833         return NS_OK;
   834     }
   836     // lines other than the first ignore leading spaces and tabs
   837     while (begin != end && (*begin == ' ' || *begin == '\t'))
   838         begin++;
   840     // ignore blank lines and comments
   841     if (begin == end || *begin == '#')
   842         return NS_OK;
   844     const nsCSubstring &line = Substring(begin, end);
   846     if (line.EqualsLiteral("CACHE:")) {
   847         mParserState = PARSE_CACHE_ENTRIES;
   848         return NS_OK;
   849     }
   851     if (line.EqualsLiteral("FALLBACK:")) {
   852         mParserState = PARSE_FALLBACK_ENTRIES;
   853         return NS_OK;
   854     }
   856     if (line.EqualsLiteral("NETWORK:")) {
   857         mParserState = PARSE_BYPASS_ENTRIES;
   858         return NS_OK;
   859     }
   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     }
   868     nsresult rv;
   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     }
   877     case PARSE_UNKNOWN_SECTION: {
   878         // just jump over
   879         return NS_OK;
   880     }
   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;
   890         nsAutoCString scheme;
   891         uri->GetScheme(scheme);
   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;
   898         mExplicitURIs.AppendObject(uri);
   899         break;
   900     }
   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         }
   910         nsCString namespaceSpec(Substring(line, 0, separator));
   911         nsCString fallbackSpec(Substring(line, separator + 1));
   912         namespaceSpec.CompressWhitespace();
   913         fallbackSpec.CompressWhitespace();
   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;
   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;
   936         // Manifest and namespace must be same origin
   937         if (!NS_SecurityCompareURIs(mURI, namespaceURI,
   938                                     mStrictFileOriginPolicy))
   939             break;
   941         // Fallback and namespace must be same origin
   942         if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
   943                                     mStrictFileOriginPolicy))
   944             break;
   946         mFallbackURIs.AppendObject(fallbackURI);
   948         AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
   949                      namespaceSpec, fallbackSpec);
   950         break;
   951     }
   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         }
   966         nsCOMPtr<nsIURI> bypassURI;
   967         rv = NS_NewURI(getter_AddRefs(bypassURI), line, nullptr, mURI);
   968         if (NS_FAILED(rv))
   969             break;
   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;
   982         AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
   983                      spec, EmptyCString());
   984         break;
   985     }
   986     }
   988     return NS_OK;
   989 }
   991 nsresult
   992 nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
   993 {
   994     nsresult rv;
   996     nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
   997     NS_ENSURE_SUCCESS(rv, rv);
   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);
  1007         rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
  1008         if (NS_FAILED(rv))
  1009             mOldManifestHashValue.Truncate();
  1012     return NS_OK;
  1015 nsresult
  1016 nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
  1018     nsresult rv;
  1020     if (!mManifestHash) {
  1021         // Nothing to compare against...
  1022         return NS_OK;
  1025     nsCString newManifestHashValue;
  1026     rv = mManifestHash->Finish(true, mManifestHashValue);
  1027     mManifestHash = nullptr;
  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;
  1035     if (!ParseSucceeded()) {
  1036         // Parsing failed, the hash is not valid
  1037         return NS_OK;
  1040     if (mOldManifestHashValue == mManifestHashValue) {
  1041         LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
  1042         mNeedsUpdate = false;
  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);
  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);
  1056         rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
  1057         NS_ENSURE_SUCCESS(rv, rv);
  1060     return NS_OK;
  1063 void
  1064 nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
  1066     mStrictFileOriginPolicy =
  1067         Preferences::GetBool("security.fileuri.strict_origin_policy", true);
  1070 NS_IMETHODIMP
  1071 nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
  1072                                       nsISupports *aContext)
  1074     nsresult rv;
  1076     nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
  1077     NS_ENSURE_SUCCESS(rv, rv);
  1079     bool succeeded;
  1080     rv = channel->GetRequestSucceeded(&succeeded);
  1081     NS_ENSURE_SUCCESS(rv, rv);
  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;
  1090     rv = GetOldManifestContentHash(aRequest);
  1091     NS_ENSURE_SUCCESS(rv, rv);
  1093     return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
  1096 NS_IMETHODIMP
  1097 nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
  1098                                        nsISupports *aContext,
  1099                                        nsIInputStream *aStream,
  1100                                        uint64_t aOffset,
  1101                                        uint32_t aCount)
  1103     uint32_t bytesRead = 0;
  1104     aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
  1105     mBytesRead += bytesRead;
  1107     if (mParserState == PARSE_ERROR) {
  1108         LOG(("OnDataAvailable is canceling the request due a parse error\n"));
  1109         return NS_ERROR_ABORT;
  1112     LOG(("loaded %u bytes into offline cache [offset=%u]\n",
  1113          bytesRead, aOffset));
  1115     // All the parent method does is read and discard, don't bother
  1116     // chaining up.
  1118     return NS_OK;
  1121 NS_IMETHODIMP
  1122 nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
  1123                                      nsISupports *aContext,
  1124                                      nsresult aStatus)
  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);
  1138         rv = CheckNewManifestContentHash(aRequest);
  1139         NS_ENSURE_SUCCESS(rv, rv);
  1142     return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
  1145 //-----------------------------------------------------------------------------
  1146 // nsOfflineCacheUpdate::nsISupports
  1147 //-----------------------------------------------------------------------------
  1149 NS_IMPL_ISUPPORTS(nsOfflineCacheUpdate,
  1150                   nsIOfflineCacheUpdateObserver,
  1151                   nsIOfflineCacheUpdate,
  1152                   nsIRunnable)
  1154 //-----------------------------------------------------------------------------
  1155 // nsOfflineCacheUpdate <public>
  1156 //-----------------------------------------------------------------------------
  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)
  1174 nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
  1176     LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
  1179 /* static */
  1180 nsresult
  1181 nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
  1183     aKey.Truncate();
  1185     nsCOMPtr<nsIURI> newURI;
  1186     nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(newURI));
  1187     NS_ENSURE_SUCCESS(rv, rv);
  1189     rv = newURI->GetAsciiSpec(aKey);
  1190     NS_ENSURE_SUCCESS(rv, rv);
  1192     return NS_OK;
  1195 nsresult
  1196 nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI)
  1198     nsresult rv;
  1200     // Only http and https applications are supported.
  1201     bool match;
  1202     rv = aManifestURI->SchemeIs("http", &match);
  1203     NS_ENSURE_SUCCESS(rv, rv);
  1205     if (!match) {
  1206         rv = aManifestURI->SchemeIs("https", &match);
  1207         NS_ENSURE_SUCCESS(rv, rv);
  1208         if (!match)
  1209             return NS_ERROR_ABORT;
  1212     mManifestURI = aManifestURI;
  1214     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
  1215     NS_ENSURE_SUCCESS(rv, rv);
  1217     mPartialUpdate = false;
  1219     return NS_OK;
  1222 nsresult
  1223 nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
  1224                            nsIURI *aDocumentURI,
  1225                            nsIDOMDocument *aDocument,
  1226                            nsIFile *aCustomProfileDir,
  1227                            uint32_t aAppID,
  1228                            bool aInBrowser)
  1230     nsresult rv;
  1232     // Make sure the service has been initialized
  1233     nsOfflineCacheUpdateService* service =
  1234         nsOfflineCacheUpdateService::EnsureService();
  1235     if (!service)
  1236         return NS_ERROR_FAILURE;
  1238     LOG(("nsOfflineCacheUpdate::Init [%p]", this));
  1240     rv = InitInternal(aManifestURI);
  1241     NS_ENSURE_SUCCESS(rv, rv);
  1243     nsCOMPtr<nsIApplicationCacheService> cacheService =
  1244         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
  1245     NS_ENSURE_SUCCESS(rv, rv);
  1247     mDocumentURI = aDocumentURI;
  1249     if (aCustomProfileDir) {
  1250         rv = cacheService->BuildGroupIDForApp(aManifestURI,
  1251                                               aAppID, aInBrowser,
  1252                                               mGroupID);
  1253         NS_ENSURE_SUCCESS(rv, rv);
  1255         // Create only a new offline application cache in the custom profile
  1256         // This is a preload of a new cache.
  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;
  1263         rv = cacheService->CreateCustomApplicationCache(mGroupID,
  1264                                                         aCustomProfileDir,
  1265                                                         kCustomProfileQuota,
  1266                                                         getter_AddRefs(mApplicationCache));
  1267         NS_ENSURE_SUCCESS(rv, rv);
  1269         mCustomProfileDir = aCustomProfileDir;
  1271     else {
  1272         rv = cacheService->BuildGroupIDForApp(aManifestURI,
  1273                                               aAppID, aInBrowser,
  1274                                               mGroupID);
  1275         NS_ENSURE_SUCCESS(rv, rv);
  1277         rv = cacheService->GetActiveCache(mGroupID,
  1278                                           getter_AddRefs(mPreviousApplicationCache));
  1279         NS_ENSURE_SUCCESS(rv, rv);
  1281         rv = cacheService->CreateApplicationCache(mGroupID,
  1282                                                   getter_AddRefs(mApplicationCache));
  1283         NS_ENSURE_SUCCESS(rv, rv);
  1286     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
  1287                                                              nullptr,
  1288                                                              &mPinned);
  1289     NS_ENSURE_SUCCESS(rv, rv);
  1291     mAppID = aAppID;
  1292     mInBrowser = aInBrowser;
  1294     mState = STATE_INITIALIZED;
  1295     return NS_OK;
  1298 nsresult
  1299 nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI,
  1300                                          uint32_t aAppID,
  1301                                          bool aInBrowser,
  1302                                          nsIObserver *aObserver)
  1304     nsresult rv;
  1306     // Make sure the service has been initialized
  1307     nsOfflineCacheUpdateService* service =
  1308         nsOfflineCacheUpdateService::EnsureService();
  1309     if (!service)
  1310         return NS_ERROR_FAILURE;
  1312     LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this));
  1314     rv = InitInternal(aManifestURI);
  1315     NS_ENSURE_SUCCESS(rv, rv);
  1317     nsCOMPtr<nsIApplicationCacheService> cacheService =
  1318         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
  1319     NS_ENSURE_SUCCESS(rv, rv);
  1321     rv = cacheService->BuildGroupIDForApp(aManifestURI,
  1322                                           aAppID, aInBrowser,
  1323                                           mGroupID);
  1324     NS_ENSURE_SUCCESS(rv, rv);
  1326     rv = cacheService->GetActiveCache(mGroupID,
  1327                                       getter_AddRefs(mPreviousApplicationCache));
  1328     NS_ENSURE_SUCCESS(rv, rv);
  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;
  1338     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI,
  1339                                                              nullptr,
  1340                                                              &mPinned);
  1341     NS_ENSURE_SUCCESS(rv, rv);
  1343     mUpdateAvailableObserver = aObserver;
  1344     mOnlyCheckUpdate = true;
  1346     mState = STATE_INITIALIZED;
  1347     return NS_OK;
  1350 nsresult
  1351 nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
  1352                                   const nsACString& clientID,
  1353                                   nsIURI *aDocumentURI)
  1355     nsresult rv;
  1357     // Make sure the service has been initialized
  1358     nsOfflineCacheUpdateService* service =
  1359         nsOfflineCacheUpdateService::EnsureService();
  1360     if (!service)
  1361         return NS_ERROR_FAILURE;
  1363     LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
  1365     mPartialUpdate = true;
  1366     mDocumentURI = aDocumentURI;
  1368     mManifestURI = aManifestURI;
  1369     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
  1370     NS_ENSURE_SUCCESS(rv, rv);
  1372     nsCOMPtr<nsIApplicationCacheService> cacheService =
  1373         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
  1374     NS_ENSURE_SUCCESS(rv, rv);
  1376     rv = cacheService->GetApplicationCache(clientID,
  1377                                            getter_AddRefs(mApplicationCache));
  1378     NS_ENSURE_SUCCESS(rv, rv);
  1380     if (!mApplicationCache) {
  1381         nsAutoCString manifestSpec;
  1382         rv = GetCacheKey(mManifestURI, manifestSpec);
  1383         NS_ENSURE_SUCCESS(rv, rv);
  1385         rv = cacheService->CreateApplicationCache
  1386             (manifestSpec, getter_AddRefs(mApplicationCache));
  1387         NS_ENSURE_SUCCESS(rv, rv);
  1390     rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI));
  1391     NS_ENSURE_SUCCESS(rv, rv);
  1393     nsAutoCString groupID;
  1394     rv = mApplicationCache->GetGroupID(groupID);
  1395     NS_ENSURE_SUCCESS(rv, rv);
  1397     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
  1398                                                              nullptr,
  1399                                                              &mPinned);
  1400     NS_ENSURE_SUCCESS(rv, rv);
  1402     mState = STATE_INITIALIZED;
  1403     return NS_OK;
  1406 nsresult
  1407 nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate)
  1409     // Be pessimistic
  1410     *aDoUpdate = false;
  1412     bool succeeded;
  1413     nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
  1414     NS_ENSURE_SUCCESS(rv, rv);
  1416     if (!succeeded || !mManifestItem->ParseSucceeded()) {
  1417         return NS_ERROR_FAILURE;
  1420     if (!mManifestItem->NeedsUpdate()) {
  1421         return NS_OK;
  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);
  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);
  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);
  1442     // Add items previously cached implicitly
  1443     rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT);
  1444     NS_ENSURE_SUCCESS(rv, rv);
  1446     // Add items requested by the script API
  1447     rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
  1448     NS_ENSURE_SUCCESS(rv, rv);
  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);
  1456     *aDoUpdate = true;
  1458     return NS_OK;
  1461 bool
  1462 nsOfflineCacheUpdate::CheckUpdateAvailability()
  1464     nsresult rv;
  1466     bool succeeded;
  1467     rv = mManifestItem->GetRequestSucceeded(&succeeded);
  1468     NS_ENSURE_SUCCESS(rv, false);
  1470     if (!succeeded || !mManifestItem->ParseSucceeded()) {
  1471         return false;
  1474     if (!mPinned) {
  1475         uint16_t status;
  1476         rv = mManifestItem->GetStatus(&status);
  1477         NS_ENSURE_SUCCESS(rv, false);
  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;
  1487     return mManifestItem->NeedsUpdate();
  1490 void
  1491 nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem)
  1493     nsresult rv;
  1495     LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
  1497     if (mState == STATE_FINISHED) {
  1498         LOG(("  after completion, ignoring"));
  1499         return;
  1502     // Keep the object alive through a Finish() call.
  1503     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
  1505     if (mState == STATE_CANCELLED) {
  1506         NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  1507         Finish();
  1508         return;
  1511     if (mState == STATE_CHECKING) {
  1512         // Manifest load finished.
  1514         if (mOnlyCheckUpdate) {
  1515             Finish();
  1516             NotifyUpdateAvailability(CheckUpdateAvailability());
  1517             return;
  1520         NS_ASSERTION(mManifestItem,
  1521                      "Must have a manifest item in STATE_CHECKING.");
  1522         NS_ASSERTION(mManifestItem == aItem,
  1523                      "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted");
  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;
  1541             } else {
  1542                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  1543                 mObsolete = true;
  1545             Finish();
  1546             return;
  1549         bool doUpdate;
  1550         if (NS_FAILED(HandleManifest(&doUpdate))) {
  1551             mSucceeded = false;
  1552             NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  1553             Finish();
  1554             return;
  1557         if (!doUpdate) {
  1558             LogToConsole("Offline cache doesn't need to update", mManifestItem);
  1560             mSucceeded = false;
  1562             AssociateDocuments(mPreviousApplicationCache);
  1564             ScheduleImplicit();
  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();
  1572             return;
  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;
  1584         mState = STATE_DOWNLOADING;
  1585         NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
  1587         // Start fetching resources.
  1588         ProcessNextURI();
  1590         return;
  1593     // Normal load finished.
  1594     if (mItemsInProgress) // Just to be safe here!
  1595       --mItemsInProgress;
  1597     bool succeeded;
  1598     rv = aItem->GetRequestSucceeded(&succeeded);
  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
  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;
  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;
  1627             mPinnedEntryRetriesCount++;
  1629             LogToConsole("An unpinned offline cache deleted");
  1631             // Retry this item.
  1632             ProcessNextURI();
  1633             return;
  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;
  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;
  1652     } else {
  1653         rv = mApplicationCache->MarkEntry(aItem->mCacheKey, aItem->mItemType);
  1654         if (NS_FAILED(rv)) {
  1655             mSucceeded = false;
  1659     if (!mSucceeded) {
  1660         NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  1661         Finish();
  1662         return;
  1665     NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED);
  1667     ProcessNextURI();
  1670 void
  1671 nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
  1672                                              const nsCString &aManifestHash)
  1674     // Keep the object alive through a Finish() call.
  1675     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
  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;
  1687     if (NS_FAILED(aStatus)) {
  1688         mSucceeded = false;
  1689         NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  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();
  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);
  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]);
  1714         newUpdate->mRescheduleCount = mRescheduleCount + 1;
  1715         newUpdate->AddObserver(this, false);
  1716         newUpdate->Schedule();
  1718     else {
  1719         LogToConsole("Offline cache update done", mManifestItem);
  1720         Finish();
  1724 nsresult
  1725 nsOfflineCacheUpdate::Begin()
  1727     LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
  1729     // Keep the object alive through a ProcessNextURI()/Finish() call.
  1730     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
  1732     mItemsInProgress = 0;
  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);
  1741       return NS_OK;
  1744     if (mPartialUpdate) {
  1745         mState = STATE_DOWNLOADING;
  1746         NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
  1747         ProcessNextURI();
  1748         return NS_OK;
  1751     // Start checking the manifest.
  1752     nsCOMPtr<nsIURI> uri;
  1754     mManifestItem = new nsOfflineManifestItem(mManifestURI,
  1755                                               mDocumentURI,
  1756                                               mApplicationCache,
  1757                                               mPreviousApplicationCache);
  1758     if (!mManifestItem) {
  1759         return NS_ERROR_OUT_OF_MEMORY;
  1762     mState = STATE_CHECKING;
  1763     mByteProgress = 0;
  1764     NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING);
  1766     nsresult rv = mManifestItem->OpenChannel(this);
  1767     if (NS_FAILED(rv)) {
  1768         LoadCompleted(mManifestItem);
  1771     return NS_OK;
  1774 //-----------------------------------------------------------------------------
  1775 // nsOfflineCacheUpdate <private>
  1776 //-----------------------------------------------------------------------------
  1778 nsresult
  1779 nsOfflineCacheUpdate::AddExistingItems(uint32_t aType,
  1780                                        nsTArray<nsCString>* namespaceFilter)
  1782     if (!mPreviousApplicationCache) {
  1783         return NS_OK;
  1786     if (namespaceFilter && namespaceFilter->Length() == 0) {
  1787         // Don't bother to walk entries when there are no namespaces
  1788         // defined.
  1789         return NS_OK;
  1792     uint32_t count = 0;
  1793     char **keys = nullptr;
  1794     nsresult rv = mPreviousApplicationCache->GatherEntries(aType,
  1795                                                            &count, &keys);
  1796     NS_ENSURE_SUCCESS(rv, rv);
  1798     AutoFreeArray autoFree(count, keys);
  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));
  1808             if (!found)
  1809                 continue;
  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);
  1819     return NS_OK;
  1822 nsresult
  1823 nsOfflineCacheUpdate::ProcessNextURI()
  1825     // Keep the object alive through a Finish() call.
  1826     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
  1828     LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, inprogress=%d, numItems=%d]",
  1829          this, mItemsInProgress, mItems.Length()));
  1831     if (mState != STATE_DOWNLOADING) {
  1832         LOG(("  should only be called from the DOWNLOADING state, ignoring"));
  1833         return NS_ERROR_UNEXPECTED;
  1836     nsOfflineCacheUpdateItem * runItem = nullptr;
  1837     uint32_t completedItems = 0;
  1838     for (uint32_t i = 0; i < mItems.Length(); ++i) {
  1839         nsOfflineCacheUpdateItem * item = mItems[i];
  1841         if (item->IsScheduled()) {
  1842             runItem = item;
  1843             break;
  1846         if (item->IsCompleted())
  1847             ++completedItems;
  1850     if (completedItems == mItems.Length()) {
  1851         LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]: all items loaded", this));
  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();
  1868             return NS_OK;
  1872     if (!runItem) {
  1873         LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
  1874              " No more items to include in parallel load", this));
  1875         return NS_OK;
  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()));
  1884 #endif
  1886     ++mItemsInProgress;
  1887     NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED);
  1889     nsresult rv = runItem->OpenChannel(this);
  1890     if (NS_FAILED(rv)) {
  1891         LoadCompleted(runItem);
  1892         return rv;
  1895     if (mItemsInProgress >= kParallelLoadLimit) {
  1896         LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
  1897              " At parallel load limit", this));
  1898         return NS_OK;
  1901     // This calls this method again via a post triggering
  1902     // a parallel item load
  1903     return NS_DispatchToCurrentThread(this);
  1906 void
  1907 nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
  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--);
  1918     for (int32_t i = 0; i < mObservers.Count(); i++) {
  1919         aObservers.AppendObject(mObservers[i]);
  1923 void
  1924 nsOfflineCacheUpdate::NotifyState(uint32_t state)
  1926     LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state));
  1928     if (state == STATE_ERROR) {
  1929         LogToConsole("Offline cache update error", mManifestItem);
  1932     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
  1933     GatherObservers(observers);
  1935     for (int32_t i = 0; i < observers.Count(); i++) {
  1936         observers[i]->UpdateStateChanged(this, state);
  1940 void
  1941 nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable)
  1943     if (!mUpdateAvailableObserver)
  1944         return;
  1946     LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]",
  1947          this, updateAvailable));
  1949     const char* topic = updateAvailable
  1950                       ? "offline-cache-update-available"
  1951                       : "offline-cache-update-unavailable";
  1953     nsCOMPtr<nsIObserver> observer;
  1954     observer.swap(mUpdateAvailableObserver);
  1955     observer->Observe(mManifestURI, topic, nullptr);
  1958 void
  1959 nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
  1961     if (!cache) {
  1962         LOG(("nsOfflineCacheUpdate::AssociateDocuments bypassed"
  1963              ", no cache provided [this=%p]", this));
  1964         return;
  1967     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
  1968     GatherObservers(observers);
  1970     for (int32_t i = 0; i < observers.Count(); i++) {
  1971         observers[i]->ApplicationCacheAvailable(cache);
  1975 void
  1976 nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI)
  1978     if (!aDocumentURI)
  1979       return;
  1981     mDocumentURIs.AppendObject(aDocumentURI);
  1984 void
  1985 nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
  1987     NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
  1988     mOwner = aOwner->asWeakPtr();
  1991 bool
  1992 nsOfflineCacheUpdate::IsForGroupID(const nsCSubstring &groupID)
  1994     return mGroupID == groupID;
  1997 nsresult
  1998 nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
  2000     // Keep the object alive through a Finish() call.
  2001     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
  2003     mImplicitUpdate = nullptr;
  2005     NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
  2006     Finish();
  2008     return NS_OK;
  2011 void
  2012 nsOfflineCacheUpdate::OnByteProgress(uint64_t byteIncrement)
  2014     mByteProgress += byteIncrement;
  2015     NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMPROGRESS);
  2018 nsresult
  2019 nsOfflineCacheUpdate::ScheduleImplicit()
  2021     if (mDocumentURIs.Count() == 0)
  2022         return NS_OK;
  2024     nsresult rv;
  2026     nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
  2027     NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
  2029     nsAutoCString clientID;
  2030     if (mPreviousApplicationCache) {
  2031         rv = mPreviousApplicationCache->GetClientID(clientID);
  2032         NS_ENSURE_SUCCESS(rv, rv);
  2034     else if (mApplicationCache) {
  2035         rv = mApplicationCache->GetClientID(clientID);
  2036         NS_ENSURE_SUCCESS(rv, rv);
  2038     else {
  2039         NS_ERROR("Offline cache update not having set mApplicationCache?");
  2042     rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
  2043     NS_ENSURE_SUCCESS(rv, rv);
  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);
  2051     update->SetOwner(this);
  2052     rv = update->Begin();
  2053     NS_ENSURE_SUCCESS(rv, rv);
  2055     mImplicitUpdate = update;
  2057     return NS_OK;
  2060 nsresult
  2061 nsOfflineCacheUpdate::FinishNoNotify()
  2063     LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
  2065     mState = STATE_FINISHED;
  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;
  2076             rv = mApplicationCache->Activate();
  2077             if (NS_FAILED(rv)) {
  2078                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
  2079                 mSucceeded = false;
  2082             AssociateDocuments(mApplicationCache);
  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);
  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();
  2101             mApplicationCache->Discard();
  2105     nsresult rv = NS_OK;
  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>();
  2114     return rv;
  2117 nsresult
  2118 nsOfflineCacheUpdate::Finish()
  2120     nsresult rv = FinishNoNotify();
  2122     NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED);
  2124     return rv;
  2127 void
  2128 nsOfflineCacheUpdate::AsyncFinishWithError()
  2130     NotifyState(nsOfflineCacheUpdate::STATE_ERROR);
  2131     Finish();
  2134 static nsresult
  2135 EvictOneOfCacheGroups(nsIApplicationCacheService *cacheService,
  2136                       uint32_t count, const char * const *groups)
  2138     nsresult rv;
  2139     unsigned int i;
  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);
  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;
  2153         bool pinned;
  2154         rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(uri,
  2155                                                                  nullptr,
  2156                                                                  &pinned);
  2157         NS_ENSURE_SUCCESS(rv, rv);
  2159         if (!pinned) {
  2160             rv = cache->Discard();
  2161             return NS_OK;
  2165     return NS_ERROR_FILE_NOT_FOUND;
  2168 nsresult
  2169 nsOfflineCacheUpdate::EvictOneNonPinned()
  2171     nsresult rv;
  2173     nsCOMPtr<nsIApplicationCacheService> cacheService =
  2174         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
  2175     NS_ENSURE_SUCCESS(rv, rv);
  2177     uint32_t count;
  2178     char **groups;
  2179     rv = cacheService->GetGroupsTimeOrdered(&count, &groups);
  2180     NS_ENSURE_SUCCESS(rv, rv);
  2182     rv = EvictOneOfCacheGroups(cacheService, count, groups);
  2184     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, groups);
  2185     return rv;
  2188 //-----------------------------------------------------------------------------
  2189 // nsOfflineCacheUpdate::nsIOfflineCacheUpdate
  2190 //-----------------------------------------------------------------------------
  2192 NS_IMETHODIMP
  2193 nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
  2195     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  2197     aUpdateDomain = mUpdateDomain;
  2198     return NS_OK;
  2201 NS_IMETHODIMP
  2202 nsOfflineCacheUpdate::GetStatus(uint16_t *aStatus)
  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;
  2216     return NS_ERROR_FAILURE;
  2219 NS_IMETHODIMP
  2220 nsOfflineCacheUpdate::GetPartial(bool *aPartial)
  2222     *aPartial = mPartialUpdate || mOnlyCheckUpdate;
  2223     return NS_OK;
  2226 NS_IMETHODIMP
  2227 nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
  2229     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  2231     NS_IF_ADDREF(*aManifestURI = mManifestURI);
  2232     return NS_OK;
  2235 NS_IMETHODIMP
  2236 nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded)
  2238     NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
  2240     *aSucceeded = mSucceeded;
  2242     return NS_OK;
  2245 NS_IMETHODIMP
  2246 nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade)
  2248     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  2250     *aIsUpgrade = (mPreviousApplicationCache != nullptr);
  2252     return NS_OK;
  2255 nsresult
  2256 nsOfflineCacheUpdate::AddURI(nsIURI *aURI, uint32_t aType)
  2258     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  2260     if (mState >= STATE_DOWNLOADING)
  2261         return NS_ERROR_NOT_AVAILABLE;
  2263     // Resource URIs must have the same scheme as the manifest.
  2264     nsAutoCString scheme;
  2265     aURI->GetScheme(scheme);
  2267     bool match;
  2268     if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
  2269         return NS_ERROR_FAILURE;
  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;
  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;
  2289     mItems.AppendElement(item);
  2290     mAddedItems = true;
  2292     return NS_OK;
  2295 NS_IMETHODIMP
  2296 nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
  2298     if (GeckoProcessType_Default != XRE_GetProcessType())
  2299         return NS_ERROR_NOT_IMPLEMENTED;
  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);
  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);
  2314             return NS_OK;
  2318     return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
  2321 NS_IMETHODIMP
  2322 nsOfflineCacheUpdate::Cancel()
  2324     LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
  2326     if ((mState == STATE_FINISHED) || (mState == STATE_CANCELLED)) {
  2327       return NS_ERROR_NOT_AVAILABLE;
  2330     mState = STATE_CANCELLED;
  2331     mSucceeded = false;
  2333     // Cancel all running downloads
  2334     for (uint32_t i = 0; i < mItems.Length(); ++i) {
  2335         nsOfflineCacheUpdateItem * item = mItems[i];
  2337         if (item->IsInProgress())
  2338             item->Cancel();
  2341     return NS_OK;
  2344 NS_IMETHODIMP
  2345 nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
  2346                                   bool aHoldWeak)
  2348     LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this));
  2350     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  2352     if (aHoldWeak) {
  2353         nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
  2354         mWeakObservers.AppendObject(weakRef);
  2355     } else {
  2356         mObservers.AppendObject(aObserver);
  2359     return NS_OK;
  2362 NS_IMETHODIMP
  2363 nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
  2365     LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this));
  2367     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
  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;
  2378     for (int32_t i = 0; i < mObservers.Count(); i++) {
  2379         if (mObservers[i] == aObserver) {
  2380             mObservers.RemoveObjectAt(i);
  2381             return NS_OK;
  2385     return NS_OK;
  2388 NS_IMETHODIMP
  2389 nsOfflineCacheUpdate::GetByteProgress(uint64_t * _result)
  2391     NS_ENSURE_ARG(_result);
  2393     *_result = mByteProgress;
  2394     return NS_OK;
  2397 NS_IMETHODIMP
  2398 nsOfflineCacheUpdate::Schedule()
  2400     LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
  2402     nsOfflineCacheUpdateService* service =
  2403         nsOfflineCacheUpdateService::EnsureService();
  2405     if (!service) {
  2406         return NS_ERROR_FAILURE;
  2409     return service->ScheduleUpdate(this);
  2412 NS_IMETHODIMP
  2413 nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
  2414                                          uint32_t aState)
  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;
  2425     NotifyState(aState);
  2426     if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED)
  2427         aUpdate->RemoveObserver(this);
  2429     return NS_OK;
  2432 NS_IMETHODIMP
  2433 nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache)
  2435     AssociateDocuments(applicationCache);
  2436     return NS_OK;
  2439 //-----------------------------------------------------------------------------
  2440 // nsOfflineCacheUpdate::nsIRunable
  2441 //-----------------------------------------------------------------------------
  2443 NS_IMETHODIMP
  2444 nsOfflineCacheUpdate::Run()
  2446     ProcessNextURI();
  2447     return NS_OK;

mercurial