LCOV - code coverage report
Current view: top level - uriloader/prefetch - nsOfflineCacheUpdate.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1111 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 99 0.0 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13