LCOV - app.info - uriloader/prefetch/nsOfflineCacheUpdate.cpp

LCOV - code coverage report
| Current view: | directory - uriloader/prefetch - nsOfflineCacheUpdate.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 854 | 480 | 56.2 %
| Date: | 2012-07-07 | Functions: | 85 | 60 | 70.6 %

           1                 : /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
           2                 : /* ***** BEGIN LICENSE BLOCK *****
           3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           4                 :  *
           5                 :  * The contents of this file are subject to the Mozilla Public License Version
           6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           7                 :  * the License. You may obtain a copy of the License at
           8                 :  * http://www.mozilla.org/MPL/
           9                 :  *
          10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          12                 :  * for the specific language governing rights and limitations under the
          13                 :  * License.
          14                 :  *
          15                 :  * The Original Code is mozilla.org code.
          16                 :  *
          17                 :  * The Initial Developer of the Original Code is
          18                 :  * Mozilla Corporation
          19                 :  * Portions created by the Initial Developer are Copyright (C) 2007
          20                 :  * the Initial Developer. All Rights Reserved.
          21                 :  *
          22                 :  * Contributor(s):
          23                 :  *   Dave Camp <dcamp@mozilla.com>
          24                 :  *
          25                 :  * Alternatively, the contents of this file may be used under the terms of
          26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          29                 :  * of those above. If you wish to allow use of your version of this file only
          30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          31                 :  * use your version of this file under the terms of the MPL, indicate your
          32                 :  * decision by deleting the provisions above and replace them with the notice
          33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          34                 :  * the provisions above, a recipient may use your version of this file under
          35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          36                 :  *
          37                 :  * ***** END LICENSE BLOCK ***** */
          38                 :
          39                 : #include "nsOfflineCacheUpdate.h"
          40                 :
          41                 : #include "nsCPrefetchService.h"
          42                 : #include "nsCURILoader.h"
          43                 : #include "nsIApplicationCacheContainer.h"
          44                 : #include "nsIApplicationCacheChannel.h"
          45                 : #include "nsIApplicationCacheService.h"
          46                 : #include "nsICache.h"
          47                 : #include "nsICacheService.h"
          48                 : #include "nsICacheSession.h"
          49                 : #include "nsICachingChannel.h"
          50                 : #include "nsIContent.h"
          51                 : #include "mozilla/dom/Element.h"
          52                 : #include "nsIDocumentLoader.h"
          53                 : #include "nsIDOMElement.h"
          54                 : #include "nsIDOMWindow.h"
          55                 : #include "nsIDOMOfflineResourceList.h"
          56                 : #include "nsIDocument.h"
          57                 : #include "nsIObserverService.h"
          58                 : #include "nsIURL.h"
          59                 : #include "nsIWebProgress.h"
          60                 : #include "nsICryptoHash.h"
          61                 : #include "nsICacheEntryDescriptor.h"
          62                 : #include "nsIPermissionManager.h"
          63                 : #include "nsIPrincipal.h"
          64                 : #include "nsNetCID.h"
          65                 : #include "nsNetUtil.h"
          66                 : #include "nsServiceManagerUtils.h"
          67                 : #include "nsStreamUtils.h"
          68                 : #include "nsThreadUtils.h"
          69                 : #include "nsProxyRelease.h"
          70                 : #include "prlog.h"
          71                 : #include "nsIAsyncVerifyRedirectCallback.h"
          72                 : #include "mozilla/Preferences.h"
          73                 :
          74                 : #include "nsXULAppAPI.h"
          75                 :
          76                 : using namespace mozilla;
          77                 :
          78                 : static const PRUint32 kRescheduleLimit = 3;
          79                 :
          80                 : #if defined(PR_LOGGING)
          81                 : //
          82                 : // To enable logging (see prlog.h for full details):
          83                 : //
          84                 : //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
          85                 : //    set NSPR_LOG_FILE=offlineupdate.log
          86                 : //
          87                 : // this enables PR_LOG_ALWAYS level information and places all output in
          88                 : // the file offlineupdate.log
          89                 : //
          90                 : extern PRLogModuleInfo *gOfflineCacheUpdateLog;
          91                 : #endif
          92                 : #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
          93                 : #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
          94                 :
          95                 : class AutoFreeArray {
          96                 : public:
          97               0 :     AutoFreeArray(PRUint32 count, char **values)
          98               0 :         : mCount(count), mValues(values) {};
          99               0 :     ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
         100                 : private:
         101                 :     PRUint32 mCount;
         102                 :     char **mValues;
         103                 : };
         104                 :
         105                 : static nsresult
         106              16 : DropReferenceFromURL(nsIURI * aURI)
         107                 : {
         108                 :     // XXXdholbert If this SetRef fails, callers of this method probably
         109                 :     // want to call aURI->CloneIgnoringRef() and use the result of that.
         110              16 :     return aURI->SetRef(EmptyCString());
         111                 : }
         112                 :
         113                 : //-----------------------------------------------------------------------------
         114                 : // nsManifestCheck
         115                 : //-----------------------------------------------------------------------------
         116                 :
         117                 : class nsManifestCheck : public nsIStreamListener
         118                 :                       , public nsIChannelEventSink
         119                 :                       , public nsIInterfaceRequestor
         120               8 : {
         121                 : public:
         122               8 :     nsManifestCheck(nsOfflineCacheUpdate *aUpdate,
         123                 :                     nsIURI *aURI,
         124                 :                     nsIURI *aReferrerURI)
         125                 :         : mUpdate(aUpdate)
         126                 :         , mURI(aURI)
         127               8 :         , mReferrerURI(aReferrerURI)
         128               8 :         {}
         129                 :
         130                 :     NS_DECL_ISUPPORTS
         131                 :     NS_DECL_NSIREQUESTOBSERVER
         132                 :     NS_DECL_NSISTREAMLISTENER
         133                 :     NS_DECL_NSICHANNELEVENTSINK
         134                 :     NS_DECL_NSIINTERFACEREQUESTOR
         135                 :
         136                 :     nsresult Begin();
         137                 :
         138                 : private:
         139                 :
         140                 :     static NS_METHOD ReadManifest(nsIInputStream *aInputStream,
         141                 :                                   void *aClosure,
         142                 :                                   const char *aFromSegment,
         143                 :                                   PRUint32 aOffset,
         144                 :                                   PRUint32 aCount,
         145                 :                                   PRUint32 *aBytesConsumed);
         146                 :
         147                 :     nsRefPtr<nsOfflineCacheUpdate> mUpdate;
         148                 :     nsCOMPtr<nsIURI> mURI;
         149                 :     nsCOMPtr<nsIURI> mReferrerURI;
         150                 :     nsCOMPtr<nsICryptoHash> mManifestHash;
         151                 :     nsCOMPtr<nsIChannel> mChannel;
         152                 : };
         153                 :
         154                 : //-----------------------------------------------------------------------------
         155                 : // nsManifestCheck::nsISupports
         156                 : //-----------------------------------------------------------------------------
         157             104 : NS_IMPL_ISUPPORTS4(nsManifestCheck,
         158                 :                    nsIRequestObserver,
         159                 :                    nsIStreamListener,
         160                 :                    nsIChannelEventSink,
         161                 :                    nsIInterfaceRequestor)
         162                 :
         163                 : //-----------------------------------------------------------------------------
         164                 : // nsManifestCheck <public>
         165                 : //-----------------------------------------------------------------------------
         166                 :
         167                 : nsresult
         168               8 : nsManifestCheck::Begin()
         169                 : {
         170                 :     nsresult rv;
         171               8 :     mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
         172               8 :     NS_ENSURE_SUCCESS(rv, rv);
         173                 :
         174               8 :     rv = mManifestHash->Init(nsICryptoHash::MD5);
         175               8 :     NS_ENSURE_SUCCESS(rv, rv);
         176                 :
         177               8 :     rv = NS_NewChannel(getter_AddRefs(mChannel),
         178                 :                        mURI,
         179                 :                        nsnull, nsnull, nsnull,
         180               8 :                        nsIRequest::LOAD_BYPASS_CACHE);
         181               8 :     NS_ENSURE_SUCCESS(rv, rv);
         182                 :
         183                 :     // configure HTTP specific stuff
         184                 :     nsCOMPtr<nsIHttpChannel> httpChannel =
         185              16 :         do_QueryInterface(mChannel);
         186               8 :     if (httpChannel) {
         187               8 :         httpChannel->SetReferrer(mReferrerURI);
         188              16 :         httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
         189               8 :                                       NS_LITERAL_CSTRING("offline-resource"),
         190               8 :                                       false);
         191                 :     }
         192                 :
         193               8 :     rv = mChannel->AsyncOpen(this, nsnull);
         194               8 :     NS_ENSURE_SUCCESS(rv, rv);
         195                 :
         196               8 :     return NS_OK;
         197                 : }
         198                 :
         199                 : //-----------------------------------------------------------------------------
         200                 : // nsManifestCheck <public>
         201                 : //-----------------------------------------------------------------------------
         202                 :
         203                 : /* static */
         204                 : NS_METHOD
         205               8 : nsManifestCheck::ReadManifest(nsIInputStream *aInputStream,
         206                 :                               void *aClosure,
         207                 :                               const char *aFromSegment,
         208                 :                               PRUint32 aOffset,
         209                 :                               PRUint32 aCount,
         210                 :                               PRUint32 *aBytesConsumed)
         211                 : {
         212                 :     nsManifestCheck *manifestCheck =
         213               8 :         static_cast<nsManifestCheck*>(aClosure);
         214                 :
         215                 :     nsresult rv;
         216               8 :     *aBytesConsumed = aCount;
         217                 :
         218               8 :     rv = manifestCheck->mManifestHash->Update(
         219               8 :         reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
         220               8 :     NS_ENSURE_SUCCESS(rv, rv);
         221                 :
         222               8 :     return NS_OK;
         223                 : }
         224                 :
         225                 : //-----------------------------------------------------------------------------
         226                 : // nsManifestCheck::nsIStreamListener
         227                 : //-----------------------------------------------------------------------------
         228                 :
         229                 : NS_IMETHODIMP
         230               8 : nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
         231                 :                                 nsISupports *aContext)
         232                 : {
         233               8 :     return NS_OK;
         234                 : }
         235                 :
         236                 : NS_IMETHODIMP
         237               8 : nsManifestCheck::OnDataAvailable(nsIRequest *aRequest,
         238                 :                                  nsISupports *aContext,
         239                 :                                  nsIInputStream *aStream,
         240                 :                                  PRUint32 aOffset,
         241                 :                                  PRUint32 aCount)
         242                 : {
         243                 :     PRUint32 bytesRead;
         244               8 :     aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
         245               8 :     return NS_OK;
         246                 : }
         247                 :
         248                 : NS_IMETHODIMP
         249               8 : nsManifestCheck::OnStopRequest(nsIRequest *aRequest,
         250                 :                                nsISupports *aContext,
         251                 :                                nsresult aStatus)
         252                 : {
         253              16 :     nsCAutoString manifestHash;
         254               8 :     if (NS_SUCCEEDED(aStatus)) {
         255               8 :         mManifestHash->Finish(true, manifestHash);
         256                 :     }
         257                 :
         258               8 :     mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
         259                 :
         260               8 :     return NS_OK;
         261                 : }
         262                 :
         263                 : //-----------------------------------------------------------------------------
         264                 : // nsManifestCheck::nsIInterfaceRequestor
         265                 : //-----------------------------------------------------------------------------
         266                 :
         267                 : NS_IMETHODIMP
         268               0 : nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult)
         269                 : {
         270               0 :     if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
         271               0 :         NS_ADDREF_THIS();
         272               0 :         *aResult = static_cast<nsIChannelEventSink *>(this);
         273               0 :         return NS_OK;
         274                 :     }
         275                 :
         276               0 :     return NS_ERROR_NO_INTERFACE;
         277                 : }
         278                 :
         279                 : //-----------------------------------------------------------------------------
         280                 : // nsManifestCheck::nsIChannelEventSink
         281                 : //-----------------------------------------------------------------------------
         282                 :
         283                 : NS_IMETHODIMP
         284               0 : nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
         285                 :                                         nsIChannel *aNewChannel,
         286                 :                                         PRUint32 aFlags,
         287                 :                                         nsIAsyncVerifyRedirectCallback *callback)
         288                 : {
         289                 :     // Redirects should cause the load (and therefore the update) to fail.
         290               0 :     if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
         291               0 :         callback->OnRedirectVerifyCallback(NS_OK);
         292               0 :         return NS_OK;
         293                 :     }
         294               0 :     aOldChannel->Cancel(NS_ERROR_ABORT);
         295               0 :     return NS_ERROR_ABORT;
         296                 : }
         297                 :
         298                 : //-----------------------------------------------------------------------------
         299                 : // nsOfflineCacheUpdateItem::nsISupports
         300                 : //-----------------------------------------------------------------------------
         301                 :
         302             712 : NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
         303                 :                    nsIDOMLoadStatus,
         304                 :                    nsIRequestObserver,
         305                 :                    nsIStreamListener,
         306                 :                    nsIRunnable,
         307                 :                    nsIInterfaceRequestor,
         308                 :                    nsIChannelEventSink)
         309                 :
         310                 : //-----------------------------------------------------------------------------
         311                 : // nsOfflineCacheUpdateItem <public>
         312                 : //-----------------------------------------------------------------------------
         313                 :
         314              24 : nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
         315                 :                                                    nsIURI *aURI,
         316                 :                                                    nsIURI *aReferrerURI,
         317                 :                                                    nsIApplicationCache *aPreviousApplicationCache,
         318                 :                                                    const nsACString &aClientID,
         319                 :                                                    PRUint32 type)
         320                 :     : mURI(aURI)
         321                 :     , mReferrerURI(aReferrerURI)
         322                 :     , mPreviousApplicationCache(aPreviousApplicationCache)
         323                 :     , mClientID(aClientID)
         324                 :     , mItemType(type)
         325                 :     , mUpdate(aUpdate)
         326                 :     , mChannel(nsnull)
         327                 :     , mState(nsIDOMLoadStatus::UNINITIALIZED)
         328              24 :     , mBytesRead(0)
         329                 : {
         330              24 : }
         331                 :
         332              40 : nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
         333                 : {
         334              80 : }
         335                 :
         336                 : nsresult
         337              24 : nsOfflineCacheUpdateItem::OpenChannel()
         338                 : {
         339                 : #if defined(PR_LOGGING)
         340              24 :     if (LOG_ENABLED()) {
         341               0 :         nsCAutoString spec;
         342               0 :         mURI->GetSpec(spec);
         343               0 :         LOG(("%p: Opening channel for %s", this, spec.get()));
         344                 :     }
         345                 : #endif
         346                 :
         347              24 :     nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
         348              24 :     NS_ENSURE_SUCCESS(rv, rv);
         349                 :
         350              24 :     rv = NS_NewChannel(getter_AddRefs(mChannel),
         351                 :                        mURI,
         352                 :                        nsnull, nsnull, this,
         353                 :                        nsIRequest::LOAD_BACKGROUND |
         354                 :                        nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
         355              24 :                        nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE);
         356              24 :     NS_ENSURE_SUCCESS(rv, rv);
         357                 :
         358                 :     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         359              48 :         do_QueryInterface(mChannel, &rv);
         360                 :
         361                 :     // Support for nsIApplicationCacheChannel is required.
         362              24 :     NS_ENSURE_SUCCESS(rv, rv);
         363                 :
         364                 :     // Use the existing application cache as the cache to check.
         365              24 :     rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
         366              24 :     NS_ENSURE_SUCCESS(rv, rv);
         367                 :
         368                 :     // configure HTTP specific stuff
         369                 :     nsCOMPtr<nsIHttpChannel> httpChannel =
         370              48 :         do_QueryInterface(mChannel);
         371              24 :     if (httpChannel) {
         372              24 :         httpChannel->SetReferrer(mReferrerURI);
         373              48 :         httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
         374              24 :                                       NS_LITERAL_CSTRING("offline-resource"),
         375              24 :                                       false);
         376                 :     }
         377                 :
         378                 :     nsCOMPtr<nsICachingChannel> cachingChannel =
         379              48 :         do_QueryInterface(mChannel);
         380              24 :     if (cachingChannel) {
         381              24 :         rv = cachingChannel->SetCacheForOfflineUse(true);
         382              24 :         NS_ENSURE_SUCCESS(rv, rv);
         383                 :
         384              24 :         if (!mClientID.IsEmpty()) {
         385              24 :             rv = cachingChannel->SetOfflineCacheClientID(mClientID);
         386              24 :             NS_ENSURE_SUCCESS(rv, rv);
         387                 :         }
         388                 :     }
         389                 :
         390              24 :     rv = mChannel->AsyncOpen(this, nsnull);
         391              24 :     NS_ENSURE_SUCCESS(rv, rv);
         392                 :
         393              24 :     mState = nsIDOMLoadStatus::REQUESTED;
         394                 :
         395              24 :     return NS_OK;
         396                 : }
         397                 :
         398                 : nsresult
         399               0 : nsOfflineCacheUpdateItem::Cancel()
         400                 : {
         401               0 :     if (mChannel) {
         402               0 :         mChannel->Cancel(NS_ERROR_ABORT);
         403               0 :         mChannel = nsnull;
         404                 :     }
         405                 :
         406               0 :     mState = nsIDOMLoadStatus::UNINITIALIZED;
         407                 :
         408               0 :     return NS_OK;
         409                 : }
         410                 :
         411                 : //-----------------------------------------------------------------------------
         412                 : // nsOfflineCacheUpdateItem::nsIStreamListener
         413                 : //-----------------------------------------------------------------------------
         414                 :
         415                 : NS_IMETHODIMP
         416              24 : nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
         417                 :                                          nsISupports *aContext)
         418                 : {
         419              24 :     mState = nsIDOMLoadStatus::RECEIVING;
         420                 :
         421              24 :     return NS_OK;
         422                 : }
         423                 :
         424                 : NS_IMETHODIMP
         425              16 : nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
         426                 :                                           nsISupports *aContext,
         427                 :                                           nsIInputStream *aStream,
         428                 :                                           PRUint32 aOffset,
         429                 :                                           PRUint32 aCount)
         430                 : {
         431              16 :     PRUint32 bytesRead = 0;
         432              16 :     aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
         433              16 :     mBytesRead += bytesRead;
         434              16 :     LOG(("loaded %u bytes into offline cache [offset=%u]\n",
         435                 :          bytesRead, aOffset));
         436              16 :     return NS_OK;
         437                 : }
         438                 :
         439                 : NS_IMETHODIMP
         440              24 : nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
         441                 :                                         nsISupports *aContext,
         442                 :                                         nsresult aStatus)
         443                 : {
         444              24 :     LOG(("done fetching offline item [status=%x]\n", aStatus));
         445                 :
         446              24 :     mState = nsIDOMLoadStatus::LOADED;
         447                 :
         448              24 :     if (mBytesRead == 0 && aStatus == NS_OK) {
         449                 :         // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
         450                 :         // specified), but the object should report loadedSize as if it
         451                 :         // did.
         452               0 :         mChannel->GetContentLength(&mBytesRead);
         453                 :     }
         454                 :
         455                 :     // We need to notify the update that the load is complete, but we
         456                 :     // want to give the channel a chance to close the cache entries.
         457              24 :     NS_DispatchToCurrentThread(this);
         458                 :
         459              24 :     return NS_OK;
         460                 : }
         461                 :
         462                 :
         463                 : //-----------------------------------------------------------------------------
         464                 : // nsOfflineCacheUpdateItem::nsIRunnable
         465                 : //-----------------------------------------------------------------------------
         466                 : NS_IMETHODIMP
         467              24 : nsOfflineCacheUpdateItem::Run()
         468                 : {
         469              24 :     mUpdate->LoadCompleted();
         470                 :
         471              24 :     return NS_OK;
         472                 : }
         473                 :
         474                 : //-----------------------------------------------------------------------------
         475                 : // nsOfflineCacheUpdateItem::nsIInterfaceRequestor
         476                 : //-----------------------------------------------------------------------------
         477                 :
         478                 : NS_IMETHODIMP
         479             110 : nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
         480                 : {
         481             110 :     if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
         482               0 :         NS_ADDREF_THIS();
         483               0 :         *aResult = static_cast<nsIChannelEventSink *>(this);
         484               0 :         return NS_OK;
         485                 :     }
         486                 :
         487             110 :     return NS_ERROR_NO_INTERFACE;
         488                 : }
         489                 :
         490                 : //-----------------------------------------------------------------------------
         491                 : // nsOfflineCacheUpdateItem::nsIChannelEventSink
         492                 : //-----------------------------------------------------------------------------
         493                 :
         494                 : NS_IMETHODIMP
         495               0 : nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
         496                 :                                                  nsIChannel *aNewChannel,
         497                 :                                                  PRUint32 aFlags,
         498                 :                                                  nsIAsyncVerifyRedirectCallback *cb)
         499                 : {
         500               0 :     if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
         501                 :         // Don't allow redirect in case of non-internal redirect and cancel
         502                 :         // the channel to clean the cache entry.
         503               0 :         aOldChannel->Cancel(NS_ERROR_ABORT);
         504               0 :         return NS_ERROR_ABORT;
         505                 :     }
         506                 :
         507               0 :     nsCOMPtr<nsIURI> newURI;
         508               0 :     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
         509               0 :     if (NS_FAILED(rv))
         510               0 :         return rv;
         511                 :
         512                 :     nsCOMPtr<nsICachingChannel> newCachingChannel =
         513               0 :         do_QueryInterface(aNewChannel);
         514               0 :     if (newCachingChannel) {
         515               0 :         rv = newCachingChannel->SetCacheForOfflineUse(true);
         516               0 :         NS_ENSURE_SUCCESS(rv, rv);
         517               0 :         if (!mClientID.IsEmpty()) {
         518               0 :             rv = newCachingChannel->SetOfflineCacheClientID(mClientID);
         519               0 :             NS_ENSURE_SUCCESS(rv, rv);
         520                 :         }
         521                 :     }
         522                 :
         523               0 :     nsCAutoString oldScheme;
         524               0 :     mURI->GetScheme(oldScheme);
         525                 :
         526                 :     bool match;
         527               0 :     if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
         528               0 :         LOG(("rejected: redirected to a different scheme\n"));
         529               0 :         return NS_ERROR_ABORT;
         530                 :     }
         531                 :
         532                 :     // HTTP request headers are not automatically forwarded to the new channel.
         533               0 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
         534               0 :     NS_ENSURE_STATE(httpChannel);
         535                 :
         536               0 :     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
         537               0 :                                   NS_LITERAL_CSTRING("offline-resource"),
         538               0 :                                   false);
         539                 :
         540               0 :     mChannel = aNewChannel;
         541                 :
         542               0 :     cb->OnRedirectVerifyCallback(NS_OK);
         543               0 :     return NS_OK;
         544                 : }
         545                 :
         546                 : //-----------------------------------------------------------------------------
         547                 : // nsOfflineCacheUpdateItem::nsIDOMLoadStatus
         548                 : //-----------------------------------------------------------------------------
         549                 :
         550                 : NS_IMETHODIMP
         551               0 : nsOfflineCacheUpdateItem::GetSource(nsIDOMNode **aSource)
         552                 : {
         553               0 :     *aSource = nsnull;
         554               0 :     return NS_OK;
         555                 : }
         556                 :
         557                 : NS_IMETHODIMP
         558               0 : nsOfflineCacheUpdateItem::GetUri(nsAString &aURI)
         559                 : {
         560               0 :     nsCAutoString spec;
         561               0 :     nsresult rv = mURI->GetSpec(spec);
         562               0 :     NS_ENSURE_SUCCESS(rv, rv);
         563                 :
         564               0 :     CopyUTF8toUTF16(spec, aURI);
         565               0 :     return NS_OK;
         566                 : }
         567                 :
         568                 : NS_IMETHODIMP
         569               0 : nsOfflineCacheUpdateItem::GetTotalSize(PRInt32 *aTotalSize)
         570                 : {
         571               0 :     if (mChannel) {
         572               0 :         return mChannel->GetContentLength(aTotalSize);
         573                 :     }
         574                 :
         575               0 :     *aTotalSize = -1;
         576               0 :     return NS_OK;
         577                 : }
         578                 :
         579                 : NS_IMETHODIMP
         580               0 : nsOfflineCacheUpdateItem::GetLoadedSize(PRInt32 *aLoadedSize)
         581                 : {
         582               0 :     *aLoadedSize = mBytesRead;
         583               0 :     return NS_OK;
         584                 : }
         585                 :
         586                 : NS_IMETHODIMP
         587               0 : nsOfflineCacheUpdateItem::GetReadyState(PRUint16 *aReadyState)
         588                 : {
         589               0 :     *aReadyState = mState;
         590               0 :     return NS_OK;
         591                 : }
         592                 :
         593                 : nsresult
         594              24 : nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded)
         595                 : {
         596              24 :     *succeeded = false;
         597                 :
         598              24 :     if (!mChannel)
         599               0 :         return NS_OK;
         600                 :
         601                 :     nsresult rv;
         602              48 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
         603              24 :     NS_ENSURE_SUCCESS(rv, rv);
         604                 :
         605                 :     bool reqSucceeded;
         606              24 :     rv = httpChannel->GetRequestSucceeded(&reqSucceeded);
         607              24 :     if (NS_ERROR_NOT_AVAILABLE == rv)
         608               0 :         return NS_OK;
         609              24 :     NS_ENSURE_SUCCESS(rv, rv);
         610                 :
         611              24 :     if (!reqSucceeded) {
         612               0 :         LOG(("Request failed"));
         613               0 :         return NS_OK;
         614                 :     }
         615                 :
         616                 :     nsresult channelStatus;
         617              24 :     rv = httpChannel->GetStatus(&channelStatus);
         618              24 :     NS_ENSURE_SUCCESS(rv, rv);
         619                 :
         620              24 :     if (NS_FAILED(channelStatus)) {
         621               0 :         LOG(("Channel status=0x%08x", channelStatus));
         622               0 :         return NS_OK;
         623                 :     }
         624                 :
         625              24 :     *succeeded = true;
         626              24 :     return NS_OK;
         627                 : }
         628                 :
         629                 : NS_IMETHODIMP
         630               8 : nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
         631                 : {
         632               8 :     if (!mChannel) {
         633               0 :         *aStatus = 0;
         634               0 :         return NS_OK;
         635                 :     }
         636                 :
         637                 :     nsresult rv;
         638              16 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
         639               8 :     NS_ENSURE_SUCCESS(rv, rv);
         640                 :
         641                 :     PRUint32 httpStatus;
         642               8 :     rv = httpChannel->GetResponseStatus(&httpStatus);
         643               8 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
         644               0 :         *aStatus = 0;
         645               0 :         return NS_OK;
         646                 :     }
         647                 :
         648               8 :     NS_ENSURE_SUCCESS(rv, rv);
         649               8 :     *aStatus = PRUint16(httpStatus);
         650               8 :     return NS_OK;
         651                 : }
         652                 :
         653                 : //-----------------------------------------------------------------------------
         654                 : // nsOfflineManifestItem
         655                 : //-----------------------------------------------------------------------------
         656                 :
         657                 : //-----------------------------------------------------------------------------
         658                 : // nsOfflineManifestItem <public>
         659                 : //-----------------------------------------------------------------------------
         660                 :
         661               8 : nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
         662                 :                                              nsIURI *aURI,
         663                 :                                              nsIURI *aReferrerURI,
         664                 :                                              nsIApplicationCache *aPreviousApplicationCache,
         665                 :                                              const nsACString &aClientID)
         666                 :     : nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI,
         667                 :                                aPreviousApplicationCache, aClientID,
         668                 :                                nsIApplicationCache::ITEM_MANIFEST)
         669                 :     , mParserState(PARSE_INIT)
         670                 :     , mNeedsUpdate(true)
         671               8 :     , mManifestHashInitialized(false)
         672                 : {
         673               8 :     ReadStrictFileOriginPolicyPref();
         674               8 : }
         675                 :
         676              16 : nsOfflineManifestItem::~nsOfflineManifestItem()
         677                 : {
         678              32 : }
         679                 :
         680                 : //-----------------------------------------------------------------------------
         681                 : // nsOfflineManifestItem <private>
         682                 : //-----------------------------------------------------------------------------
         683                 :
         684                 : /* static */
         685                 : NS_METHOD
         686               8 : nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
         687                 :                                     void *aClosure,
         688                 :                                     const char *aFromSegment,
         689                 :                                     PRUint32 aOffset,
         690                 :                                     PRUint32 aCount,
         691                 :                                     PRUint32 *aBytesConsumed)
         692                 : {
         693                 :     nsOfflineManifestItem *manifest =
         694               8 :         static_cast<nsOfflineManifestItem*>(aClosure);
         695                 :
         696                 :     nsresult rv;
         697                 :
         698               8 :     *aBytesConsumed = aCount;
         699                 :
         700               8 :     if (manifest->mParserState == PARSE_ERROR) {
         701                 :         // parse already failed, ignore this
         702               0 :         return NS_OK;
         703                 :     }
         704                 :
         705               8 :     if (!manifest->mManifestHashInitialized) {
         706                 :         // Avoid re-creation of crypto hash when it fails from some reason the first time
         707               8 :         manifest->mManifestHashInitialized = true;
         708                 :
         709               8 :         manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
         710               8 :         if (NS_SUCCEEDED(rv)) {
         711               8 :             rv = manifest->mManifestHash->Init(nsICryptoHash::MD5);
         712               8 :             if (NS_FAILED(rv)) {
         713               0 :                 manifest->mManifestHash = nsnull;
         714               0 :                 LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08x", rv));
         715                 :             }
         716                 :         }
         717                 :     }
         718                 :
         719               8 :     if (manifest->mManifestHash) {
         720               8 :         rv = manifest->mManifestHash->Update(reinterpret_cast<const PRUint8 *>(aFromSegment), aCount);
         721               8 :         if (NS_FAILED(rv)) {
         722               0 :             manifest->mManifestHash = nsnull;
         723               0 :             LOG(("Could not update manifest hash, rv=%08x", rv));
         724                 :         }
         725                 :     }
         726                 :
         727               8 :     manifest->mReadBuf.Append(aFromSegment, aCount);
         728                 :
         729               8 :     nsCString::const_iterator begin, iter, end;
         730               8 :     manifest->mReadBuf.BeginReading(begin);
         731               8 :     manifest->mReadBuf.EndReading(end);
         732                 :
         733             346 :     for (iter = begin; iter != end; iter++) {
         734             338 :         if (*iter == '\r' || *iter == '\n') {
         735              24 :             nsresult rv = manifest->HandleManifestLine(begin, iter);
         736                 :
         737              24 :             if (NS_FAILED(rv)) {
         738               0 :                 LOG(("HandleManifestLine failed with 0x%08x", rv));
         739               0 :                 return NS_ERROR_ABORT;
         740                 :             }
         741                 :
         742              24 :             begin = iter;
         743              24 :             begin++;
         744                 :         }
         745                 :     }
         746                 :
         747                 :     // any leftovers are saved for next time
         748               8 :     manifest->mReadBuf = Substring(begin, end);
         749                 :
         750               8 :     return NS_OK;
         751                 : }
         752                 :
         753                 : nsresult
         754               8 : nsOfflineManifestItem::AddNamespace(PRUint32 namespaceType,
         755                 :                                     const nsCString &namespaceSpec,
         756                 :                                     const nsCString &data)
         757                 :
         758                 : {
         759                 :     nsresult rv;
         760               8 :     if (!mNamespaces) {
         761               8 :         mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
         762               8 :         NS_ENSURE_SUCCESS(rv, rv);
         763                 :     }
         764                 :
         765                 :     nsCOMPtr<nsIApplicationCacheNamespace> ns =
         766              16 :         do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv);
         767               8 :     NS_ENSURE_SUCCESS(rv, rv);
         768                 :
         769               8 :     rv = ns->Init(namespaceType, namespaceSpec, data);
         770               8 :     NS_ENSURE_SUCCESS(rv, rv);
         771                 :
         772               8 :     rv = mNamespaces->AppendElement(ns, false);
         773               8 :     NS_ENSURE_SUCCESS(rv, rv);
         774                 :
         775               8 :     return NS_OK;
         776                 : }
         777                 :
         778                 : nsresult
         779              32 : nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
         780                 :                                           const nsCString::const_iterator &aEnd)
         781                 : {
         782              32 :     nsCString::const_iterator begin = aBegin;
         783              32 :     nsCString::const_iterator end = aEnd;
         784                 :
         785                 :     // all lines ignore trailing spaces and tabs
         786              32 :     nsCString::const_iterator last = end;
         787              32 :     --last;
         788              64 :     while (end != begin && (*last == ' ' || *last == '\t')) {
         789               0 :         --end;
         790               0 :         --last;
         791                 :     }
         792                 :
         793              32 :     if (mParserState == PARSE_INIT) {
         794                 :         // Allow a UTF-8 BOM
         795               8 :         if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
         796               0 :             if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
         797               0 :                 ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
         798               0 :                 mParserState = PARSE_ERROR;
         799               0 :                 return NS_OK;
         800                 :             }
         801               0 :             ++begin;
         802                 :         }
         803                 :
         804              16 :         const nsCSubstring &magic = Substring(begin, end);
         805                 :
         806               8 :         if (!magic.EqualsLiteral("CACHE MANIFEST")) {
         807               0 :             mParserState = PARSE_ERROR;
         808               0 :             return NS_OK;
         809                 :         }
         810                 :
         811               8 :         mParserState = PARSE_CACHE_ENTRIES;
         812               8 :         return NS_OK;
         813                 :     }
         814                 :
         815                 :     // lines other than the first ignore leading spaces and tabs
         816              48 :     while (begin != end && (*begin == ' ' || *begin == '\t'))
         817               0 :         begin++;
         818                 :
         819                 :     // ignore blank lines and comments
         820              24 :     if (begin == end || *begin == '#')
         821               8 :         return NS_OK;
         822                 :
         823              32 :     const nsCSubstring &line = Substring(begin, end);
         824                 :
         825              16 :     if (line.EqualsLiteral("CACHE:")) {
         826               0 :         mParserState = PARSE_CACHE_ENTRIES;
         827               0 :         return NS_OK;
         828                 :     }
         829                 :
         830              16 :     if (line.EqualsLiteral("FALLBACK:")) {
         831               8 :         mParserState = PARSE_FALLBACK_ENTRIES;
         832               8 :         return NS_OK;
         833                 :     }
         834                 :
         835               8 :     if (line.EqualsLiteral("NETWORK:")) {
         836               0 :         mParserState = PARSE_BYPASS_ENTRIES;
         837               0 :         return NS_OK;
         838                 :     }
         839                 :
         840                 :     nsresult rv;
         841                 :
         842               8 :     switch(mParserState) {
         843                 :     case PARSE_INIT:
         844                 :     case PARSE_ERROR: {
         845                 :         // this should have been dealt with earlier
         846               0 :         return NS_ERROR_FAILURE;
         847                 :     }
         848                 :
         849                 :     case PARSE_CACHE_ENTRIES: {
         850               0 :         nsCOMPtr<nsIURI> uri;
         851               0 :         rv = NS_NewURI(getter_AddRefs(uri), line, nsnull, mURI);
         852               0 :         if (NS_FAILED(rv))
         853                 :             break;
         854               0 :         if (NS_FAILED(DropReferenceFromURL(uri)))
         855                 :             break;
         856                 :
         857               0 :         nsCAutoString scheme;
         858               0 :         uri->GetScheme(scheme);
         859                 :
         860                 :         // Manifest URIs must have the same scheme as the manifest.
         861                 :         bool match;
         862               0 :         if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
         863                 :             break;
         864                 :
         865               0 :         mExplicitURIs.AppendObject(uri);
         866                 :         break;
         867                 :     }
         868                 :
         869                 :     case PARSE_FALLBACK_ENTRIES: {
         870               8 :         PRInt32 separator = line.FindChar(' ');
         871               8 :         if (separator == kNotFound) {
         872               0 :             separator = line.FindChar('\t');
         873               0 :             if (separator == kNotFound)
         874               0 :                 break;
         875                 :         }
         876                 :
         877              16 :         nsCString namespaceSpec(Substring(line, 0, separator));
         878              16 :         nsCString fallbackSpec(Substring(line, separator + 1));
         879               8 :         namespaceSpec.CompressWhitespace();
         880               8 :         fallbackSpec.CompressWhitespace();
         881                 :
         882              16 :         nsCOMPtr<nsIURI> namespaceURI;
         883               8 :         rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nsnull, mURI);
         884               8 :         if (NS_FAILED(rv))
         885                 :             break;
         886               8 :         if (NS_FAILED(DropReferenceFromURL(namespaceURI)))
         887                 :             break;
         888               8 :         rv = namespaceURI->GetAsciiSpec(namespaceSpec);
         889               8 :         if (NS_FAILED(rv))
         890                 :             break;
         891                 :
         892                 :
         893              16 :         nsCOMPtr<nsIURI> fallbackURI;
         894               8 :         rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nsnull, mURI);
         895               8 :         if (NS_FAILED(rv))
         896                 :             break;
         897               8 :         if (NS_FAILED(DropReferenceFromURL(fallbackURI)))
         898                 :             break;
         899               8 :         rv = fallbackURI->GetAsciiSpec(fallbackSpec);
         900               8 :         if (NS_FAILED(rv))
         901                 :             break;
         902                 :
         903                 :         // Manifest and namespace must be same origin
         904               8 :         if (!NS_SecurityCompareURIs(mURI, namespaceURI,
         905               8 :                                     mStrictFileOriginPolicy))
         906                 :             break;
         907                 :
         908                 :         // Fallback and namespace must be same origin
         909               8 :         if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
         910               8 :                                     mStrictFileOriginPolicy))
         911                 :             break;
         912                 :
         913               8 :         mFallbackURIs.AppendObject(fallbackURI);
         914                 :
         915                 :         AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
         916               8 :                      namespaceSpec, fallbackSpec);
         917                 :         break;
         918                 :     }
         919                 :
         920                 :     case PARSE_BYPASS_ENTRIES: {
         921               0 :         if (line[0] == '*' && (line.Length() == 1 || line[1] == ' ' || line[1] == '\t'))
         922                 :         {
         923                 :           // '*' indicates to make the online whitelist wildcard flag open,
         924                 :           // i.e. do allow load of resources not present in the offline cache
         925                 :           // or not conforming any namespace.
         926                 :           // We achive that simply by adding an 'empty' - i.e. universal
         927                 :           // namespace of BYPASS type into the cache.
         928                 :           AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
         929               0 :                        EmptyCString(), EmptyCString());
         930               0 :           break;
         931                 :         }
         932                 :
         933               0 :         nsCOMPtr<nsIURI> bypassURI;
         934               0 :         rv = NS_NewURI(getter_AddRefs(bypassURI), line, nsnull, mURI);
         935               0 :         if (NS_FAILED(rv))
         936                 :             break;
         937                 :
         938               0 :         nsCAutoString scheme;
         939               0 :         bypassURI->GetScheme(scheme);
         940                 :         bool equals;
         941               0 :         if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals)
         942                 :             break;
         943               0 :         if (NS_FAILED(DropReferenceFromURL(bypassURI)))
         944                 :             break;
         945               0 :         nsCString spec;
         946               0 :         if (NS_FAILED(bypassURI->GetAsciiSpec(spec)))
         947                 :             break;
         948                 :
         949                 :         AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
         950               0 :                      spec, EmptyCString());
         951                 :         break;
         952                 :     }
         953                 :     }
         954                 :
         955               8 :     return NS_OK;
         956                 : }
         957                 :
         958                 : nsresult
         959               8 : nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
         960                 : {
         961                 :     nsresult rv;
         962                 :
         963              16 :     nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
         964               8 :     NS_ENSURE_SUCCESS(rv, rv);
         965                 :
         966                 :     // load the main cache token that is actually the old offline cache token and
         967                 :     // read previous manifest content hash value
         968              16 :     nsCOMPtr<nsISupports> cacheToken;
         969               8 :     cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
         970               8 :     if (cacheToken) {
         971              16 :         nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
         972               8 :         NS_ENSURE_SUCCESS(rv, rv);
         973                 :
         974               8 :         rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
         975               8 :         if (NS_FAILED(rv))
         976               8 :             mOldManifestHashValue.Truncate();
         977                 :     }
         978                 :
         979               8 :     return NS_OK;
         980                 : }
         981                 :
         982                 : nsresult
         983               8 : nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
         984                 : {
         985                 :     nsresult rv;
         986                 :
         987               8 :     if (!mManifestHash) {
         988                 :         // Nothing to compare against...
         989               0 :         return NS_OK;
         990                 :     }
         991                 :
         992              16 :     nsCString newManifestHashValue;
         993               8 :     rv = mManifestHash->Finish(true, mManifestHashValue);
         994               8 :     mManifestHash = nsnull;
         995                 :
         996               8 :     if (NS_FAILED(rv)) {
         997               0 :         LOG(("Could not finish manifest hash, rv=%08x", rv));
         998                 :         // This is not critical error
         999               0 :         return NS_OK;
        1000                 :     }
        1001                 :
        1002               8 :     if (!ParseSucceeded()) {
        1003                 :         // Parsing failed, the hash is not valid
        1004               0 :         return NS_OK;
        1005                 :     }
        1006                 :
        1007               8 :     if (mOldManifestHashValue == mManifestHashValue) {
        1008               0 :         LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
        1009               0 :         mNeedsUpdate = false;
        1010                 :     }
        1011                 :
        1012                 :     // Store the manifest content hash value to the new
        1013                 :     // offline cache token
        1014              16 :     nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
        1015               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1016                 :
        1017              16 :     nsCOMPtr<nsISupports> cacheToken;
        1018               8 :     cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken));
        1019               8 :     if (cacheToken) {
        1020              16 :         nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
        1021               8 :         NS_ENSURE_SUCCESS(rv, rv);
        1022                 :
        1023               8 :         rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
        1024               8 :         NS_ENSURE_SUCCESS(rv, rv);
        1025                 :     }
        1026                 :
        1027               8 :     return NS_OK;
        1028                 : }
        1029                 :
        1030                 : void
        1031               8 : nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
        1032                 : {
        1033                 :     mStrictFileOriginPolicy =
        1034               8 :         Preferences::GetBool("security.fileuri.strict_origin_policy", true);
        1035               8 : }
        1036                 :
        1037                 : NS_IMETHODIMP
        1038               8 : nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
        1039                 :                                       nsISupports *aContext)
        1040                 : {
        1041                 :     nsresult rv;
        1042                 :
        1043              16 :     nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
        1044               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1045                 :
        1046                 :     bool succeeded;
        1047               8 :     rv = channel->GetRequestSucceeded(&succeeded);
        1048               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1049                 :
        1050               8 :     if (!succeeded) {
        1051               0 :         LOG(("HTTP request failed"));
        1052               0 :         mParserState = PARSE_ERROR;
        1053               0 :         return NS_ERROR_ABORT;
        1054                 :     }
        1055                 :
        1056              16 :     nsCAutoString contentType;
        1057               8 :     rv = channel->GetContentType(contentType);
        1058               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1059                 :
        1060               8 :     if (!contentType.EqualsLiteral("text/cache-manifest")) {
        1061               0 :         LOG(("Rejected cache manifest with Content-Type %s (expecting text/cache-manifest)",
        1062                 :              contentType.get()));
        1063               0 :         mParserState = PARSE_ERROR;
        1064               0 :         return NS_ERROR_ABORT;
        1065                 :     }
        1066                 :
        1067               8 :     rv = GetOldManifestContentHash(aRequest);
        1068               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1069                 :
        1070               8 :     return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
        1071                 : }
        1072                 :
        1073                 : NS_IMETHODIMP
        1074               8 : nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
        1075                 :                                        nsISupports *aContext,
        1076                 :                                        nsIInputStream *aStream,
        1077                 :                                        PRUint32 aOffset,
        1078                 :                                        PRUint32 aCount)
        1079                 : {
        1080               8 :     PRUint32 bytesRead = 0;
        1081               8 :     aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
        1082               8 :     mBytesRead += bytesRead;
        1083                 :
        1084               8 :     if (mParserState == PARSE_ERROR) {
        1085               0 :         LOG(("OnDataAvailable is canceling the request due a parse error\n"));
        1086               0 :         return NS_ERROR_ABORT;
        1087                 :     }
        1088                 :
        1089               8 :     LOG(("loaded %u bytes into offline cache [offset=%u]\n",
        1090                 :          bytesRead, aOffset));
        1091                 :
        1092                 :     // All the parent method does is read and discard, don't bother
        1093                 :     // chaining up.
        1094                 :
        1095               8 :     return NS_OK;
        1096                 : }
        1097                 :
        1098                 : NS_IMETHODIMP
        1099               8 : nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
        1100                 :                                      nsISupports *aContext,
        1101                 :                                      nsresult aStatus)
        1102                 : {
        1103                 :     // handle any leftover manifest data
        1104               8 :     nsCString::const_iterator begin, end;
        1105               8 :     mReadBuf.BeginReading(begin);
        1106               8 :     mReadBuf.EndReading(end);
        1107               8 :     nsresult rv = HandleManifestLine(begin, end);
        1108               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1109                 :
        1110               8 :     if (mBytesRead == 0) {
        1111                 :         // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
        1112                 :         // specified.)
        1113               0 :         mNeedsUpdate = false;
        1114                 :     } else {
        1115               8 :         rv = CheckNewManifestContentHash(aRequest);
        1116               8 :         NS_ENSURE_SUCCESS(rv, rv);
        1117                 :     }
        1118                 :
        1119               8 :     return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
        1120                 : }
        1121                 :
        1122                 : //-----------------------------------------------------------------------------
        1123                 : // nsOfflineCacheUpdate::nsISupports
        1124                 : //-----------------------------------------------------------------------------
        1125                 :
        1126             392 : NS_IMPL_ISUPPORTS2(nsOfflineCacheUpdate,
        1127                 :                    nsIOfflineCacheUpdateObserver,
        1128                 :                    nsIOfflineCacheUpdate)
        1129                 :
        1130                 : //-----------------------------------------------------------------------------
        1131                 : // nsOfflineCacheUpdate <public>
        1132                 : //-----------------------------------------------------------------------------
        1133                 :
        1134               8 : nsOfflineCacheUpdate::nsOfflineCacheUpdate()
        1135                 :     : mState(STATE_UNINITIALIZED)
        1136                 :     , mOwner(nsnull)
        1137                 :     , mAddedItems(false)
        1138                 :     , mPartialUpdate(false)
        1139                 :     , mSucceeded(true)
        1140                 :     , mObsolete(false)
        1141                 :     , mCurrentItem(-1)
        1142               8 :     , mRescheduleCount(0)
        1143                 : {
        1144               8 : }
        1145                 :
        1146              16 : nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
        1147                 : {
        1148               8 :     LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
        1149               8 : }
        1150                 :
        1151                 : /* static */
        1152                 : nsresult
        1153              32 : nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
        1154                 : {
        1155              32 :     aKey.Truncate();
        1156                 :
        1157              64 :     nsCOMPtr<nsIURI> newURI;
        1158              32 :     nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(newURI));
        1159              32 :     NS_ENSURE_SUCCESS(rv, rv);
        1160                 :
        1161              32 :     rv = newURI->GetAsciiSpec(aKey);
        1162              32 :     NS_ENSURE_SUCCESS(rv, rv);
        1163                 :
        1164              32 :     return NS_OK;
        1165                 : }
        1166                 :
        1167                 : nsresult
        1168               8 : nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
        1169                 :                            nsIURI *aDocumentURI,
        1170                 :                            nsIDOMDocument *aDocument)
        1171                 : {
        1172                 :     nsresult rv;
        1173                 :
        1174                 :     // Make sure the service has been initialized
        1175                 :     nsOfflineCacheUpdateService* service =
        1176               8 :         nsOfflineCacheUpdateService::EnsureService();
        1177               8 :     if (!service)
        1178               0 :         return NS_ERROR_FAILURE;
        1179                 :
        1180               8 :     LOG(("nsOfflineCacheUpdate::Init [%p]", this));
        1181                 :
        1182               8 :     mPartialUpdate = false;
        1183                 :
        1184                 :     // Only http and https applications are supported.
        1185                 :     bool match;
        1186               8 :     rv = aManifestURI->SchemeIs("http", &match);
        1187               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1188                 :
        1189               8 :     if (!match) {
        1190               0 :         rv = aManifestURI->SchemeIs("https", &match);
        1191               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1192               0 :         if (!match)
        1193               0 :             return NS_ERROR_ABORT;
        1194                 :     }
        1195                 :
        1196               8 :     mManifestURI = aManifestURI;
        1197                 :
        1198               8 :     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
        1199               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1200                 :
        1201              16 :     nsCAutoString manifestSpec;
        1202                 :
        1203               8 :     rv = GetCacheKey(mManifestURI, manifestSpec);
        1204               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1205                 :
        1206               8 :     mDocumentURI = aDocumentURI;
        1207                 :
        1208                 :     nsCOMPtr<nsIApplicationCacheService> cacheService =
        1209              16 :         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
        1210               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1211                 :
        1212               8 :     rv = cacheService->GetActiveCache(manifestSpec,
        1213               8 :                                       getter_AddRefs(mPreviousApplicationCache));
        1214               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1215                 :
        1216               8 :     rv = cacheService->CreateApplicationCache(manifestSpec,
        1217               8 :                                               getter_AddRefs(mApplicationCache));
        1218               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1219                 :
        1220               8 :     rv = mApplicationCache->GetClientID(mClientID);
        1221               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1222                 :
        1223               8 :     mState = STATE_INITIALIZED;
        1224               8 :     return NS_OK;
        1225                 : }
        1226                 :
        1227                 : nsresult
        1228               0 : nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
        1229                 :                                   const nsACString& clientID,
        1230                 :                                   nsIURI *aDocumentURI)
        1231                 : {
        1232                 :     nsresult rv;
        1233                 :
        1234                 :     // Make sure the service has been initialized
        1235                 :     nsOfflineCacheUpdateService* service =
        1236               0 :         nsOfflineCacheUpdateService::EnsureService();
        1237               0 :     if (!service)
        1238               0 :         return NS_ERROR_FAILURE;
        1239                 :
        1240               0 :     LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
        1241                 :
        1242               0 :     mPartialUpdate = true;
        1243               0 :     mClientID = clientID;
        1244               0 :     mDocumentURI = aDocumentURI;
        1245                 :
        1246               0 :     mManifestURI = aManifestURI;
        1247               0 :     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
        1248               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1249                 :
        1250                 :     nsCOMPtr<nsIApplicationCacheService> cacheService =
        1251               0 :         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
        1252               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1253                 :
        1254               0 :     rv = cacheService->GetApplicationCache(mClientID,
        1255               0 :                                            getter_AddRefs(mApplicationCache));
        1256               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1257                 :
        1258               0 :     if (!mApplicationCache) {
        1259               0 :         nsCAutoString manifestSpec;
        1260               0 :         rv = GetCacheKey(mManifestURI, manifestSpec);
        1261               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1262                 :
        1263               0 :         rv = cacheService->CreateApplicationCache
        1264               0 :             (manifestSpec, getter_AddRefs(mApplicationCache));
        1265               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1266                 :     }
        1267                 :
        1268               0 :     nsCAutoString groupID;
        1269               0 :     rv = mApplicationCache->GetGroupID(groupID);
        1270               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1271                 :
        1272               0 :     rv = NS_NewURI(getter_AddRefs(mManifestURI), groupID);
        1273               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1274                 :
        1275               0 :     mState = STATE_INITIALIZED;
        1276               0 :     return NS_OK;
        1277                 : }
        1278                 :
        1279                 : nsresult
        1280               8 : nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate)
        1281                 : {
        1282                 :     // Be pessimistic
        1283               8 :     *aDoUpdate = false;
        1284                 :
        1285                 :     bool succeeded;
        1286               8 :     nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
        1287               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1288                 :
        1289               8 :     if (!succeeded || !mManifestItem->ParseSucceeded()) {
        1290               0 :         return NS_ERROR_FAILURE;
        1291                 :     }
        1292                 :
        1293               8 :     if (!mManifestItem->NeedsUpdate()) {
        1294               0 :         return NS_OK;
        1295                 :     }
        1296                 :
        1297                 :     // Add items requested by the manifest.
        1298               8 :     const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
        1299               8 :     for (PRInt32 i = 0; i < manifestURIs.Count(); i++) {
        1300               0 :         rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
        1301               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1302                 :     }
        1303                 :
        1304               8 :     const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs();
        1305              16 :     for (PRInt32 i = 0; i < fallbackURIs.Count(); i++) {
        1306               8 :         rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK);
        1307               8 :         NS_ENSURE_SUCCESS(rv, rv);
        1308                 :     }
        1309                 :
        1310                 :     // The document that requested the manifest is implicitly included
        1311                 :     // as part of that manifest update.
        1312               8 :     rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT);
        1313               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1314                 :
        1315                 :     // Add items previously cached implicitly
        1316               8 :     rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT);
        1317               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1318                 :
        1319                 :     // Add items requested by the script API
        1320               8 :     rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
        1321               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1322                 :
        1323                 :     // Add opportunistically cached items conforming current opportunistic
        1324                 :     // namespace list
        1325                 :     rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC,
        1326               8 :                           &mManifestItem->GetOpportunisticNamespaces());
        1327               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1328                 :
        1329               8 :     *aDoUpdate = true;
        1330                 :
        1331               8 :     return NS_OK;
        1332                 : }
        1333                 :
        1334                 : void
        1335              24 : nsOfflineCacheUpdate::LoadCompleted()
        1336                 : {
        1337                 :     nsresult rv;
        1338                 :
        1339                 :     // Keep the object alive through a Finish() call.
        1340              48 :     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
        1341                 :
        1342              24 :     LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
        1343                 :
        1344              24 :     if (mState == STATE_CANCELLED) {
        1345               0 :         Finish();
        1346                 :         return;
        1347                 :     }
        1348                 :
        1349              24 :     if (mState == STATE_CHECKING) {
        1350                 :         // Manifest load finished.
        1351                 :
        1352               8 :         NS_ASSERTION(mManifestItem,
        1353                 :                      "Must have a manifest item in STATE_CHECKING.");
        1354                 :
        1355                 :         // A 404 or 410 is interpreted as an intentional removal of
        1356                 :         // the manifest file, rather than a transient server error.
        1357                 :         // Obsolete this cache group if one of these is returned.
        1358                 :         PRUint16 status;
        1359               8 :         rv = mManifestItem->GetStatus(&status);
        1360               8 :         if (status == 404 || status == 410) {
        1361               0 :             mSucceeded = false;
        1362               0 :             mObsolete = true;
        1363               0 :             if (mPreviousApplicationCache) {
        1364               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE);
        1365                 :             } else {
        1366               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1367                 :             }
        1368               0 :             Finish();
        1369                 :             return;
        1370                 :         }
        1371                 :
        1372                 :         bool doUpdate;
        1373               8 :         if (NS_FAILED(HandleManifest(&doUpdate))) {
        1374               0 :             mSucceeded = false;
        1375               0 :             NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1376               0 :             Finish();
        1377                 :             return;
        1378                 :         }
        1379                 :
        1380               8 :         if (!doUpdate) {
        1381               0 :             mSucceeded = false;
        1382                 :
        1383               0 :             AssociateDocuments(mPreviousApplicationCache);
        1384                 :
        1385               0 :             ScheduleImplicit();
        1386                 :
        1387                 :             // If we didn't need an implicit update, we can
        1388                 :             // send noupdate and end the update now.
        1389               0 :             if (!mImplicitUpdate) {
        1390               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
        1391               0 :                 Finish();
        1392                 :             }
        1393                 :             return;
        1394                 :         }
        1395                 :
        1396              16 :         rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
        1397              16 :                                           mManifestItem->mItemType);
        1398               8 :         if (NS_FAILED(rv)) {
        1399               0 :             mSucceeded = false;
        1400               0 :             NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1401               0 :             Finish();
        1402                 :             return;
        1403                 :         }
        1404                 :
        1405               8 :         mState = STATE_DOWNLOADING;
        1406               8 :         NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
        1407                 :
        1408                 :         // Start fetching resources.
        1409               8 :         ProcessNextURI();
        1410                 :
        1411                 :         return;
        1412                 :     }
        1413                 :
        1414                 :     // Normal load finished.
        1415                 :
        1416              32 :     nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[mCurrentItem];
        1417              16 :     mCurrentItem++;
        1418                 :
        1419                 :     bool succeeded;
        1420              16 :     rv = item->GetRequestSucceeded(&succeeded);
        1421                 :
        1422                 :     // Check for failures.  3XX, 4XX and 5XX errors on items explicitly
        1423                 :     // listed in the manifest will cause the update to fail.
        1424              16 :     if (NS_FAILED(rv) || !succeeded) {
        1425               0 :         if (item->mItemType &
        1426                 :             (nsIApplicationCache::ITEM_EXPLICIT |
        1427                 :              nsIApplicationCache::ITEM_FALLBACK)) {
        1428               0 :             mSucceeded = false;
        1429                 :         }
        1430                 :     } else {
        1431              16 :         rv = mApplicationCache->MarkEntry(item->mCacheKey, item->mItemType);
        1432              16 :         if (NS_FAILED(rv)) {
        1433               0 :             mSucceeded = false;
        1434                 :         }
        1435                 :     }
        1436                 :
        1437              16 :     if (!mSucceeded) {
        1438               0 :         NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1439               0 :         Finish();
        1440                 :         return;
        1441                 :     }
        1442                 :
        1443              16 :     rv = NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED);
        1444              16 :     if (NS_FAILED(rv)) return;
        1445                 :
        1446              32 :     ProcessNextURI();
        1447                 : }
        1448                 :
        1449                 : void
        1450               8 : nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
        1451                 :                                              const nsCString &aManifestHash)
        1452                 : {
        1453                 :     // Keep the object alive through a Finish() call.
        1454              16 :     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
        1455                 :
        1456               8 :     if (NS_SUCCEEDED(aStatus)) {
        1457              16 :         nsCAutoString firstManifestHash;
        1458               8 :         mManifestItem->GetManifestHash(firstManifestHash);
        1459               8 :         if (aManifestHash != firstManifestHash) {
        1460               0 :             LOG(("Manifest has changed during cache items download [%p]", this));
        1461               0 :             aStatus = NS_ERROR_FAILURE;
        1462                 :         }
        1463                 :     }
        1464                 :
        1465               8 :     if (NS_FAILED(aStatus)) {
        1466               0 :         mSucceeded = false;
        1467               0 :         NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1468                 :     }
        1469                 :
        1470               8 :     if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
        1471                 :         // Do the final stuff but prevent notification of STATE_FINISHED.
        1472                 :         // That would disconnect listeners that are responsible for document
        1473                 :         // association after a successful update. Forwarding notifications
        1474                 :         // from a new update through this dead update to them is absolutely
        1475                 :         // correct.
        1476               0 :         FinishNoNotify();
        1477                 :
        1478                 :         nsRefPtr<nsOfflineCacheUpdate> newUpdate =
        1479               0 :             new nsOfflineCacheUpdate();
        1480                 :         // Leave aDocument argument null. Only glues and children keep
        1481                 :         // document instances.
        1482               0 :         newUpdate->Init(mManifestURI, mDocumentURI, nsnull);
        1483                 :
        1484                 :         // In a rare case the manifest will not be modified on the next refetch
        1485                 :         // transfer all master document URIs to the new update to ensure that
        1486                 :         // all documents refering it will be properly cached.
        1487               0 :         for (PRInt32 i = 0; i < mDocumentURIs.Count(); i++) {
        1488               0 :             newUpdate->StickDocument(mDocumentURIs[i]);
        1489                 :         }
        1490                 :
        1491               0 :         newUpdate->mRescheduleCount = mRescheduleCount + 1;
        1492               0 :         newUpdate->AddObserver(this, false);
        1493               0 :         newUpdate->Schedule();
        1494                 :     }
        1495                 :     else {
        1496               8 :         Finish();
        1497                 :     }
        1498               8 : }
        1499                 :
        1500                 : nsresult
        1501               8 : nsOfflineCacheUpdate::Begin()
        1502                 : {
        1503               8 :     LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
        1504                 :
        1505                 :     // Keep the object alive through a ProcessNextURI()/Finish() call.
        1506              16 :     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
        1507                 :
        1508               8 :     mCurrentItem = 0;
        1509                 :
        1510               8 :     if (mPartialUpdate) {
        1511               0 :         mState = STATE_DOWNLOADING;
        1512               0 :         NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
        1513               0 :         ProcessNextURI();
        1514               0 :         return NS_OK;
        1515                 :     }
        1516                 :
        1517                 :     // Start checking the manifest.
        1518              16 :     nsCOMPtr<nsIURI> uri;
        1519                 :
        1520                 :     mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
        1521                 :                                               mDocumentURI,
        1522                 :                                               mPreviousApplicationCache,
        1523              16 :                                               mClientID);
        1524               8 :     if (!mManifestItem) {
        1525               0 :         return NS_ERROR_OUT_OF_MEMORY;
        1526                 :     }
        1527                 :
        1528               8 :     mState = STATE_CHECKING;
        1529               8 :     NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING);
        1530                 :
        1531               8 :     nsresult rv = mManifestItem->OpenChannel();
        1532               8 :     if (NS_FAILED(rv)) {
        1533               0 :         LoadCompleted();
        1534                 :     }
        1535                 :
        1536               8 :     return NS_OK;
        1537                 : }
        1538                 :
        1539                 : nsresult
        1540               0 : nsOfflineCacheUpdate::Cancel()
        1541                 : {
        1542               0 :     LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
        1543                 :
        1544               0 :     mState = STATE_CANCELLED;
        1545               0 :     mSucceeded = false;
        1546                 :
        1547               0 :     if (mCurrentItem >= 0 &&
        1548               0 :         mCurrentItem < static_cast<PRInt32>(mItems.Length())) {
        1549                 :         // Load might be running
        1550               0 :         mItems[mCurrentItem]->Cancel();
        1551                 :     }
        1552                 :
        1553               0 :     return NS_OK;
        1554                 : }
        1555                 :
        1556                 : //-----------------------------------------------------------------------------
        1557                 : // nsOfflineCacheUpdate <private>
        1558                 : //-----------------------------------------------------------------------------
        1559                 :
        1560                 : nsresult
        1561              24 : nsOfflineCacheUpdate::AddExistingItems(PRUint32 aType,
        1562                 :                                        nsTArray<nsCString>* namespaceFilter)
        1563                 : {
        1564              24 :     if (!mPreviousApplicationCache) {
        1565              24 :         return NS_OK;
        1566                 :     }
        1567                 :
        1568               0 :     if (namespaceFilter && namespaceFilter->Length() == 0) {
        1569                 :         // Don't bother to walk entries when there are no namespaces
        1570                 :         // defined.
        1571               0 :         return NS_OK;
        1572                 :     }
        1573                 :
        1574               0 :     PRUint32 count = 0;
        1575               0 :     char **keys = nsnull;
        1576               0 :     nsresult rv = mPreviousApplicationCache->GatherEntries(aType,
        1577               0 :                                                            &count, &keys);
        1578               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1579                 :
        1580               0 :     AutoFreeArray autoFree(count, keys);
        1581                 :
        1582               0 :     for (PRUint32 i = 0; i < count; i++) {
        1583               0 :         if (namespaceFilter) {
        1584               0 :             bool found = false;
        1585               0 :             for (PRUint32 j = 0; j < namespaceFilter->Length() && !found; j++) {
        1586               0 :                 found = StringBeginsWith(nsDependentCString(keys[i]),
        1587               0 :                                          namespaceFilter->ElementAt(j));
        1588                 :             }
        1589                 :
        1590               0 :             if (!found)
        1591               0 :                 continue;
        1592                 :         }
        1593                 :
        1594               0 :         nsCOMPtr<nsIURI> uri;
        1595               0 :         if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
        1596               0 :             rv = AddURI(uri, aType);
        1597               0 :             NS_ENSURE_SUCCESS(rv, rv);
        1598                 :         }
        1599                 :     }
        1600                 :
        1601               0 :     return NS_OK;
        1602                 : }
        1603                 :
        1604                 : nsresult
        1605              24 : nsOfflineCacheUpdate::ProcessNextURI()
        1606                 : {
        1607                 :     // Keep the object alive through a Finish() call.
        1608              48 :     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
        1609                 :
        1610              24 :     LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, current=%d, numItems=%d]",
        1611                 :          this, mCurrentItem, mItems.Length()));
        1612                 :
        1613              24 :     NS_ASSERTION(mState == STATE_DOWNLOADING,
        1614                 :                  "ProcessNextURI should only be called from the DOWNLOADING state");
        1615                 :
        1616              24 :     if (mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
        1617               8 :         if (mPartialUpdate) {
        1618               0 :             return Finish();
        1619                 :         } else {
        1620                 :             // Verify that the manifest wasn't changed during the
        1621                 :             // update, to prevent capturing a cache while the server
        1622                 :             // is being updated.  The check will call
        1623                 :             // ManifestCheckCompleted() when it's done.
        1624                 :             nsRefPtr<nsManifestCheck> manifestCheck =
        1625              24 :                 new nsManifestCheck(this, mManifestURI, mDocumentURI);
        1626               8 :             if (NS_FAILED(manifestCheck->Begin())) {
        1627               0 :                 mSucceeded = false;
        1628               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1629               0 :                 return Finish();
        1630                 :             }
        1631                 :
        1632               8 :             return NS_OK;
        1633                 :         }
        1634                 :     }
        1635                 :
        1636                 : #if defined(PR_LOGGING)
        1637              16 :     if (LOG_ENABLED()) {
        1638               0 :         nsCAutoString spec;
        1639               0 :         mItems[mCurrentItem]->mURI->GetSpec(spec);
        1640               0 :         LOG(("%p: Opening channel for %s", this, spec.get()));
        1641                 :     }
        1642                 : #endif
        1643                 :
        1644              16 :     NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED);
        1645                 :
        1646              16 :     nsresult rv = mItems[mCurrentItem]->OpenChannel();
        1647              16 :     if (NS_FAILED(rv)) {
        1648               0 :         LoadCompleted();
        1649               0 :         return rv;
        1650                 :     }
        1651                 :
        1652              16 :     return NS_OK;
        1653                 : }
        1654                 :
        1655                 : nsresult
        1656              64 : nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
        1657                 : {
        1658              64 :     for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
        1659                 :         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
        1660               0 :             do_QueryReferent(mWeakObservers[i]);
        1661               0 :         if (observer)
        1662               0 :             aObservers.AppendObject(observer);
        1663                 :         else
        1664               0 :             mWeakObservers.RemoveObjectAt(i--);
        1665                 :     }
        1666                 :
        1667             128 :     for (PRInt32 i = 0; i < mObservers.Count(); i++) {
        1668              64 :         aObservers.AppendObject(mObservers[i]);
        1669                 :     }
        1670                 :
        1671              64 :     return NS_OK;
        1672                 : }
        1673                 :
        1674                 : nsresult
        1675              56 : nsOfflineCacheUpdate::NotifyState(PRUint32 state)
        1676                 : {
        1677              56 :     LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state));
        1678                 :
        1679             112 :     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
        1680              56 :     nsresult rv = GatherObservers(observers);
        1681              56 :     NS_ENSURE_SUCCESS(rv, rv);
        1682                 :
        1683             112 :     for (PRInt32 i = 0; i < observers.Count(); i++) {
        1684              56 :         observers[i]->UpdateStateChanged(this, state);
        1685                 :     }
        1686                 :
        1687              56 :     return NS_OK;
        1688                 : }
        1689                 :
        1690                 : nsresult
        1691               8 : nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
        1692                 : {
        1693              16 :     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
        1694               8 :     nsresult rv = GatherObservers(observers);
        1695               8 :     NS_ENSURE_SUCCESS(rv, rv);
        1696                 :
        1697              16 :     for (PRInt32 i = 0; i < observers.Count(); i++) {
        1698               8 :         observers[i]->ApplicationCacheAvailable(cache);
        1699                 :     }
        1700                 :
        1701               8 :     return NS_OK;
        1702                 : }
        1703                 :
        1704                 : void
        1705               0 : nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI)
        1706                 : {
        1707               0 :     if (!aDocumentURI)
        1708               0 :       return;
        1709                 :
        1710               0 :     mDocumentURIs.AppendObject(aDocumentURI);
        1711                 : }
        1712                 :
        1713                 : void
        1714               8 : nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
        1715                 : {
        1716               8 :     NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
        1717               8 :     mOwner = aOwner;
        1718               8 : }
        1719                 :
        1720                 : nsresult
        1721               0 : nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
        1722                 : {
        1723                 :     // Keep the object alive through a Finish() call.
        1724               0 :     nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
        1725                 :
        1726               0 :     mImplicitUpdate = nsnull;
        1727                 :
        1728               0 :     NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
        1729               0 :     Finish();
        1730                 :
        1731               0 :     return NS_OK;
        1732                 : }
        1733                 :
        1734                 : nsresult
        1735               0 : nsOfflineCacheUpdate::ScheduleImplicit()
        1736                 : {
        1737               0 :     if (mDocumentURIs.Count() == 0)
        1738               0 :         return NS_OK;
        1739                 :
        1740                 :     nsresult rv;
        1741                 :
        1742               0 :     nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
        1743               0 :     NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
        1744                 :
        1745               0 :     nsCAutoString clientID;
        1746               0 :     if (mPreviousApplicationCache) {
        1747               0 :         rv = mPreviousApplicationCache->GetClientID(clientID);
        1748               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1749                 :     }
        1750                 :     else {
        1751               0 :         clientID = mClientID;
        1752                 :     }
        1753                 :
        1754               0 :     rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
        1755               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1756                 :
        1757               0 :     for (PRInt32 i = 0; i < mDocumentURIs.Count(); i++) {
        1758                 :         rv = update->AddURI(mDocumentURIs[i],
        1759               0 :               nsIApplicationCache::ITEM_IMPLICIT);
        1760               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1761                 :     }
        1762                 :
        1763               0 :     update->SetOwner(this);
        1764               0 :     rv = update->Begin();
        1765               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1766                 :
        1767               0 :     mImplicitUpdate = update;
        1768                 :
        1769               0 :     return NS_OK;
        1770                 : }
        1771                 :
        1772                 : nsresult
        1773               8 : nsOfflineCacheUpdate::FinishNoNotify()
        1774                 : {
        1775               8 :     LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
        1776                 :
        1777               8 :     mState = STATE_FINISHED;
        1778                 :
        1779               8 :     if (!mPartialUpdate) {
        1780               8 :         if (mSucceeded) {
        1781               8 :             nsIArray *namespaces = mManifestItem->GetNamespaces();
        1782               8 :             nsresult rv = mApplicationCache->AddNamespaces(namespaces);
        1783               8 :             if (NS_FAILED(rv)) {
        1784               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1785               0 :                 mSucceeded = false;
        1786                 :             }
        1787                 :
        1788               8 :             rv = mApplicationCache->Activate();
        1789               8 :             if (NS_FAILED(rv)) {
        1790               0 :                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
        1791               0 :                 mSucceeded = false;
        1792                 :             }
        1793                 :
        1794               8 :             AssociateDocuments(mApplicationCache);
        1795                 :         }
        1796                 :
        1797               8 :         if (mObsolete) {
        1798                 :             nsCOMPtr<nsIApplicationCacheService> appCacheService =
        1799               0 :                 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
        1800               0 :             if (appCacheService) {
        1801               0 :                 nsCAutoString groupID;
        1802               0 :                 mApplicationCache->GetGroupID(groupID);
        1803               0 :                 appCacheService->DeactivateGroup(groupID);
        1804                 :              }
        1805                 :          }
        1806                 :
        1807               8 :         if (!mSucceeded) {
        1808                 :             // Update was not merged, mark all the loads as failures
        1809               0 :             for (PRUint32 i = 0; i < mItems.Length(); i++) {
        1810               0 :                 mItems[i]->Cancel();
        1811                 :             }
        1812                 :
        1813               0 :             mApplicationCache->Discard();
        1814                 :         }
        1815                 :     }
        1816                 :
        1817               8 :     nsresult rv = NS_OK;
        1818                 :
        1819               8 :     if (mOwner) {
        1820               8 :         rv = mOwner->UpdateFinished(this);
        1821               8 :         mOwner = nsnull;
        1822                 :     }
        1823                 :
        1824               8 :     return rv;
        1825                 : }
        1826                 :
        1827                 : nsresult
        1828               8 : nsOfflineCacheUpdate::Finish()
        1829                 : {
        1830               8 :     nsresult rv = FinishNoNotify();
        1831                 :
        1832               8 :     NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED);
        1833                 :
        1834               8 :     return rv;
        1835                 : }
        1836                 :
        1837                 : //-----------------------------------------------------------------------------
        1838                 : // nsOfflineCacheUpdate::nsIOfflineCacheUpdate
        1839                 : //-----------------------------------------------------------------------------
        1840                 :
        1841                 : NS_IMETHODIMP
        1842               0 : nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
        1843                 : {
        1844               0 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1845                 :
        1846               0 :     aUpdateDomain = mUpdateDomain;
        1847               0 :     return NS_OK;
        1848                 : }
        1849                 :
        1850                 : NS_IMETHODIMP
        1851               0 : nsOfflineCacheUpdate::GetStatus(PRUint16 *aStatus)
        1852                 : {
        1853               0 :     switch (mState) {
        1854                 :     case STATE_CHECKING :
        1855               0 :         *aStatus = nsIDOMOfflineResourceList::CHECKING;
        1856               0 :         return NS_OK;
        1857                 :     case STATE_DOWNLOADING :
        1858               0 :         *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
        1859               0 :         return NS_OK;
        1860                 :     default :
        1861               0 :         *aStatus = nsIDOMOfflineResourceList::IDLE;
        1862               0 :         return NS_OK;
        1863                 :     }
        1864                 :
        1865                 :     return NS_ERROR_FAILURE;
        1866                 : }
        1867                 :
        1868                 : NS_IMETHODIMP
        1869               0 : nsOfflineCacheUpdate::GetPartial(bool *aPartial)
        1870                 : {
        1871               0 :     *aPartial = mPartialUpdate;
        1872               0 :     return NS_OK;
        1873                 : }
        1874                 :
        1875                 : NS_IMETHODIMP
        1876               0 : nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
        1877                 : {
        1878               0 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1879                 :
        1880               0 :     NS_IF_ADDREF(*aManifestURI = mManifestURI);
        1881               0 :     return NS_OK;
        1882                 : }
        1883                 :
        1884                 : NS_IMETHODIMP
        1885               0 : nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded)
        1886                 : {
        1887               0 :     NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
        1888                 :
        1889               0 :     *aSucceeded = mSucceeded;
        1890                 :
        1891               0 :     return NS_OK;
        1892                 : }
        1893                 :
        1894                 : NS_IMETHODIMP
        1895               0 : nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade)
        1896                 : {
        1897               0 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1898                 :
        1899               0 :     *aIsUpgrade = (mPreviousApplicationCache != nsnull);
        1900                 :
        1901               0 :     return NS_OK;
        1902                 : }
        1903                 :
        1904                 : nsresult
        1905              16 : nsOfflineCacheUpdate::AddURI(nsIURI *aURI, PRUint32 aType)
        1906                 : {
        1907              16 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1908                 :
        1909              16 :     if (mState >= STATE_DOWNLOADING)
        1910               0 :         return NS_ERROR_NOT_AVAILABLE;
        1911                 :
        1912                 :     // Resource URIs must have the same scheme as the manifest.
        1913              32 :     nsCAutoString scheme;
        1914              16 :     aURI->GetScheme(scheme);
        1915                 :
        1916                 :     bool match;
        1917              16 :     if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
        1918               0 :         return NS_ERROR_FAILURE;
        1919                 :
        1920                 :     // Don't fetch the same URI twice.
        1921              24 :     for (PRUint32 i = 0; i < mItems.Length(); i++) {
        1922                 :         bool equals;
        1923               8 :         if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) {
        1924                 :             // retain both types.
        1925               0 :             mItems[i]->mItemType |= aType;
        1926               0 :             return NS_OK;
        1927                 :         }
        1928                 :     }
        1929                 :
        1930                 :     nsRefPtr<nsOfflineCacheUpdateItem> item =
        1931                 :         new nsOfflineCacheUpdateItem(this, aURI, mDocumentURI,
        1932                 :                                      mPreviousApplicationCache, mClientID,
        1933              48 :                                      aType);
        1934              16 :     if (!item) return NS_ERROR_OUT_OF_MEMORY;
        1935                 :
        1936              16 :     mItems.AppendElement(item);
        1937              16 :     mAddedItems = true;
        1938                 :
        1939              16 :     return NS_OK;
        1940                 : }
        1941                 :
        1942                 : NS_IMETHODIMP
        1943               0 : nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
        1944                 : {
        1945               0 :     if (GeckoProcessType_Default != XRE_GetProcessType())
        1946               0 :         return NS_ERROR_NOT_IMPLEMENTED;
        1947                 :
        1948                 :     // If this is a partial update and the resource is already in the
        1949                 :     // cache, we should only mark the entry, not fetch it again.
        1950               0 :     if (mPartialUpdate) {
        1951               0 :         nsCAutoString key;
        1952               0 :         GetCacheKey(aURI, key);
        1953                 :
        1954                 :         PRUint32 types;
        1955               0 :         nsresult rv = mApplicationCache->GetTypes(key, &types);
        1956               0 :         if (NS_SUCCEEDED(rv)) {
        1957               0 :             if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) {
        1958               0 :                 mApplicationCache->MarkEntry
        1959               0 :                     (key, nsIApplicationCache::ITEM_DYNAMIC);
        1960                 :             }
        1961               0 :             return NS_OK;
        1962                 :         }
        1963                 :     }
        1964                 :
        1965               0 :     return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
        1966                 : }
        1967                 :
        1968                 : NS_IMETHODIMP
        1969               8 : nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
        1970                 :                                   bool aHoldWeak)
        1971                 : {
        1972               8 :     LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this));
        1973                 :
        1974               8 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1975                 :
        1976               8 :     if (aHoldWeak) {
        1977               0 :         nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
        1978               0 :         mWeakObservers.AppendObject(weakRef);
        1979                 :     } else {
        1980               8 :         mObservers.AppendObject(aObserver);
        1981                 :     }
        1982                 :
        1983               8 :     return NS_OK;
        1984                 : }
        1985                 :
        1986                 : NS_IMETHODIMP
        1987               8 : nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
        1988                 : {
        1989               8 :     LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this));
        1990                 :
        1991               8 :     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
        1992                 :
        1993               8 :     for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
        1994                 :         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
        1995               0 :             do_QueryReferent(mWeakObservers[i]);
        1996               0 :         if (observer == aObserver) {
        1997               0 :             mWeakObservers.RemoveObjectAt(i);
        1998               0 :             return NS_OK;
        1999                 :         }
        2000                 :     }
        2001                 :
        2002               8 :     for (PRInt32 i = 0; i < mObservers.Count(); i++) {
        2003               8 :         if (mObservers[i] == aObserver) {
        2004               8 :             mObservers.RemoveObjectAt(i);
        2005               8 :             return NS_OK;
        2006                 :         }
        2007                 :     }
        2008                 :
        2009               0 :     return NS_OK;
        2010                 : }
        2011                 :
        2012                 :
        2013                 : NS_IMETHODIMP
        2014               8 : nsOfflineCacheUpdate::Schedule()
        2015                 : {
        2016               8 :     LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
        2017                 :
        2018                 :     nsOfflineCacheUpdateService* service =
        2019               8 :         nsOfflineCacheUpdateService::EnsureService();
        2020                 :
        2021               8 :     if (!service) {
        2022               0 :         return NS_ERROR_FAILURE;
        2023                 :     }
        2024                 :
        2025               8 :     return service->ScheduleUpdate(this);
        2026                 : }
        2027                 :
        2028                 : NS_IMETHODIMP
        2029               0 : nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
        2030                 :                                          PRUint32 aState)
        2031                 : {
        2032               0 :     if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
        2033                 :         // Take the mSucceeded flag from the underlying update, we will be
        2034                 :         // queried for it soon. mSucceeded of this update is false (manifest
        2035                 :         // check failed) but the subsequent re-fetch update might succeed
        2036                 :         bool succeeded;
        2037               0 :         aUpdate->GetSucceeded(&succeeded);
        2038               0 :         mSucceeded = succeeded;
        2039                 :     }
        2040                 :
        2041               0 :     nsresult rv = NotifyState(aState);
        2042               0 :     if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED)
        2043               0 :         aUpdate->RemoveObserver(this);
        2044                 :
        2045               0 :     return rv;
        2046                 : }
        2047                 :
        2048                 : NS_IMETHODIMP
        2049               0 : nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache)
        2050                 : {
        2051               0 :     return AssociateDocuments(applicationCache);
        2052                 : }

Generated by: LCOV version 1.7