LCOV - code coverage report
Current view: top level - dom/webbrowserpersist - nsWebBrowserPersist.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1293 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 95 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "mozilla/ArrayUtils.h"
       7             : 
       8             : #include "nspr.h"
       9             : 
      10             : #include "nsIFileStreams.h"       // New Necko file streams
      11             : #include <algorithm>
      12             : 
      13             : #include "nsAutoPtr.h"
      14             : #include "nsNetCID.h"
      15             : #include "nsNetUtil.h"
      16             : #include "nsIInterfaceRequestorUtils.h"
      17             : #include "nsILoadContext.h"
      18             : #include "nsIPrivateBrowsingChannel.h"
      19             : #include "nsComponentManagerUtils.h"
      20             : #include "nsIComponentRegistrar.h"
      21             : #include "nsIStorageStream.h"
      22             : #include "nsISeekableStream.h"
      23             : #include "nsIHttpChannel.h"
      24             : #include "nsIHttpChannelInternal.h"
      25             : #include "nsIEncodedChannel.h"
      26             : #include "nsIUploadChannel.h"
      27             : #include "nsICacheInfoChannel.h"
      28             : #include "nsIFileChannel.h"
      29             : #include "nsEscape.h"
      30             : #include "nsUnicharUtils.h"
      31             : #include "nsIStringEnumerator.h"
      32             : #include "nsCRT.h"
      33             : #include "nsContentCID.h"
      34             : #include "nsStreamUtils.h"
      35             : 
      36             : #include "nsCExternalHandlerService.h"
      37             : 
      38             : #include "nsIURL.h"
      39             : #include "nsIFileURL.h"
      40             : #include "nsIWebProgressListener.h"
      41             : #include "nsIAuthPrompt.h"
      42             : #include "nsIPrompt.h"
      43             : #include "nsISHEntry.h"
      44             : #include "nsIWebPageDescriptor.h"
      45             : #include "nsIFormControl.h"
      46             : #include "nsContentUtils.h"
      47             : 
      48             : #include "nsIImageLoadingContent.h"
      49             : 
      50             : #include "ftpCore.h"
      51             : #include "nsITransport.h"
      52             : #include "nsISocketTransport.h"
      53             : #include "nsIStringBundle.h"
      54             : #include "nsIProtocolHandler.h"
      55             : 
      56             : #include "nsIWebBrowserPersistable.h"
      57             : #include "nsWebBrowserPersist.h"
      58             : #include "WebBrowserPersistLocalDocument.h"
      59             : 
      60             : #include "nsIContent.h"
      61             : #include "nsIMIMEInfo.h"
      62             : #include "mozilla/dom/HTMLInputElement.h"
      63             : #include "mozilla/dom/HTMLSharedElement.h"
      64             : #include "mozilla/dom/HTMLSharedObjectElement.h"
      65             : #include "mozilla/Printf.h"
      66             : 
      67             : using namespace mozilla;
      68             : using namespace mozilla::dom;
      69             : 
      70             : // Buffer file writes in 32kb chunks
      71             : #define BUFFERED_OUTPUT_SIZE (1024 * 32)
      72             : 
      73           0 : struct nsWebBrowserPersist::WalkData
      74             : {
      75             :     nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
      76             :     nsCOMPtr<nsIURI> mFile;
      77             :     nsCOMPtr<nsIURI> mDataPath;
      78             : };
      79             : 
      80             : // Information about a DOM document
      81           0 : struct nsWebBrowserPersist::DocData
      82             : {
      83             :     nsCOMPtr<nsIURI> mBaseURI;
      84             :     nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
      85             :     nsCOMPtr<nsIURI> mFile;
      86             :     nsCString mCharset;
      87             : };
      88             : 
      89             : // Information about a URI
      90           0 : struct nsWebBrowserPersist::URIData
      91             : {
      92             :     bool mNeedsPersisting;
      93             :     bool mSaved;
      94             :     bool mIsSubFrame;
      95             :     bool mDataPathIsRelative;
      96             :     bool mNeedsFixup;
      97             :     nsString mFilename;
      98             :     nsString mSubFrameExt;
      99             :     nsCOMPtr<nsIURI> mFile;
     100             :     nsCOMPtr<nsIURI> mDataPath;
     101             :     nsCOMPtr<nsIURI> mRelativeDocumentURI;
     102             :     nsCString mRelativePathToData;
     103             :     nsCString mCharset;
     104             : 
     105             :     nsresult GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut);
     106             : };
     107             : 
     108             : // Information about the output stream
     109             : struct nsWebBrowserPersist::OutputData
     110             : {
     111             :     nsCOMPtr<nsIURI> mFile;
     112             :     nsCOMPtr<nsIURI> mOriginalLocation;
     113             :     nsCOMPtr<nsIOutputStream> mStream;
     114             :     int64_t mSelfProgress;
     115             :     int64_t mSelfProgressMax;
     116             :     bool mCalcFileExt;
     117             : 
     118           0 :     OutputData(nsIURI *aFile, nsIURI *aOriginalLocation, bool aCalcFileExt) :
     119             :         mFile(aFile),
     120             :         mOriginalLocation(aOriginalLocation),
     121             :         mSelfProgress(0),
     122             :         mSelfProgressMax(10000),
     123           0 :         mCalcFileExt(aCalcFileExt)
     124             :     {
     125           0 :     }
     126           0 :     ~OutputData()
     127           0 :     {
     128           0 :         if (mStream)
     129             :         {
     130           0 :             mStream->Close();
     131             :         }
     132           0 :     }
     133             : };
     134             : 
     135           0 : struct nsWebBrowserPersist::UploadData
     136             : {
     137             :     nsCOMPtr<nsIURI> mFile;
     138             :     int64_t mSelfProgress;
     139             :     int64_t mSelfProgressMax;
     140             : 
     141           0 :     explicit UploadData(nsIURI *aFile) :
     142             :         mFile(aFile),
     143             :         mSelfProgress(0),
     144           0 :         mSelfProgressMax(10000)
     145             :     {
     146           0 :     }
     147             : };
     148             : 
     149           0 : struct nsWebBrowserPersist::CleanupData
     150             : {
     151             :     nsCOMPtr<nsIFile> mFile;
     152             :     // Snapshot of what the file actually is at the time of creation so that if
     153             :     // it transmutes into something else later on it can be ignored. For example,
     154             :     // catch files that turn into dirs or vice versa.
     155             :     bool mIsDirectory;
     156             : };
     157             : 
     158             : class nsWebBrowserPersist::OnWalk final
     159             :     : public nsIWebBrowserPersistResourceVisitor
     160             : {
     161             : public:
     162           0 :     OnWalk(nsWebBrowserPersist* aParent, nsIURI* aFile, nsIFile* aDataPath)
     163           0 :     : mParent(aParent)
     164             :     , mFile(aFile)
     165           0 :     , mDataPath(aDataPath)
     166           0 :     { }
     167             : 
     168             :     NS_DECL_NSIWEBBROWSERPERSISTRESOURCEVISITOR
     169             :     NS_DECL_ISUPPORTS
     170             : private:
     171             :     RefPtr<nsWebBrowserPersist> mParent;
     172             :     nsCOMPtr<nsIURI> mFile;
     173             :     nsCOMPtr<nsIFile> mDataPath;
     174             : 
     175           0 :     virtual ~OnWalk() = default;
     176             : };
     177             : 
     178           0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnWalk,
     179             :                   nsIWebBrowserPersistResourceVisitor)
     180             : 
     181             : class nsWebBrowserPersist::OnWrite final
     182             :     : public nsIWebBrowserPersistWriteCompletion
     183             : {
     184             : public:
     185           0 :     OnWrite(nsWebBrowserPersist* aParent,
     186             :             nsIURI* aFile,
     187             :             nsIFile* aLocalFile)
     188           0 :     : mParent(aParent)
     189             :     , mFile(aFile)
     190           0 :     , mLocalFile(aLocalFile)
     191           0 :     { }
     192             : 
     193             :     NS_DECL_NSIWEBBROWSERPERSISTWRITECOMPLETION
     194             :     NS_DECL_ISUPPORTS
     195             : private:
     196             :     RefPtr<nsWebBrowserPersist> mParent;
     197             :     nsCOMPtr<nsIURI> mFile;
     198             :     nsCOMPtr<nsIFile> mLocalFile;
     199             : 
     200           0 :     virtual ~OnWrite() = default;
     201             : };
     202             : 
     203           0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnWrite,
     204             :                   nsIWebBrowserPersistWriteCompletion)
     205             : 
     206             : class nsWebBrowserPersist::FlatURIMap final
     207             :     : public nsIWebBrowserPersistURIMap
     208             : {
     209             : public:
     210           0 :     explicit FlatURIMap(const nsACString& aTargetBase)
     211           0 :     : mTargetBase(aTargetBase) { }
     212             : 
     213           0 :     void Add(const nsACString& aMapFrom, const nsACString& aMapTo) {
     214           0 :         mMapFrom.AppendElement(aMapFrom);
     215           0 :         mMapTo.AppendElement(aMapTo);
     216           0 :     }
     217             : 
     218             :     NS_DECL_NSIWEBBROWSERPERSISTURIMAP
     219             :     NS_DECL_ISUPPORTS
     220             : 
     221             : private:
     222             :     nsTArray<nsCString> mMapFrom;
     223             :     nsTArray<nsCString> mMapTo;
     224             :     nsCString mTargetBase;
     225             : 
     226           0 :     virtual ~FlatURIMap() = default;
     227             : };
     228             : 
     229           0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::FlatURIMap, nsIWebBrowserPersistURIMap)
     230             : 
     231             : NS_IMETHODIMP
     232           0 : nsWebBrowserPersist::FlatURIMap::GetNumMappedURIs(uint32_t* aNum)
     233             : {
     234           0 :     MOZ_ASSERT(mMapFrom.Length() == mMapTo.Length());
     235           0 :     *aNum = mMapTo.Length();
     236           0 :     return NS_OK;
     237             : }
     238             : 
     239             : NS_IMETHODIMP
     240           0 : nsWebBrowserPersist::FlatURIMap::GetTargetBaseURI(nsACString& aTargetBase)
     241             : {
     242           0 :     aTargetBase = mTargetBase;
     243           0 :     return NS_OK;
     244             : }
     245             : 
     246             : NS_IMETHODIMP
     247           0 : nsWebBrowserPersist::FlatURIMap::GetURIMapping(uint32_t aIndex,
     248             :                                       nsACString& aMapFrom,
     249             :                                       nsACString& aMapTo)
     250             : {
     251           0 :     MOZ_ASSERT(mMapFrom.Length() == mMapTo.Length());
     252           0 :     if (aIndex >= mMapTo.Length()) {
     253           0 :         return NS_ERROR_INVALID_ARG;
     254             :     }
     255           0 :     aMapFrom = mMapFrom[aIndex];
     256           0 :     aMapTo = mMapTo[aIndex];
     257           0 :     return NS_OK;
     258             : }
     259             : 
     260             : 
     261             : // Maximum file length constant. The max file name length is
     262             : // volume / server dependent but it is difficult to obtain
     263             : // that information. Instead this constant is a reasonable value that
     264             : // modern systems should able to cope with.
     265             : const uint32_t kDefaultMaxFilenameLength = 64;
     266             : 
     267             : // Default flags for persistence
     268             : const uint32_t kDefaultPersistFlags =
     269             :     nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
     270             :     nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
     271             : 
     272             : // String bundle where error messages come from
     273             : const char *kWebBrowserPersistStringBundle =
     274             :     "chrome://global/locale/nsWebBrowserPersist.properties";
     275             : 
     276           0 : nsWebBrowserPersist::nsWebBrowserPersist() :
     277             :     mCurrentDataPathIsRelative(false),
     278             :     mCurrentThingsToPersist(0),
     279             :     mFirstAndOnlyUse(true),
     280             :     mSavingDocument(false),
     281             :     mCancel(false),
     282             :     mCompleted(false),
     283             :     mStartSaving(false),
     284             :     mReplaceExisting(true),
     285             :     mSerializingOutput(false),
     286             :     mIsPrivate(false),
     287             :     mPersistFlags(kDefaultPersistFlags),
     288             :     mPersistResult(NS_OK),
     289             :     mTotalCurrentProgress(0),
     290             :     mTotalMaxProgress(0),
     291             :     mWrapColumn(72),
     292           0 :     mEncodingFlags(0)
     293             : {
     294           0 : }
     295             : 
     296           0 : nsWebBrowserPersist::~nsWebBrowserPersist()
     297             : {
     298           0 :     Cleanup();
     299           0 : }
     300             : 
     301             : //*****************************************************************************
     302             : // nsWebBrowserPersist::nsISupports
     303             : //*****************************************************************************
     304             : 
     305           0 : NS_IMPL_ADDREF(nsWebBrowserPersist)
     306           0 : NS_IMPL_RELEASE(nsWebBrowserPersist)
     307             : 
     308           0 : NS_INTERFACE_MAP_BEGIN(nsWebBrowserPersist)
     309           0 :     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersist)
     310           0 :     NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersist)
     311           0 :     NS_INTERFACE_MAP_ENTRY(nsICancelable)
     312           0 :     NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
     313           0 :     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     314           0 :     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
     315           0 :     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
     316           0 :     NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
     317           0 : NS_INTERFACE_MAP_END
     318             : 
     319             : 
     320             : //*****************************************************************************
     321             : // nsWebBrowserPersist::nsIInterfaceRequestor
     322             : //*****************************************************************************
     323             : 
     324           0 : NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
     325             : {
     326           0 :     NS_ENSURE_ARG_POINTER(aIFace);
     327             : 
     328           0 :     *aIFace = nullptr;
     329             : 
     330           0 :     nsresult rv = QueryInterface(aIID, aIFace);
     331           0 :     if (NS_SUCCEEDED(rv))
     332             :     {
     333           0 :         return rv;
     334             :     }
     335             : 
     336           0 :     if (mProgressListener && (aIID.Equals(NS_GET_IID(nsIAuthPrompt))
     337           0 :                              || aIID.Equals(NS_GET_IID(nsIPrompt))))
     338             :     {
     339           0 :         mProgressListener->QueryInterface(aIID, aIFace);
     340           0 :         if (*aIFace)
     341           0 :             return NS_OK;
     342             :     }
     343             : 
     344           0 :     nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
     345           0 :     if (req)
     346             :     {
     347           0 :         return req->GetInterface(aIID, aIFace);
     348             :     }
     349             : 
     350           0 :     return NS_ERROR_NO_INTERFACE;
     351             : }
     352             : 
     353             : 
     354             : //*****************************************************************************
     355             : // nsWebBrowserPersist::nsIWebBrowserPersist
     356             : //*****************************************************************************
     357             : 
     358           0 : NS_IMETHODIMP nsWebBrowserPersist::GetPersistFlags(uint32_t *aPersistFlags)
     359             : {
     360           0 :     NS_ENSURE_ARG_POINTER(aPersistFlags);
     361           0 :     *aPersistFlags = mPersistFlags;
     362           0 :     return NS_OK;
     363             : }
     364           0 : NS_IMETHODIMP nsWebBrowserPersist::SetPersistFlags(uint32_t aPersistFlags)
     365             : {
     366           0 :     mPersistFlags = aPersistFlags;
     367           0 :     mReplaceExisting = (mPersistFlags & PERSIST_FLAGS_REPLACE_EXISTING_FILES) ? true : false;
     368           0 :     mSerializingOutput = (mPersistFlags & PERSIST_FLAGS_SERIALIZE_OUTPUT) ? true : false;
     369           0 :     return NS_OK;
     370             : }
     371             : 
     372           0 : NS_IMETHODIMP nsWebBrowserPersist::GetCurrentState(uint32_t *aCurrentState)
     373             : {
     374           0 :     NS_ENSURE_ARG_POINTER(aCurrentState);
     375           0 :     if (mCompleted)
     376             :     {
     377           0 :         *aCurrentState = PERSIST_STATE_FINISHED;
     378             :     }
     379           0 :     else if (mFirstAndOnlyUse)
     380             :     {
     381           0 :         *aCurrentState = PERSIST_STATE_SAVING;
     382             :     }
     383             :     else
     384             :     {
     385           0 :         *aCurrentState = PERSIST_STATE_READY;
     386             :     }
     387           0 :     return NS_OK;
     388             : }
     389             : 
     390           0 : NS_IMETHODIMP nsWebBrowserPersist::GetResult(nsresult *aResult)
     391             : {
     392           0 :     NS_ENSURE_ARG_POINTER(aResult);
     393           0 :     *aResult = mPersistResult;
     394           0 :     return NS_OK;
     395             : }
     396             : 
     397           0 : NS_IMETHODIMP nsWebBrowserPersist::GetProgressListener(
     398             :     nsIWebProgressListener * *aProgressListener)
     399             : {
     400           0 :     NS_ENSURE_ARG_POINTER(aProgressListener);
     401           0 :     *aProgressListener = mProgressListener;
     402           0 :     NS_IF_ADDREF(*aProgressListener);
     403           0 :     return NS_OK;
     404             : }
     405             : 
     406           0 : NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
     407             :     nsIWebProgressListener * aProgressListener)
     408             : {
     409           0 :     mProgressListener = aProgressListener;
     410           0 :     mProgressListener2 = do_QueryInterface(aProgressListener);
     411           0 :     mEventSink = do_GetInterface(aProgressListener);
     412           0 :     return NS_OK;
     413             : }
     414             : 
     415           0 : NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
     416             :     nsIURI *aURI, nsISupports *aCacheKey,
     417             :     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     418             :     nsIInputStream *aPostData, const char *aExtraHeaders,
     419             :     nsISupports *aFile, nsILoadContext* aPrivacyContext)
     420             : {
     421           0 :     return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aReferrerPolicy,
     422             :                                aPostData, aExtraHeaders, aFile,
     423           0 :                                aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
     424             : }
     425             : 
     426           0 : NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
     427             :     nsIURI *aURI, nsISupports *aCacheKey,
     428             :     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     429             :     nsIInputStream *aPostData, const char *aExtraHeaders,
     430             :     nsISupports *aFile, bool aIsPrivate)
     431             : {
     432           0 :     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     433           0 :     mFirstAndOnlyUse = false; // Stop people from reusing this object!
     434             : 
     435           0 :     nsCOMPtr<nsIURI> fileAsURI;
     436             :     nsresult rv;
     437           0 :     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
     438           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
     439             : 
     440             :     // SaveURI doesn't like broken uris.
     441           0 :     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
     442           0 :     rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aReferrerPolicy,
     443             :                          aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
     444           0 :     return NS_FAILED(rv) ? rv : NS_OK;
     445             : }
     446             : 
     447           0 : NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
     448             :     nsIChannel *aChannel, nsISupports *aFile)
     449             : {
     450           0 :     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     451           0 :     mFirstAndOnlyUse = false; // Stop people from reusing this object!
     452             : 
     453           0 :     nsCOMPtr<nsIURI> fileAsURI;
     454             :     nsresult rv;
     455           0 :     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
     456           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
     457             : 
     458           0 :     rv = aChannel->GetURI(getter_AddRefs(mURI));
     459           0 :     NS_ENSURE_SUCCESS(rv, rv);
     460             : 
     461             :     // SaveURI doesn't like broken uris.
     462           0 :     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
     463           0 :     rv = SaveChannelInternal(aChannel, fileAsURI, false);
     464           0 :     return NS_FAILED(rv) ? rv : NS_OK;
     465             : }
     466             : 
     467             : 
     468           0 : NS_IMETHODIMP nsWebBrowserPersist::SaveDocument(
     469             :     nsISupports *aDocument, nsISupports *aFile, nsISupports *aDataPath,
     470             :     const char *aOutputContentType, uint32_t aEncodingFlags, uint32_t aWrapColumn)
     471             : {
     472           0 :     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     473           0 :     mFirstAndOnlyUse = false; // Stop people from reusing this object!
     474             : 
     475             :     // We need a STATE_IS_NETWORK start/stop pair to bracket the
     476             :     // notification callbacks.  For a whole document we generate those
     477             :     // here and in EndDownload(), but for the single-request methods
     478             :     // that's done in On{Start,Stop}Request instead.
     479           0 :     mSavingDocument = true;
     480             : 
     481           0 :     NS_ENSURE_ARG_POINTER(aDocument);
     482           0 :     NS_ENSURE_ARG_POINTER(aFile);
     483             : 
     484           0 :     nsCOMPtr<nsIURI> fileAsURI;
     485           0 :     nsCOMPtr<nsIURI> datapathAsURI;
     486             :     nsresult rv;
     487             : 
     488           0 :     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
     489           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
     490           0 :     if (aDataPath)
     491             :     {
     492           0 :         rv = GetValidURIFromObject(aDataPath, getter_AddRefs(datapathAsURI));
     493           0 :         NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
     494             :     }
     495             : 
     496           0 :     mWrapColumn = aWrapColumn;
     497           0 :     mEncodingFlags = aEncodingFlags;
     498             : 
     499           0 :     if (aOutputContentType)
     500             :     {
     501           0 :         mContentType.AssignASCII(aOutputContentType);
     502             :     }
     503             : 
     504             :     // State start notification
     505           0 :     if (mProgressListener) {
     506           0 :         mProgressListener->OnStateChange(nullptr, nullptr,
     507             :             nsIWebProgressListener::STATE_START
     508           0 :             | nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
     509             :     }
     510             : 
     511           0 :     nsCOMPtr<nsIWebBrowserPersistDocument> doc = do_QueryInterface(aDocument);
     512           0 :     if (!doc) {
     513           0 :         nsCOMPtr<nsIDocument> localDoc = do_QueryInterface(aDocument);
     514           0 :         if (localDoc) {
     515           0 :             doc = new mozilla::WebBrowserPersistLocalDocument(localDoc);
     516             :         } else {
     517           0 :             rv = NS_ERROR_NO_INTERFACE;
     518             :         }
     519             :     }
     520           0 :     if (doc) {
     521           0 :         rv = SaveDocumentInternal(doc, fileAsURI, datapathAsURI);
     522             :     }
     523           0 :     if (NS_FAILED(rv)) {
     524           0 :         SendErrorStatusChange(true, rv, nullptr, mURI);
     525           0 :         EndDownload(rv);
     526             :     }
     527           0 :     return rv;
     528             : }
     529             : 
     530           0 : NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
     531             : {
     532           0 :     mCancel = true;
     533           0 :     EndDownload(aReason);
     534           0 :     return NS_OK;
     535             : }
     536             : 
     537             : 
     538           0 : NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
     539             : {
     540           0 :     return Cancel(NS_BINDING_ABORTED);
     541             : }
     542             : 
     543             : 
     544             : nsresult
     545           0 : nsWebBrowserPersist::StartUpload(nsIStorageStream *storStream,
     546             :     nsIURI *aDestinationURI, const nsACString &aContentType)
     547             : {
     548             :      // setup the upload channel if the destination is not local
     549           0 :     nsCOMPtr<nsIInputStream> inputstream;
     550           0 :     nsresult rv = storStream->NewInputStream(0, getter_AddRefs(inputstream));
     551           0 :     NS_ENSURE_TRUE(inputstream, NS_ERROR_FAILURE);
     552           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
     553           0 :     return StartUpload(inputstream, aDestinationURI, aContentType);
     554             : }
     555             : 
     556             : nsresult
     557           0 : nsWebBrowserPersist::StartUpload(nsIInputStream *aInputStream,
     558             :     nsIURI *aDestinationURI, const nsACString &aContentType)
     559             : {
     560           0 :     nsCOMPtr<nsIChannel> destChannel;
     561           0 :     CreateChannelFromURI(aDestinationURI, getter_AddRefs(destChannel));
     562           0 :     nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(destChannel));
     563           0 :     NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
     564             : 
     565             :     // Set the upload stream
     566             :     // NOTE: ALL data must be available in "inputstream"
     567           0 :     nsresult rv = uploadChannel->SetUploadStream(aInputStream, aContentType, -1);
     568           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
     569           0 :     rv = destChannel->AsyncOpen2(this);
     570           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
     571             : 
     572             :     // add this to the upload list
     573           0 :     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
     574           0 :     mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
     575             : 
     576           0 :     return NS_OK;
     577             : }
     578             : 
     579             : void
     580           0 : nsWebBrowserPersist::SerializeNextFile()
     581             : {
     582           0 :     nsresult rv = NS_OK;
     583           0 :     MOZ_ASSERT(mWalkStack.Length() == 0);
     584             : 
     585             :     // First, handle gathered URIs.
     586             :     // Count how many URIs in the URI map require persisting
     587           0 :     uint32_t urisToPersist = 0;
     588           0 :     if (mURIMap.Count() > 0) {
     589             :         // This is potentially O(n^2), when taking into account the
     590             :         // number of times this method is called.  If it becomes a
     591             :         // bottleneck, the count of not-yet-persisted URIs could be
     592             :         // maintained separately.
     593           0 :         for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
     594           0 :             URIData *data = iter.UserData();
     595           0 :             if (data->mNeedsPersisting && !data->mSaved) {
     596           0 :                 urisToPersist++;
     597             :             }
     598             :         }
     599             :     }
     600             : 
     601           0 :     if (urisToPersist > 0) {
     602             :         // Persist each file in the uri map. The document(s)
     603             :         // will be saved after the last one of these is saved.
     604           0 :         for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
     605           0 :             URIData *data = iter.UserData();
     606             : 
     607           0 :             if (!data->mNeedsPersisting || data->mSaved) {
     608           0 :                 continue;
     609             :             }
     610             : 
     611             :             nsresult rv;
     612             : 
     613             :             // Create a URI from the key.
     614           0 :             nsCOMPtr<nsIURI> uri;
     615           0 :             rv = NS_NewURI(getter_AddRefs(uri), iter.Key(),
     616           0 :                            data->mCharset.get());
     617           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
     618           0 :                 break;
     619             :             }
     620             : 
     621             :             // Make a URI to save the data to.
     622           0 :             nsCOMPtr<nsIURI> fileAsURI;
     623           0 :             rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
     624           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
     625           0 :                 break;
     626             :             }
     627           0 :             rv = AppendPathToURI(fileAsURI, data->mFilename);
     628           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
     629           0 :                 break;
     630             :             }
     631             : 
     632             :             // The Referrer Policy doesn't matter here since the referrer is
     633             :             // nullptr.
     634           0 :             rv = SaveURIInternal(uri, nullptr, nullptr,
     635             :                                  mozilla::net::RP_Unset, nullptr, nullptr,
     636           0 :                                  fileAsURI, true, mIsPrivate);
     637             :             // If SaveURIInternal fails, then it will have called EndDownload,
     638             :             // which means that |data| is no longer valid memory. We MUST bail.
     639           0 :             if (NS_WARN_IF(NS_FAILED(rv))) {
     640           0 :                 break;
     641             :             }
     642             : 
     643           0 :             if (rv == NS_OK) {
     644             :                 // Store the actual object because once it's persisted this
     645             :                 // will be fixed up with the right file extension.
     646           0 :                 data->mFile = fileAsURI;
     647           0 :                 data->mSaved = true;
     648             :             } else {
     649           0 :                 data->mNeedsFixup = false;
     650             :             }
     651             : 
     652           0 :             if (mSerializingOutput) {
     653           0 :                 break;
     654             :             }
     655             :         }
     656             :     }
     657             : 
     658             :     // If there are downloads happening, wait until they're done; the
     659             :     // OnStopRequest handler will call this method again.
     660           0 :     if (mOutputMap.Count() > 0) {
     661           0 :         return;
     662             :     }
     663             : 
     664             :     // If serializing, also wait until last upload is done.
     665           0 :     if (mSerializingOutput && mUploadList.Count() > 0) {
     666           0 :         return;
     667             :     }
     668             : 
     669             :     // If there are also no more documents, then we're done.
     670           0 :     if (mDocList.Length() == 0) {
     671             :         // ...or not quite done, if there are still uploads.
     672           0 :         if (mUploadList.Count() > 0) {
     673           0 :             return;
     674             :         }
     675             :         // Finish and clean things up.  Defer this because the caller
     676             :         // may have been expecting to use the listeners that that
     677             :         // method will clear.
     678           0 :         NS_DispatchToCurrentThread(
     679           0 :           NewRunnableMethod("nsWebBrowserPersist::FinishDownload",
     680             :                             this,
     681           0 :                             &nsWebBrowserPersist::FinishDownload));
     682           0 :         return;
     683             :     }
     684             : 
     685             :     // There are no URIs to save, so just save the next document.
     686           0 :     mStartSaving = true;
     687           0 :     mozilla::UniquePtr<DocData> docData(mDocList.ElementAt(0));
     688           0 :     mDocList.RemoveElementAt(0); // O(n^2) but probably doesn't matter.
     689           0 :     MOZ_ASSERT(docData);
     690           0 :     if (!docData) {
     691           0 :         EndDownload(NS_ERROR_FAILURE);
     692           0 :         return;
     693             :     }
     694             : 
     695           0 :     mCurrentBaseURI = docData->mBaseURI;
     696           0 :     mCurrentCharset = docData->mCharset;
     697           0 :     mTargetBaseURI = docData->mFile;
     698             : 
     699             :     // Save the document, fixing it up with the new URIs as we do
     700             : 
     701           0 :     nsAutoCString targetBaseSpec;
     702           0 :     if (mTargetBaseURI) {
     703           0 :         rv = mTargetBaseURI->GetSpec(targetBaseSpec);
     704           0 :         if (NS_FAILED(rv)) {
     705           0 :             SendErrorStatusChange(true, rv, nullptr, nullptr);
     706           0 :             EndDownload(rv);
     707           0 :             return;
     708             :         }
     709             :     }
     710             : 
     711             :     // mFlatURIMap must be rebuilt each time through SerializeNextFile, as
     712             :     // mTargetBaseURI is used to create the relative URLs and will be different
     713             :     // with each serialized document.
     714           0 :     RefPtr<FlatURIMap> flatMap = new FlatURIMap(targetBaseSpec);
     715           0 :     for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
     716           0 :         nsAutoCString mapTo;
     717           0 :         nsresult rv = iter.UserData()->GetLocalURI(mTargetBaseURI, mapTo);
     718           0 :         if (NS_SUCCEEDED(rv) || !mapTo.IsVoid()) {
     719           0 :             flatMap->Add(iter.Key(), mapTo);
     720             :         }
     721             :     }
     722           0 :     mFlatURIMap = flatMap.forget();
     723             : 
     724           0 :     nsCOMPtr<nsIFile> localFile;
     725           0 :     GetLocalFileFromURI(docData->mFile, getter_AddRefs(localFile));
     726           0 :     if (localFile) {
     727             :         // if we're not replacing an existing file but the file
     728             :         // exists, something is wrong
     729           0 :         bool fileExists = false;
     730           0 :         rv = localFile->Exists(&fileExists);
     731           0 :         if (NS_SUCCEEDED(rv) && !mReplaceExisting && fileExists) {
     732           0 :             rv = NS_ERROR_FILE_ALREADY_EXISTS;
     733             :         }
     734           0 :         if (NS_FAILED(rv)) {
     735           0 :             SendErrorStatusChange(false, rv, nullptr, docData->mFile);
     736           0 :             EndDownload(rv);
     737           0 :             return;
     738             :         }
     739             :     }
     740           0 :     nsCOMPtr<nsIOutputStream> outputStream;
     741           0 :     rv = MakeOutputStream(docData->mFile, getter_AddRefs(outputStream));
     742           0 :     if (NS_SUCCEEDED(rv) && !outputStream) {
     743           0 :         rv = NS_ERROR_FAILURE;
     744             :     }
     745           0 :     if (NS_FAILED(rv)) {
     746           0 :         SendErrorStatusChange(false, rv, nullptr, docData->mFile);
     747           0 :         EndDownload(rv);
     748           0 :         return;
     749             :     }
     750             : 
     751           0 :     RefPtr<OnWrite> finish = new OnWrite(this, docData->mFile, localFile);
     752           0 :     rv = docData->mDocument->WriteContent(outputStream,
     753             :                                           mFlatURIMap,
     754           0 :                                           NS_ConvertUTF16toUTF8(mContentType),
     755             :                                           mEncodingFlags,
     756           0 :                                           mWrapColumn,
     757           0 :                                           finish);
     758           0 :     if (NS_FAILED(rv)) {
     759           0 :         SendErrorStatusChange(false, rv, nullptr, docData->mFile);
     760           0 :         EndDownload(rv);
     761             :     }
     762             : }
     763             : 
     764             : NS_IMETHODIMP
     765           0 : nsWebBrowserPersist::OnWrite::OnFinish(nsIWebBrowserPersistDocument* aDoc,
     766             :                                        nsIOutputStream *aStream,
     767             :                                        const nsACString& aContentType,
     768             :                                        nsresult aStatus)
     769             : {
     770           0 :     nsresult rv = aStatus;
     771             : 
     772           0 :     if (NS_FAILED(rv)) {
     773           0 :         mParent->SendErrorStatusChange(false, rv, nullptr, mFile);
     774           0 :         mParent->EndDownload(rv);
     775           0 :         return NS_OK;
     776             :     }
     777           0 :     if (!mLocalFile) {
     778           0 :         nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(aStream));
     779           0 :         if (storStream) {
     780           0 :             aStream->Close();
     781           0 :             rv = mParent->StartUpload(storStream, mFile, aContentType);
     782           0 :             if (NS_FAILED(rv)) {
     783           0 :                 mParent->SendErrorStatusChange(false, rv, nullptr, mFile);
     784           0 :                 mParent->EndDownload(rv);
     785             :             }
     786             :             // Either we failed and we're done, or we're uploading and
     787             :             // the OnStopRequest callback is responsible for the next
     788             :             // SerializeNextFile().
     789           0 :             return NS_OK;
     790             :         }
     791             :     }
     792           0 :     NS_DispatchToCurrentThread(
     793           0 :       NewRunnableMethod("nsWebBrowserPersist::SerializeNextFile",
     794             :                         mParent,
     795           0 :                         &nsWebBrowserPersist::SerializeNextFile));
     796           0 :     return NS_OK;
     797             : }
     798             : 
     799             : //*****************************************************************************
     800             : // nsWebBrowserPersist::nsIRequestObserver
     801             : //*****************************************************************************
     802             : 
     803           0 : NS_IMETHODIMP nsWebBrowserPersist::OnStartRequest(
     804             :     nsIRequest* request, nsISupports *ctxt)
     805             : {
     806           0 :     if (mProgressListener)
     807             :     {
     808             :         uint32_t stateFlags = nsIWebProgressListener::STATE_START |
     809           0 :                               nsIWebProgressListener::STATE_IS_REQUEST;
     810           0 :         if (!mSavingDocument) {
     811           0 :             stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
     812             :         }
     813           0 :         mProgressListener->OnStateChange(nullptr, request, stateFlags, NS_OK);
     814             :     }
     815             : 
     816           0 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     817           0 :     NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
     818             : 
     819           0 :     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
     820           0 :     OutputData *data = mOutputMap.Get(keyPtr);
     821             : 
     822             :     // NOTE: This code uses the channel as a hash key so it will not
     823             :     //       recognize redirected channels because the key is not the same.
     824             :     //       When that happens we remove and add the data entry to use the
     825             :     //       new channel as the hash key.
     826           0 :     if (!data)
     827             :     {
     828           0 :         UploadData *upData = mUploadList.Get(keyPtr);
     829           0 :         if (!upData)
     830             :         {
     831             :             // Redirect? Try and fixup the output table
     832           0 :             nsresult rv = FixRedirectedChannelEntry(channel);
     833           0 :             NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
     834             : 
     835             :             // Should be able to find the data after fixup unless redirects
     836             :             // are disabled.
     837           0 :             data = mOutputMap.Get(keyPtr);
     838           0 :             if (!data)
     839             :             {
     840           0 :                 return NS_ERROR_FAILURE;
     841             :             }
     842             :         }
     843             :     }
     844             : 
     845           0 :     if (data && data->mFile)
     846             :     {
     847             :         // If PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION is set in mPersistFlags,
     848             :         // try to determine whether this channel needs to apply Content-Encoding
     849             :         // conversions.
     850           0 :         NS_ASSERTION(!((mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION) &&
     851             :                       (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)),
     852             :                      "Conflict in persist flags: both AUTODETECT and NO_CONVERSION set");
     853           0 :         if (mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION)
     854           0 :             SetApplyConversionIfNeeded(channel);
     855             : 
     856           0 :         if (data->mCalcFileExt && !(mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES))
     857             :         {
     858             :             // this is the first point at which the server can tell us the mimetype
     859           0 :             CalculateAndAppendFileExt(data->mFile, channel, data->mOriginalLocation);
     860             : 
     861             :             // now make filename conformant and unique
     862           0 :             CalculateUniqueFilename(data->mFile);
     863             :         }
     864             : 
     865             :         // compare uris and bail before we add to output map if they are equal
     866           0 :         bool isEqual = false;
     867           0 :         if (NS_SUCCEEDED(data->mFile->Equals(data->mOriginalLocation, &isEqual))
     868           0 :             && isEqual)
     869             :         {
     870             :             // remove from output map
     871           0 :             mOutputMap.Remove(keyPtr);
     872             : 
     873             :             // cancel; we don't need to know any more
     874             :             // stop request will get called
     875           0 :             request->Cancel(NS_BINDING_ABORTED);
     876             :         }
     877             :     }
     878             : 
     879           0 :     return NS_OK;
     880             : }
     881             : 
     882           0 : NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest(
     883             :     nsIRequest* request, nsISupports *ctxt, nsresult status)
     884             : {
     885           0 :     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
     886           0 :     OutputData *data = mOutputMap.Get(keyPtr);
     887           0 :     if (data) {
     888           0 :         if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(status)) {
     889           0 :             SendErrorStatusChange(true, status, request, data->mFile);
     890             :         }
     891             : 
     892             :         // This will automatically close the output stream
     893           0 :         mOutputMap.Remove(keyPtr);
     894             :     } else {
     895             :         // if we didn't find the data in mOutputMap, try mUploadList
     896           0 :         UploadData *upData = mUploadList.Get(keyPtr);
     897           0 :         if (upData) {
     898           0 :             mUploadList.Remove(keyPtr);
     899             :         }
     900             :     }
     901             : 
     902             :     // Do more work.
     903           0 :     SerializeNextFile();
     904             : 
     905           0 :     if (mProgressListener) {
     906             :         uint32_t stateFlags = nsIWebProgressListener::STATE_STOP |
     907           0 :                               nsIWebProgressListener::STATE_IS_REQUEST;
     908           0 :         if (!mSavingDocument) {
     909           0 :             stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
     910             :         }
     911           0 :         mProgressListener->OnStateChange(nullptr, request, stateFlags, status);
     912             :     }
     913             : 
     914           0 :     return NS_OK;
     915             : }
     916             : 
     917             : //*****************************************************************************
     918             : // nsWebBrowserPersist::nsIStreamListener
     919             : //*****************************************************************************
     920             : 
     921             : NS_IMETHODIMP
     922           0 : nsWebBrowserPersist::OnDataAvailable(
     923             :     nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream,
     924             :     uint64_t aOffset, uint32_t aLength)
     925             : {
     926           0 :     bool cancel = mCancel;
     927           0 :     if (!cancel)
     928             :     {
     929           0 :         nsresult rv = NS_OK;
     930           0 :         uint32_t bytesRemaining = aLength;
     931             : 
     932           0 :         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     933           0 :         NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
     934             : 
     935           0 :         nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
     936           0 :         OutputData *data = mOutputMap.Get(keyPtr);
     937           0 :         if (!data) {
     938             :             // might be uploadData; consume necko's buffer and bail...
     939             :             uint32_t n;
     940           0 :             return aIStream->ReadSegments(NS_DiscardSegment, nullptr, aLength, &n);
     941             :         }
     942             : 
     943           0 :         bool readError = true;
     944             : 
     945             :         // Make the output stream
     946           0 :         if (!data->mStream)
     947             :         {
     948           0 :             rv = MakeOutputStream(data->mFile, getter_AddRefs(data->mStream));
     949           0 :             if (NS_FAILED(rv))
     950             :             {
     951           0 :                 readError = false;
     952           0 :                 cancel = true;
     953             :             }
     954             :         }
     955             : 
     956             :         // Read data from the input and write to the output
     957             :         char buffer[8192];
     958             :         uint32_t bytesRead;
     959           0 :         while (!cancel && bytesRemaining)
     960             :         {
     961           0 :             readError = true;
     962           0 :             rv = aIStream->Read(buffer,
     963           0 :                                 std::min(uint32_t(sizeof(buffer)), bytesRemaining),
     964           0 :                                 &bytesRead);
     965           0 :             if (NS_SUCCEEDED(rv))
     966             :             {
     967           0 :                 readError = false;
     968             :                 // Write out the data until something goes wrong, or, it is
     969             :                 // all written.  We loop because for some errors (e.g., disk
     970             :                 // full), we get NS_OK with some bytes written, then an error.
     971             :                 // So, we want to write again in that case to get the actual
     972             :                 // error code.
     973           0 :                 const char *bufPtr = buffer; // Where to write from.
     974           0 :                 while (NS_SUCCEEDED(rv) && bytesRead)
     975             :                 {
     976           0 :                     uint32_t bytesWritten = 0;
     977           0 :                     rv = data->mStream->Write(bufPtr, bytesRead, &bytesWritten);
     978           0 :                     if (NS_SUCCEEDED(rv))
     979             :                     {
     980           0 :                         bytesRead -= bytesWritten;
     981           0 :                         bufPtr += bytesWritten;
     982           0 :                         bytesRemaining -= bytesWritten;
     983             :                         // Force an error if (for some reason) we get NS_OK but
     984             :                         // no bytes written.
     985           0 :                         if (!bytesWritten)
     986             :                         {
     987           0 :                             rv = NS_ERROR_FAILURE;
     988           0 :                             cancel = true;
     989             :                         }
     990             :                     }
     991             :                     else
     992             :                     {
     993             :                         // Disaster - can't write out the bytes - disk full / permission?
     994           0 :                         cancel = true;
     995             :                     }
     996             :                 }
     997             :             }
     998             :             else
     999             :             {
    1000             :                 // Disaster - can't read the bytes - broken link / file error?
    1001           0 :                 cancel = true;
    1002             :             }
    1003             :         }
    1004             : 
    1005           0 :         int64_t channelContentLength = -1;
    1006           0 :         if (!cancel &&
    1007           0 :             NS_SUCCEEDED(channel->GetContentLength(&channelContentLength)))
    1008             :         {
    1009             :             // if we get -1 at this point, we didn't get content-length header
    1010             :             // assume that we got all of the data and push what we have;
    1011             :             // that's the best we can do now
    1012           0 :             if ((-1 == channelContentLength) ||
    1013           0 :                 ((channelContentLength - (aOffset + aLength)) == 0))
    1014             :             {
    1015           0 :                 NS_WARNING_ASSERTION(
    1016             :                     channelContentLength != -1,
    1017             :                     "nsWebBrowserPersist::OnDataAvailable() no content length "
    1018             :                     "header, pushing what we have");
    1019             :                 // we're done with this pass; see if we need to do upload
    1020           0 :                 nsAutoCString contentType;
    1021           0 :                 channel->GetContentType(contentType);
    1022             :                 // if we don't have the right type of output stream then it's a local file
    1023           0 :                 nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(data->mStream));
    1024           0 :                 if (storStream)
    1025             :                 {
    1026           0 :                     data->mStream->Close();
    1027           0 :                     data->mStream = nullptr; // null out stream so we don't close it later
    1028           0 :                     rv = StartUpload(storStream, data->mFile, contentType);
    1029           0 :                     if (NS_FAILED(rv))
    1030             :                     {
    1031           0 :                         readError = false;
    1032           0 :                         cancel = true;
    1033             :                     }
    1034             :                 }
    1035             :             }
    1036             :         }
    1037             : 
    1038             :         // Notify listener if an error occurred.
    1039           0 :         if (cancel)
    1040             :         {
    1041           0 :             SendErrorStatusChange(readError, rv,
    1042           0 :                 readError ? request : nullptr, data->mFile);
    1043             :         }
    1044             :     }
    1045             : 
    1046             :     // Cancel reading?
    1047           0 :     if (cancel)
    1048             :     {
    1049           0 :         EndDownload(NS_BINDING_ABORTED);
    1050             :     }
    1051             : 
    1052           0 :     return NS_OK;
    1053             : }
    1054             : 
    1055             : 
    1056             : //*****************************************************************************
    1057             : // nsWebBrowserPersist::nsIProgressEventSink
    1058             : //*****************************************************************************
    1059             : 
    1060           0 : NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
    1061             :     nsIRequest *request, nsISupports *ctxt, int64_t aProgress,
    1062             :     int64_t aProgressMax)
    1063             : {
    1064           0 :     if (!mProgressListener)
    1065             :     {
    1066           0 :         return NS_OK;
    1067             :     }
    1068             : 
    1069             :     // Store the progress of this request
    1070           0 :     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
    1071           0 :     OutputData *data = mOutputMap.Get(keyPtr);
    1072           0 :     if (data)
    1073             :     {
    1074           0 :         data->mSelfProgress = aProgress;
    1075           0 :         data->mSelfProgressMax = aProgressMax;
    1076             :     }
    1077             :     else
    1078             :     {
    1079           0 :         UploadData *upData = mUploadList.Get(keyPtr);
    1080           0 :         if (upData)
    1081             :         {
    1082           0 :             upData->mSelfProgress = aProgress;
    1083           0 :             upData->mSelfProgressMax = aProgressMax;
    1084             :         }
    1085             :     }
    1086             : 
    1087             :     // Notify listener of total progress
    1088           0 :     CalcTotalProgress();
    1089           0 :     if (mProgressListener2)
    1090             :     {
    1091           0 :       mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
    1092           0 :             aProgressMax, mTotalCurrentProgress, mTotalMaxProgress);
    1093             :     }
    1094             :     else
    1095             :     {
    1096             :       // have to truncate 64-bit to 32bit
    1097           0 :       mProgressListener->OnProgressChange(nullptr, request, uint64_t(aProgress),
    1098           0 :               uint64_t(aProgressMax), mTotalCurrentProgress, mTotalMaxProgress);
    1099             :     }
    1100             : 
    1101             :     // If our progress listener implements nsIProgressEventSink,
    1102             :     // forward the notification
    1103           0 :     if (mEventSink)
    1104             :     {
    1105           0 :         mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
    1106             :     }
    1107             : 
    1108           0 :     return NS_OK;
    1109             : }
    1110             : 
    1111           0 : NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
    1112             :     nsIRequest *request, nsISupports *ctxt, nsresult status,
    1113             :     const char16_t *statusArg)
    1114             : {
    1115           0 :     if (mProgressListener)
    1116             :     {
    1117             :         // We need to filter out non-error error codes.
    1118             :         // Is the only NS_SUCCEEDED value NS_OK?
    1119           0 :         switch ( status )
    1120             :         {
    1121             :         case NS_NET_STATUS_RESOLVING_HOST:
    1122             :         case NS_NET_STATUS_RESOLVED_HOST:
    1123             :         case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
    1124             :         case NS_NET_STATUS_END_FTP_TRANSACTION:
    1125             :         case NS_NET_STATUS_CONNECTING_TO:
    1126             :         case NS_NET_STATUS_CONNECTED_TO:
    1127             :         case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
    1128             :         case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
    1129             :         case NS_NET_STATUS_SENDING_TO:
    1130             :         case NS_NET_STATUS_RECEIVING_FROM:
    1131             :         case NS_NET_STATUS_WAITING_FOR:
    1132             :         case NS_NET_STATUS_READING:
    1133             :         case NS_NET_STATUS_WRITING:
    1134           0 :             break;
    1135             : 
    1136             :         default:
    1137             :             // Pass other notifications (for legitimate errors) along.
    1138           0 :             mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
    1139           0 :             break;
    1140             :         }
    1141             : 
    1142             :     }
    1143             : 
    1144             :     // If our progress listener implements nsIProgressEventSink,
    1145             :     // forward the notification
    1146           0 :     if (mEventSink)
    1147             :     {
    1148           0 :         mEventSink->OnStatus(request, ctxt, status, statusArg);
    1149             :     }
    1150             : 
    1151           0 :     return NS_OK;
    1152             : }
    1153             : 
    1154             : 
    1155             : //*****************************************************************************
    1156             : // nsWebBrowserPersist private methods
    1157             : //*****************************************************************************
    1158             : 
    1159             : // Convert error info into proper message text and send OnStatusChange notification
    1160             : // to the web progress listener.
    1161           0 : nsresult nsWebBrowserPersist::SendErrorStatusChange(
    1162             :     bool aIsReadError, nsresult aResult, nsIRequest *aRequest, nsIURI *aURI)
    1163             : {
    1164           0 :     NS_ENSURE_ARG_POINTER(aURI);
    1165             : 
    1166           0 :     if (!mProgressListener)
    1167             :     {
    1168             :         // Do nothing
    1169           0 :         return NS_OK;
    1170             :     }
    1171             : 
    1172             :     // Get the file path or spec from the supplied URI
    1173           0 :     nsCOMPtr<nsIFile> file;
    1174           0 :     GetLocalFileFromURI(aURI, getter_AddRefs(file));
    1175           0 :     nsAutoString path;
    1176             :     nsresult rv;
    1177           0 :     if (file)
    1178             :     {
    1179           0 :         file->GetPath(path);
    1180             :     }
    1181             :     else
    1182             :     {
    1183           0 :         nsAutoCString fileurl;
    1184           0 :         rv = aURI->GetSpec(fileurl);
    1185           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1186           0 :         AppendUTF8toUTF16(fileurl, path);
    1187             :     }
    1188             : 
    1189           0 :     nsAutoString msgId;
    1190           0 :     switch(aResult)
    1191             :     {
    1192             :     case NS_ERROR_FILE_NAME_TOO_LONG:
    1193             :         // File name too long.
    1194           0 :         msgId.AssignLiteral("fileNameTooLongError");
    1195           0 :         break;
    1196             :     case NS_ERROR_FILE_ALREADY_EXISTS:
    1197             :         // File exists with same name as directory.
    1198           0 :         msgId.AssignLiteral("fileAlreadyExistsError");
    1199           0 :         break;
    1200             :     case NS_ERROR_FILE_DISK_FULL:
    1201             :     case NS_ERROR_FILE_NO_DEVICE_SPACE:
    1202             :         // Out of space on target volume.
    1203           0 :         msgId.AssignLiteral("diskFull");
    1204           0 :         break;
    1205             : 
    1206             :     case NS_ERROR_FILE_READ_ONLY:
    1207             :         // Attempt to write to read/only file.
    1208           0 :         msgId.AssignLiteral("readOnly");
    1209           0 :         break;
    1210             : 
    1211             :     case NS_ERROR_FILE_ACCESS_DENIED:
    1212             :         // Attempt to write without sufficient permissions.
    1213           0 :         msgId.AssignLiteral("accessError");
    1214           0 :         break;
    1215             : 
    1216             :     default:
    1217             :         // Generic read/write error message.
    1218           0 :         if (aIsReadError)
    1219           0 :             msgId.AssignLiteral("readError");
    1220             :         else
    1221           0 :             msgId.AssignLiteral("writeError");
    1222           0 :         break;
    1223             :     }
    1224             :     // Get properties file bundle and extract status string.
    1225           0 :     nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    1226           0 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
    1227             : 
    1228           0 :     nsCOMPtr<nsIStringBundle> bundle;
    1229           0 :     rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
    1230           0 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
    1231             : 
    1232           0 :     nsXPIDLString msgText;
    1233             :     const char16_t *strings[1];
    1234           0 :     strings[0] = path.get();
    1235           0 :     rv = bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText));
    1236           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    1237             : 
    1238           0 :     mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
    1239             : 
    1240           0 :     return NS_OK;
    1241             : }
    1242             : 
    1243           0 : nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
    1244             : {
    1245           0 :     NS_ENSURE_ARG_POINTER(aObject);
    1246           0 :     NS_ENSURE_ARG_POINTER(aURI);
    1247             : 
    1248           0 :     nsCOMPtr<nsIFile> objAsFile = do_QueryInterface(aObject);
    1249           0 :     if (objAsFile)
    1250             :     {
    1251           0 :         return NS_NewFileURI(aURI, objAsFile);
    1252             :     }
    1253           0 :     nsCOMPtr<nsIURI> objAsURI = do_QueryInterface(aObject);
    1254           0 :     if (objAsURI)
    1255             :     {
    1256           0 :         *aURI = objAsURI;
    1257           0 :         NS_ADDREF(*aURI);
    1258           0 :         return NS_OK;
    1259             :     }
    1260             : 
    1261           0 :     return NS_ERROR_FAILURE;
    1262             : }
    1263             : 
    1264             : /* static */ nsresult
    1265           0 : nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile)
    1266             : {
    1267             :     nsresult rv;
    1268             : 
    1269           0 :     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
    1270           0 :     if (NS_FAILED(rv))
    1271           0 :         return rv;
    1272             : 
    1273           0 :     nsCOMPtr<nsIFile> file;
    1274           0 :     rv = fileURL->GetFile(getter_AddRefs(file));
    1275           0 :     if (NS_FAILED(rv)) {
    1276           0 :         return rv;
    1277             :     }
    1278             : 
    1279           0 :     file.forget(aLocalFile);
    1280           0 :     return NS_OK;
    1281             : }
    1282             : 
    1283             : /* static */ nsresult
    1284           0 : nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath)
    1285             : {
    1286           0 :     NS_ENSURE_ARG_POINTER(aURI);
    1287             : 
    1288           0 :     nsAutoCString newPath;
    1289           0 :     nsresult rv = aURI->GetPath(newPath);
    1290           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    1291             : 
    1292             :     // Append a forward slash if necessary
    1293           0 :     int32_t len = newPath.Length();
    1294           0 :     if (len > 0 && newPath.CharAt(len - 1) != '/')
    1295             :     {
    1296           0 :         newPath.Append('/');
    1297             :     }
    1298             : 
    1299             :     // Store the path back on the URI
    1300           0 :     AppendUTF16toUTF8(aPath, newPath);
    1301           0 :     aURI->SetPath(newPath);
    1302             : 
    1303           0 :     return NS_OK;
    1304             : }
    1305             : 
    1306           0 : nsresult nsWebBrowserPersist::SaveURIInternal(
    1307             :     nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
    1308             :     uint32_t aReferrerPolicy, nsIInputStream *aPostData,
    1309             :     const char *aExtraHeaders, nsIURI *aFile,
    1310             :     bool aCalcFileExt, bool aIsPrivate)
    1311             : {
    1312           0 :     NS_ENSURE_ARG_POINTER(aURI);
    1313           0 :     NS_ENSURE_ARG_POINTER(aFile);
    1314             : 
    1315           0 :     nsresult rv = NS_OK;
    1316             : 
    1317           0 :     mURI = aURI;
    1318             : 
    1319           0 :     nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
    1320           0 :     if (mPersistFlags & PERSIST_FLAGS_BYPASS_CACHE)
    1321             :     {
    1322           0 :         loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
    1323             :     }
    1324           0 :     else if (mPersistFlags & PERSIST_FLAGS_FROM_CACHE)
    1325             :     {
    1326           0 :         loadFlags |= nsIRequest::LOAD_FROM_CACHE;
    1327             :     }
    1328             : 
    1329             :     // Extract the cache key
    1330           0 :     nsCOMPtr<nsISupports> cacheKey;
    1331           0 :     if (aCacheKey)
    1332             :     {
    1333             :         // Test if the cache key is actually a web page descriptor (docshell)
    1334             :         // or session history entry.
    1335           0 :         nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(aCacheKey);
    1336           0 :         if (!shEntry)
    1337             :         {
    1338             :             nsCOMPtr<nsIWebPageDescriptor> webPageDescriptor =
    1339           0 :                 do_QueryInterface(aCacheKey);
    1340           0 :             if (webPageDescriptor)
    1341             :             {
    1342           0 :                 nsCOMPtr<nsISupports> currentDescriptor;
    1343           0 :                 webPageDescriptor->GetCurrentDescriptor(getter_AddRefs(currentDescriptor));
    1344           0 :                 shEntry = do_QueryInterface(currentDescriptor);
    1345             :             }
    1346             :         }
    1347             : 
    1348           0 :         if (shEntry)
    1349             :         {
    1350           0 :             shEntry->GetCacheKey(getter_AddRefs(cacheKey));
    1351             :         }
    1352             :         else
    1353             :         {
    1354             :             // Assume a plain cache key
    1355           0 :             cacheKey = aCacheKey;
    1356             :         }
    1357             :     }
    1358             : 
    1359             :     // Open a channel to the URI
    1360           0 :     nsCOMPtr<nsIChannel> inputChannel;
    1361           0 :     rv = NS_NewChannel(getter_AddRefs(inputChannel),
    1362             :                        aURI,
    1363             :                        nsContentUtils::GetSystemPrincipal(),
    1364             :                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    1365             :                        nsIContentPolicy::TYPE_OTHER,
    1366             :                        nullptr,  // aLoadGroup
    1367             :                        static_cast<nsIInterfaceRequestor*>(this),
    1368           0 :                        loadFlags);
    1369             : 
    1370           0 :     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
    1371           0 :     if (pbChannel)
    1372             :     {
    1373           0 :         pbChannel->SetPrivate(aIsPrivate);
    1374             :     }
    1375             : 
    1376           0 :     if (NS_FAILED(rv) || inputChannel == nullptr)
    1377             :     {
    1378           0 :         EndDownload(NS_ERROR_FAILURE);
    1379           0 :         return NS_ERROR_FAILURE;
    1380             :     }
    1381             : 
    1382             :     // Disable content conversion
    1383           0 :     if (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)
    1384             :     {
    1385           0 :         nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
    1386           0 :         if (encodedChannel)
    1387             :         {
    1388           0 :             encodedChannel->SetApplyConversion(false);
    1389             :         }
    1390             :     }
    1391             : 
    1392           0 :     if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
    1393             :     {
    1394             :         nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
    1395           0 :                 do_QueryInterface(inputChannel);
    1396           0 :         if (httpChannelInternal) {
    1397           0 :             rv = httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
    1398           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1399             :         }
    1400             :     }
    1401             : 
    1402             :     // Set the referrer, post data and headers if any
    1403           0 :     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
    1404           0 :     if (httpChannel)
    1405             :     {
    1406             :         // Referrer
    1407           0 :         if (aReferrer)
    1408             :         {
    1409           0 :             rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
    1410           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1411             :         }
    1412             : 
    1413             :         // Post data
    1414           0 :         if (aPostData)
    1415             :         {
    1416           0 :             nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
    1417           0 :             if (stream)
    1418             :             {
    1419             :                 // Rewind the postdata stream
    1420           0 :                 stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    1421           0 :                 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
    1422           0 :                 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
    1423             :                 // Attach the postdata to the http channel
    1424           0 :                 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
    1425             :             }
    1426             :         }
    1427             : 
    1428             :         // Cache key
    1429           0 :         nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(httpChannel));
    1430           0 :         if (cacheChannel && cacheKey)
    1431             :         {
    1432           0 :             cacheChannel->SetCacheKey(cacheKey);
    1433             :         }
    1434             : 
    1435             :         // Headers
    1436           0 :         if (aExtraHeaders)
    1437             :         {
    1438           0 :             nsAutoCString oneHeader;
    1439           0 :             nsAutoCString headerName;
    1440           0 :             nsAutoCString headerValue;
    1441           0 :             int32_t crlf = 0;
    1442           0 :             int32_t colon = 0;
    1443           0 :             const char *kWhitespace = "\b\t\r\n ";
    1444           0 :             nsAutoCString extraHeaders(aExtraHeaders);
    1445             :             while (true)
    1446             :             {
    1447           0 :                 crlf = extraHeaders.Find("\r\n", true);
    1448           0 :                 if (crlf == -1)
    1449           0 :                     break;
    1450           0 :                 extraHeaders.Mid(oneHeader, 0, crlf);
    1451           0 :                 extraHeaders.Cut(0, crlf + 2);
    1452           0 :                 colon = oneHeader.Find(":");
    1453           0 :                 if (colon == -1)
    1454           0 :                     break; // Should have a colon
    1455           0 :                 oneHeader.Left(headerName, colon);
    1456           0 :                 colon++;
    1457           0 :                 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
    1458           0 :                 headerName.Trim(kWhitespace);
    1459           0 :                 headerValue.Trim(kWhitespace);
    1460             :                 // Add the header (merging if required)
    1461           0 :                 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
    1462           0 :                 if (NS_FAILED(rv))
    1463             :                 {
    1464           0 :                     EndDownload(NS_ERROR_FAILURE);
    1465           0 :                     return NS_ERROR_FAILURE;
    1466             :                 }
    1467             :             }
    1468             :         }
    1469             :     }
    1470           0 :     return SaveChannelInternal(inputChannel, aFile, aCalcFileExt);
    1471             : }
    1472             : 
    1473           0 : nsresult nsWebBrowserPersist::SaveChannelInternal(
    1474             :     nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
    1475             : {
    1476           0 :     NS_ENSURE_ARG_POINTER(aChannel);
    1477           0 :     NS_ENSURE_ARG_POINTER(aFile);
    1478             : 
    1479             :     // The default behaviour of SaveChannelInternal is to download the source
    1480             :     // into a storage stream and upload that to the target. MakeOutputStream
    1481             :     // special-cases a file target and creates a file output stream directly.
    1482             :     // We want to special-case a file source and create a file input stream,
    1483             :     // but we don't need to do this in the case of a file target.
    1484           0 :     nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
    1485           0 :     nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
    1486             : 
    1487           0 :     if (fc && !fu) {
    1488           0 :         nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
    1489           0 :         nsresult rv = NS_MaybeOpenChannelUsingOpen2(aChannel,
    1490           0 :                         getter_AddRefs(fileInputStream));
    1491           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1492           0 :         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
    1493           0 :                                        fileInputStream, BUFFERED_OUTPUT_SIZE);
    1494           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1495           0 :         nsAutoCString contentType;
    1496           0 :         aChannel->GetContentType(contentType);
    1497           0 :         return StartUpload(bufferedInputStream, aFile, contentType);
    1498             :     }
    1499             : 
    1500             :     // Read from the input channel
    1501           0 :     nsresult rv = NS_MaybeOpenChannelUsingAsyncOpen2(aChannel, this);
    1502           0 :     if (rv == NS_ERROR_NO_CONTENT)
    1503             :     {
    1504             :         // Assume this is a protocol such as mailto: which does not feed out
    1505             :         // data and just ignore it.
    1506           0 :         return NS_SUCCESS_DONT_FIXUP;
    1507             :     }
    1508             : 
    1509           0 :     if (NS_FAILED(rv))
    1510             :     {
    1511             :         // Opening failed, but do we care?
    1512           0 :         if (mPersistFlags & PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS)
    1513             :         {
    1514           0 :             SendErrorStatusChange(true, rv, aChannel, aFile);
    1515           0 :             EndDownload(NS_ERROR_FAILURE);
    1516           0 :             return NS_ERROR_FAILURE;
    1517             :         }
    1518           0 :         return NS_SUCCESS_DONT_FIXUP;
    1519             :     }
    1520             : 
    1521             :     // Add the output transport to the output map with the channel as the key
    1522           0 :     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aChannel);
    1523           0 :     mOutputMap.Put(keyPtr, new OutputData(aFile, mURI, aCalcFileExt));
    1524             : 
    1525           0 :     return NS_OK;
    1526             : }
    1527             : 
    1528             : nsresult
    1529           0 : nsWebBrowserPersist::GetExtensionForContentType(const char16_t *aContentType, char16_t **aExt)
    1530             : {
    1531           0 :     NS_ENSURE_ARG_POINTER(aContentType);
    1532           0 :     NS_ENSURE_ARG_POINTER(aExt);
    1533             : 
    1534           0 :     *aExt = nullptr;
    1535             : 
    1536             :     nsresult rv;
    1537           0 :     if (!mMIMEService)
    1538             :     {
    1539           0 :         mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
    1540           0 :         NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
    1541             :     }
    1542             : 
    1543           0 :     nsAutoCString contentType;
    1544           0 :     contentType.AssignWithConversion(aContentType);
    1545           0 :     nsAutoCString ext;
    1546           0 :     rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
    1547           0 :     if (NS_SUCCEEDED(rv))
    1548             :     {
    1549           0 :         *aExt = UTF8ToNewUnicode(ext);
    1550           0 :         NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
    1551           0 :         return NS_OK;
    1552             :     }
    1553             : 
    1554           0 :     return NS_ERROR_FAILURE;
    1555             : }
    1556             : 
    1557             : nsresult
    1558           0 : nsWebBrowserPersist::SaveDocumentDeferred(mozilla::UniquePtr<WalkData>&& aData)
    1559             : {
    1560             :     nsresult rv =
    1561           0 :         SaveDocumentInternal(aData->mDocument, aData->mFile, aData->mDataPath);
    1562           0 :     if (NS_FAILED(rv)) {
    1563           0 :         SendErrorStatusChange(true, rv, nullptr, mURI);
    1564           0 :         EndDownload(rv);
    1565             :     }
    1566           0 :     return rv;
    1567             : }
    1568             : 
    1569           0 : nsresult nsWebBrowserPersist::SaveDocumentInternal(
    1570             :     nsIWebBrowserPersistDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
    1571             : {
    1572           0 :     mURI = nullptr;
    1573           0 :     NS_ENSURE_ARG_POINTER(aDocument);
    1574           0 :     NS_ENSURE_ARG_POINTER(aFile);
    1575             : 
    1576           0 :     nsresult rv = aDocument->SetPersistFlags(mPersistFlags);
    1577           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1578             : 
    1579           0 :     rv = aDocument->GetIsPrivate(&mIsPrivate);
    1580           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1581             : 
    1582             :     // See if we can get the local file representation of this URI
    1583           0 :     nsCOMPtr<nsIFile> localFile;
    1584           0 :     rv = GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
    1585             : 
    1586           0 :     nsCOMPtr<nsIFile> localDataPath;
    1587           0 :     if (NS_SUCCEEDED(rv) && aDataPath)
    1588             :     {
    1589             :         // See if we can get the local file representation of this URI
    1590           0 :         rv = GetLocalFileFromURI(aDataPath, getter_AddRefs(localDataPath));
    1591           0 :         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    1592             :     }
    1593             : 
    1594             :     // Persist the main document
    1595           0 :     rv = aDocument->GetCharacterSet(mCurrentCharset);
    1596           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1597           0 :     nsAutoCString uriSpec;
    1598           0 :     rv = aDocument->GetDocumentURI(uriSpec);
    1599           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1600           0 :     rv = NS_NewURI(getter_AddRefs(mURI), uriSpec, mCurrentCharset.get());
    1601           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1602           0 :     rv = aDocument->GetBaseURI(uriSpec);
    1603           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1604           0 :     rv = NS_NewURI(getter_AddRefs(mCurrentBaseURI), uriSpec,
    1605           0 :                    mCurrentCharset.get());
    1606           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1607             : 
    1608             :     // Does the caller want to fixup the referenced URIs and save those too?
    1609           0 :     if (aDataPath)
    1610             :     {
    1611             :         // Basic steps are these.
    1612             :         //
    1613             :         // 1. Iterate through the document (and subdocuments) building a list
    1614             :         //    of unique URIs.
    1615             :         // 2. For each URI create an OutputData entry and open a channel to save
    1616             :         //    it. As each URI is saved, discover the mime type and fix up the
    1617             :         //    local filename with the correct extension.
    1618             :         // 3. Store the document in a list and wait for URI persistence to finish
    1619             :         // 4. After URI persistence completes save the list of documents,
    1620             :         //    fixing it up as it goes out to file.
    1621             : 
    1622           0 :         mCurrentDataPathIsRelative = false;
    1623           0 :         mCurrentDataPath = aDataPath;
    1624           0 :         mCurrentRelativePathToData = "";
    1625           0 :         mCurrentThingsToPersist = 0;
    1626           0 :         mTargetBaseURI = aFile;
    1627             : 
    1628             :         // Determine if the specified data path is relative to the
    1629             :         // specified file, (e.g. c:\docs\htmldata is relative to
    1630             :         // c:\docs\myfile.htm, but not to d:\foo\data.
    1631             : 
    1632             :         // Starting with the data dir work back through its parents
    1633             :         // checking if one of them matches the base directory.
    1634             : 
    1635           0 :         if (localDataPath && localFile)
    1636             :         {
    1637           0 :             nsCOMPtr<nsIFile> baseDir;
    1638           0 :             localFile->GetParent(getter_AddRefs(baseDir));
    1639             : 
    1640           0 :             nsAutoCString relativePathToData;
    1641           0 :             nsCOMPtr<nsIFile> dataDirParent;
    1642           0 :             dataDirParent = localDataPath;
    1643           0 :             while (dataDirParent)
    1644             :             {
    1645           0 :                 bool sameDir = false;
    1646           0 :                 dataDirParent->Equals(baseDir, &sameDir);
    1647           0 :                 if (sameDir)
    1648             :                 {
    1649           0 :                     mCurrentRelativePathToData = relativePathToData;
    1650           0 :                     mCurrentDataPathIsRelative = true;
    1651           0 :                     break;
    1652             :                 }
    1653             : 
    1654           0 :                 nsAutoString dirName;
    1655           0 :                 dataDirParent->GetLeafName(dirName);
    1656             : 
    1657           0 :                 nsAutoCString newRelativePathToData;
    1658           0 :                 newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
    1659           0 :                                       + NS_LITERAL_CSTRING("/")
    1660           0 :                                       + relativePathToData;
    1661           0 :                 relativePathToData = newRelativePathToData;
    1662             : 
    1663           0 :                 nsCOMPtr<nsIFile> newDataDirParent;
    1664           0 :                 rv = dataDirParent->GetParent(getter_AddRefs(newDataDirParent));
    1665           0 :                 dataDirParent = newDataDirParent;
    1666             :             }
    1667             :         }
    1668             :         else
    1669             :         {
    1670             :             // generate a relative path if possible
    1671           0 :             nsCOMPtr<nsIURL> pathToBaseURL(do_QueryInterface(aFile));
    1672           0 :             if (pathToBaseURL)
    1673             :             {
    1674           0 :                 nsAutoCString relativePath;  // nsACString
    1675           0 :                 if (NS_SUCCEEDED(pathToBaseURL->GetRelativeSpec(aDataPath, relativePath)))
    1676             :                 {
    1677           0 :                     mCurrentDataPathIsRelative = true;
    1678           0 :                     mCurrentRelativePathToData = relativePath;
    1679             :                 }
    1680             :             }
    1681             :         }
    1682             : 
    1683             :         // Store the document in a list so when URI persistence is done and the
    1684             :         // filenames of saved URIs are known, the documents can be fixed up and
    1685             :         // saved
    1686             : 
    1687           0 :         auto *docData = new DocData;
    1688           0 :         docData->mBaseURI = mCurrentBaseURI;
    1689           0 :         docData->mCharset = mCurrentCharset;
    1690           0 :         docData->mDocument = aDocument;
    1691           0 :         docData->mFile = aFile;
    1692           0 :         mDocList.AppendElement(docData);
    1693             : 
    1694             :         // Walk the DOM gathering a list of externally referenced URIs in the uri map
    1695             :         nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visit =
    1696           0 :             new OnWalk(this, aFile, localDataPath);
    1697           0 :         return aDocument->ReadResources(visit);
    1698             :     }
    1699             :     else
    1700             :     {
    1701           0 :         auto *docData = new DocData;
    1702           0 :         docData->mBaseURI = mCurrentBaseURI;
    1703           0 :         docData->mCharset = mCurrentCharset;
    1704           0 :         docData->mDocument = aDocument;
    1705           0 :         docData->mFile = aFile;
    1706           0 :         mDocList.AppendElement(docData);
    1707             : 
    1708             :         // Not walking DOMs, so go directly to serialization.
    1709           0 :         SerializeNextFile();
    1710           0 :         return NS_OK;
    1711             :     }
    1712             : }
    1713             : 
    1714             : NS_IMETHODIMP
    1715           0 : nsWebBrowserPersist::OnWalk::VisitResource(nsIWebBrowserPersistDocument* aDoc,
    1716             :                                            const nsACString& aURI)
    1717             : {
    1718           0 :     return mParent->StoreURI(nsAutoCString(aURI).get());
    1719             : }
    1720             : 
    1721             : NS_IMETHODIMP
    1722           0 : nsWebBrowserPersist::OnWalk::VisitDocument(nsIWebBrowserPersistDocument* aDoc,
    1723             :                                              nsIWebBrowserPersistDocument* aSubDoc)
    1724             : {
    1725           0 :     URIData* data = nullptr;
    1726           0 :     nsAutoCString uriSpec;
    1727           0 :     nsresult rv = aSubDoc->GetDocumentURI(uriSpec);
    1728           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1729           0 :     rv = mParent->StoreURI(uriSpec.get(), false, &data);
    1730           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1731           0 :     if (!data) {
    1732             :         // If the URI scheme isn't persistable, then don't persist.
    1733           0 :         return NS_OK;
    1734             :     }
    1735           0 :     data->mIsSubFrame = true;
    1736           0 :     return mParent->SaveSubframeContent(aSubDoc, uriSpec, data);
    1737             : }
    1738             : 
    1739             : 
    1740             : NS_IMETHODIMP
    1741           0 : nsWebBrowserPersist::OnWalk::EndVisit(nsIWebBrowserPersistDocument* aDoc,
    1742             :                                       nsresult aStatus)
    1743             : {
    1744           0 :     if (NS_FAILED(aStatus)) {
    1745           0 :         mParent->SendErrorStatusChange(true, aStatus, nullptr, mFile);
    1746           0 :         mParent->EndDownload(aStatus);
    1747           0 :         return aStatus;
    1748             :     }
    1749           0 :     mParent->FinishSaveDocumentInternal(mFile, mDataPath);
    1750           0 :     return NS_OK;
    1751             : }
    1752             : 
    1753             : void
    1754           0 : nsWebBrowserPersist::FinishSaveDocumentInternal(nsIURI* aFile,
    1755             :                                                 nsIFile* aDataPath)
    1756             : {
    1757             :     // If there are things to persist, create a directory to hold them
    1758           0 :     if (mCurrentThingsToPersist > 0) {
    1759           0 :         if (aDataPath) {
    1760           0 :             bool exists = false;
    1761           0 :             bool haveDir = false;
    1762             : 
    1763           0 :             aDataPath->Exists(&exists);
    1764           0 :             if (exists) {
    1765           0 :                 aDataPath->IsDirectory(&haveDir);
    1766             :             }
    1767           0 :             if (!haveDir) {
    1768             :                 nsresult rv =
    1769           0 :                     aDataPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
    1770           0 :                 if (NS_SUCCEEDED(rv)) {
    1771           0 :                     haveDir = true;
    1772             :                 } else {
    1773           0 :                     SendErrorStatusChange(false, rv, nullptr, aFile);
    1774             :                 }
    1775             :             }
    1776           0 :             if (!haveDir) {
    1777           0 :                 EndDownload(NS_ERROR_FAILURE);
    1778           0 :                 return;
    1779             :             }
    1780           0 :             if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE) {
    1781             :                 // Add to list of things to delete later if all goes wrong
    1782           0 :                 auto *cleanupData = new CleanupData;
    1783           0 :                 cleanupData->mFile = aDataPath;
    1784           0 :                 cleanupData->mIsDirectory = true;
    1785           0 :                 mCleanupList.AppendElement(cleanupData);
    1786             :             }
    1787             :         }
    1788             :     }
    1789             : 
    1790           0 :     if (mWalkStack.Length() > 0) {
    1791           0 :         mozilla::UniquePtr<WalkData> toWalk;
    1792           0 :         mWalkStack.LastElement().swap(toWalk);
    1793           0 :         mWalkStack.TruncateLength(mWalkStack.Length() - 1);
    1794             :         // Bounce this off the event loop to avoid stack overflow.
    1795             :         typedef StoreCopyPassByRRef<decltype(toWalk)> WalkStorage;
    1796           0 :         auto saveMethod = &nsWebBrowserPersist::SaveDocumentDeferred;
    1797           0 :         nsCOMPtr<nsIRunnable> saveLater = NewRunnableMethod<WalkStorage>(
    1798             :           "nsWebBrowserPersist::FinishSaveDocumentInternal",
    1799             :           this,
    1800             :           saveMethod,
    1801           0 :           mozilla::Move(toWalk));
    1802           0 :         NS_DispatchToCurrentThread(saveLater);
    1803             :     } else {
    1804             :         // Done walking DOMs; on to the serialization phase.
    1805           0 :         SerializeNextFile();
    1806             :     }
    1807             : }
    1808             : 
    1809           0 : void nsWebBrowserPersist::Cleanup()
    1810             : {
    1811           0 :     mURIMap.Clear();
    1812           0 :     for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
    1813           0 :         nsCOMPtr<nsIChannel> channel = do_QueryInterface(iter.Key());
    1814           0 :         if (channel) {
    1815           0 :             channel->Cancel(NS_BINDING_ABORTED);
    1816             :         }
    1817             :     }
    1818           0 :     mOutputMap.Clear();
    1819             : 
    1820           0 :     for (auto iter = mUploadList.Iter(); !iter.Done(); iter.Next()) {
    1821           0 :         nsCOMPtr<nsIChannel> channel = do_QueryInterface(iter.Key());
    1822           0 :         if (channel) {
    1823           0 :             channel->Cancel(NS_BINDING_ABORTED);
    1824             :         }
    1825             :     }
    1826           0 :     mUploadList.Clear();
    1827             : 
    1828             :     uint32_t i;
    1829           0 :     for (i = 0; i < mDocList.Length(); i++) {
    1830           0 :         DocData *docData = mDocList.ElementAt(i);
    1831           0 :         delete docData;
    1832             :     }
    1833           0 :     mDocList.Clear();
    1834             : 
    1835           0 :     for (i = 0; i < mCleanupList.Length(); i++) {
    1836           0 :         CleanupData *cleanupData = mCleanupList.ElementAt(i);
    1837           0 :         delete cleanupData;
    1838             :     }
    1839           0 :     mCleanupList.Clear();
    1840             : 
    1841           0 :     mFilenameList.Clear();
    1842           0 : }
    1843             : 
    1844           0 : void nsWebBrowserPersist::CleanupLocalFiles()
    1845             : {
    1846             :     // Two passes, the first pass cleans up files, the second pass tests
    1847             :     // for and then deletes empty directories. Directories that are not
    1848             :     // empty after the first pass must contain files from something else
    1849             :     // and are not deleted.
    1850             :     int pass;
    1851           0 :     for (pass = 0; pass < 2; pass++)
    1852             :     {
    1853             :         uint32_t i;
    1854           0 :         for (i = 0; i < mCleanupList.Length(); i++)
    1855             :         {
    1856           0 :             CleanupData *cleanupData = mCleanupList.ElementAt(i);
    1857           0 :             nsCOMPtr<nsIFile> file = cleanupData->mFile;
    1858             : 
    1859             :             // Test if the dir / file exists (something in an earlier loop
    1860             :             // may have already removed it)
    1861           0 :             bool exists = false;
    1862           0 :             file->Exists(&exists);
    1863           0 :             if (!exists)
    1864           0 :                 continue;
    1865             : 
    1866             :             // Test if the file has changed in between creation and deletion
    1867             :             // in some way that means it should be ignored
    1868           0 :             bool isDirectory = false;
    1869           0 :             file->IsDirectory(&isDirectory);
    1870           0 :             if (isDirectory != cleanupData->mIsDirectory)
    1871           0 :                 continue; // A file has become a dir or vice versa !
    1872             : 
    1873           0 :             if (pass == 0 && !isDirectory)
    1874             :             {
    1875           0 :                 file->Remove(false);
    1876             :             }
    1877           0 :             else if (pass == 1 && isDirectory) // Directory
    1878             :             {
    1879             :                 // Directories are more complicated. Enumerate through
    1880             :                 // children looking for files. Any files created by the
    1881             :                 // persist object would have been deleted by the first
    1882             :                 // pass so if there are any there at this stage, the dir
    1883             :                 // cannot be deleted because it has someone else's files
    1884             :                 // in it. Empty child dirs are deleted but they must be
    1885             :                 // recursed through to ensure they are actually empty.
    1886             : 
    1887           0 :                 bool isEmptyDirectory = true;
    1888           0 :                 nsCOMArray<nsISimpleEnumerator> dirStack;
    1889           0 :                 int32_t stackSize = 0;
    1890             : 
    1891             :                 // Push the top level enum onto the stack
    1892           0 :                 nsCOMPtr<nsISimpleEnumerator> pos;
    1893           0 :                 if (NS_SUCCEEDED(file->GetDirectoryEntries(getter_AddRefs(pos))))
    1894           0 :                     dirStack.AppendObject(pos);
    1895             : 
    1896           0 :                 while (isEmptyDirectory && (stackSize = dirStack.Count()))
    1897             :                 {
    1898             :                     // Pop the last element
    1899           0 :                     nsCOMPtr<nsISimpleEnumerator> curPos;
    1900           0 :                     curPos = dirStack[stackSize-1];
    1901           0 :                     dirStack.RemoveObjectAt(stackSize - 1);
    1902             : 
    1903             :                     // Test if the enumerator has any more files in it
    1904           0 :                     bool hasMoreElements = false;
    1905           0 :                     curPos->HasMoreElements(&hasMoreElements);
    1906           0 :                     if (!hasMoreElements)
    1907             :                     {
    1908           0 :                         continue;
    1909             :                     }
    1910             : 
    1911             :                     // Child files automatically make this code drop out,
    1912             :                     // while child dirs keep the loop going.
    1913           0 :                     nsCOMPtr<nsISupports> child;
    1914           0 :                     curPos->GetNext(getter_AddRefs(child));
    1915           0 :                     NS_ASSERTION(child, "No child element, but hasMoreElements says otherwise");
    1916           0 :                     if (!child)
    1917           0 :                         continue;
    1918           0 :                     nsCOMPtr<nsIFile> childAsFile = do_QueryInterface(child);
    1919           0 :                     NS_ASSERTION(childAsFile, "This should be a file but isn't");
    1920             : 
    1921           0 :                     bool childIsSymlink = false;
    1922           0 :                     childAsFile->IsSymlink(&childIsSymlink);
    1923           0 :                     bool childIsDir = false;
    1924           0 :                     childAsFile->IsDirectory(&childIsDir);
    1925           0 :                     if (!childIsDir || childIsSymlink)
    1926             :                     {
    1927             :                         // Some kind of file or symlink which means dir
    1928             :                         // is not empty so just drop out.
    1929           0 :                         isEmptyDirectory = false;
    1930           0 :                         break;
    1931             :                     }
    1932             :                     // Push parent enumerator followed by child enumerator
    1933           0 :                     nsCOMPtr<nsISimpleEnumerator> childPos;
    1934           0 :                     childAsFile->GetDirectoryEntries(getter_AddRefs(childPos));
    1935           0 :                     dirStack.AppendObject(curPos);
    1936           0 :                     if (childPos)
    1937           0 :                         dirStack.AppendObject(childPos);
    1938             : 
    1939             :                 }
    1940           0 :                 dirStack.Clear();
    1941             : 
    1942             :                 // If after all that walking the dir is deemed empty, delete it
    1943           0 :                 if (isEmptyDirectory)
    1944             :                 {
    1945           0 :                     file->Remove(true);
    1946             :                 }
    1947             :             }
    1948             :         }
    1949             :     }
    1950           0 : }
    1951             : 
    1952             : nsresult
    1953           0 : nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
    1954             : {
    1955           0 :     nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
    1956           0 :     NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
    1957             : 
    1958           0 :     bool nameHasChanged = false;
    1959             :     nsresult rv;
    1960             : 
    1961             :     // Get the old filename
    1962           0 :     nsAutoCString filename;
    1963           0 :     rv = url->GetFileName(filename);
    1964           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    1965           0 :     nsAutoCString directory;
    1966           0 :     rv = url->GetDirectory(directory);
    1967           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    1968             : 
    1969             :     // Split the filename into a base and an extension.
    1970             :     // e.g. "foo.html" becomes "foo" & ".html"
    1971             :     //
    1972             :     // The nsIURL methods GetFileBaseName & GetFileExtension don't
    1973             :     // preserve the dot whereas this code does to save some effort
    1974             :     // later when everything is put back together.
    1975           0 :     int32_t lastDot = filename.RFind(".");
    1976           0 :     nsAutoCString base;
    1977           0 :     nsAutoCString ext;
    1978           0 :     if (lastDot >= 0)
    1979             :     {
    1980           0 :         filename.Mid(base, 0, lastDot);
    1981           0 :         filename.Mid(ext, lastDot, filename.Length() - lastDot); // includes dot
    1982             :     }
    1983             :     else
    1984             :     {
    1985             :         // filename contains no dot
    1986           0 :         base = filename;
    1987             :     }
    1988             : 
    1989             :     // Test if the filename is longer than allowed by the OS
    1990           0 :     int32_t needToChop = filename.Length() - kDefaultMaxFilenameLength;
    1991           0 :     if (needToChop > 0)
    1992             :     {
    1993             :         // Truncate the base first and then the ext if necessary
    1994           0 :         if (base.Length() > (uint32_t) needToChop)
    1995             :         {
    1996           0 :             base.Truncate(base.Length() - needToChop);
    1997             :         }
    1998             :         else
    1999             :         {
    2000           0 :             needToChop -= base.Length() - 1;
    2001           0 :             base.Truncate(1);
    2002           0 :             if (ext.Length() > (uint32_t) needToChop)
    2003             :             {
    2004           0 :                 ext.Truncate(ext.Length() - needToChop);
    2005             :             }
    2006             :             else
    2007             :             {
    2008           0 :                 ext.Truncate(0);
    2009             :             }
    2010             :             // If kDefaultMaxFilenameLength were 1 we'd be in trouble here,
    2011             :             // but that won't happen because it will be set to a sensible
    2012             :             // value.
    2013             :         }
    2014             : 
    2015           0 :         filename.Assign(base);
    2016           0 :         filename.Append(ext);
    2017           0 :         nameHasChanged = true;
    2018             :     }
    2019             : 
    2020             :     // Ensure the filename is unique
    2021             :     // Create a filename if it's empty, or if the filename / datapath is
    2022             :     // already taken by another URI and create an alternate name.
    2023             : 
    2024           0 :     if (base.IsEmpty() || !mFilenameList.IsEmpty())
    2025             :     {
    2026           0 :         nsAutoCString tmpPath;
    2027           0 :         nsAutoCString tmpBase;
    2028           0 :         uint32_t duplicateCounter = 1;
    2029             :         while (true)
    2030             :         {
    2031             :             // Make a file name,
    2032             :             // Foo become foo_001, foo_002, etc.
    2033             :             // Empty files become _001, _002 etc.
    2034             : 
    2035           0 :             if (base.IsEmpty() || duplicateCounter > 1)
    2036             :             {
    2037           0 :                 SmprintfPointer tmp = mozilla::Smprintf("_%03d", duplicateCounter);
    2038           0 :                 NS_ENSURE_TRUE(tmp, NS_ERROR_OUT_OF_MEMORY);
    2039           0 :                 if (filename.Length() < kDefaultMaxFilenameLength - 4)
    2040             :                 {
    2041           0 :                     tmpBase = base;
    2042             :                 }
    2043             :                 else
    2044             :                 {
    2045           0 :                     base.Mid(tmpBase, 0, base.Length() - 4);
    2046             :                 }
    2047           0 :                 tmpBase.Append(tmp.get());
    2048             :             }
    2049             :             else
    2050             :             {
    2051           0 :                 tmpBase = base;
    2052             :             }
    2053             : 
    2054           0 :             tmpPath.Assign(directory);
    2055           0 :             tmpPath.Append(tmpBase);
    2056           0 :             tmpPath.Append(ext);
    2057             : 
    2058             :             // Test if the name is a duplicate
    2059           0 :             if (!mFilenameList.Contains(tmpPath))
    2060             :             {
    2061           0 :                 if (!base.Equals(tmpBase))
    2062             :                 {
    2063           0 :                     filename.Assign(tmpBase);
    2064           0 :                     filename.Append(ext);
    2065           0 :                     nameHasChanged = true;
    2066             :                 }
    2067           0 :                 break;
    2068             :             }
    2069           0 :             duplicateCounter++;
    2070           0 :         }
    2071             :     }
    2072             : 
    2073             :     // Add name to list of those already used
    2074           0 :     nsAutoCString newFilepath(directory);
    2075           0 :     newFilepath.Append(filename);
    2076           0 :     mFilenameList.AppendElement(newFilepath);
    2077             : 
    2078             :     // Update the uri accordingly if the filename actually changed
    2079           0 :     if (nameHasChanged)
    2080             :     {
    2081             :         // Final sanity test
    2082           0 :         if (filename.Length() > kDefaultMaxFilenameLength)
    2083             :         {
    2084           0 :             NS_WARNING("Filename wasn't truncated less than the max file length - how can that be?");
    2085           0 :             return NS_ERROR_FAILURE;
    2086             :         }
    2087             : 
    2088           0 :         nsCOMPtr<nsIFile> localFile;
    2089           0 :         GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
    2090             : 
    2091           0 :         if (localFile)
    2092             :         {
    2093           0 :             nsAutoString filenameAsUnichar;
    2094           0 :             filenameAsUnichar.AssignWithConversion(filename.get());
    2095           0 :             localFile->SetLeafName(filenameAsUnichar);
    2096             : 
    2097             :             // Resync the URI with the file after the extension has been appended
    2098             :             nsresult rv;
    2099           0 :             nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
    2100           0 :             NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2101           0 :             fileURL->SetFile(localFile);  // this should recalculate uri
    2102             :         }
    2103             :         else
    2104             :         {
    2105           0 :             url->SetFileName(filename);
    2106             :         }
    2107             :     }
    2108             : 
    2109           0 :     return NS_OK;
    2110             : }
    2111             : 
    2112             : 
    2113             : nsresult
    2114           0 : nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
    2115             : {
    2116             :     // Try to get filename from the URI.
    2117           0 :     nsAutoString fileName;
    2118             : 
    2119             :     // Get a suggested file name from the URL but strip it of characters
    2120             :     // likely to cause the name to be illegal.
    2121             : 
    2122           0 :     nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
    2123           0 :     if (url)
    2124             :     {
    2125           0 :         nsAutoCString nameFromURL;
    2126           0 :         url->GetFileName(nameFromURL);
    2127           0 :         if (mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES)
    2128             :         {
    2129           0 :             fileName.AssignWithConversion(NS_UnescapeURL(nameFromURL).BeginReading());
    2130           0 :             aFilename = fileName;
    2131           0 :             return NS_OK;
    2132             :         }
    2133           0 :         if (!nameFromURL.IsEmpty())
    2134             :         {
    2135             :             // Unescape the file name (GetFileName escapes it)
    2136           0 :             NS_UnescapeURL(nameFromURL);
    2137           0 :             uint32_t nameLength = 0;
    2138           0 :             const char *p = nameFromURL.get();
    2139           0 :             for (;*p && *p != ';' && *p != '?' && *p != '#' && *p != '.'
    2140             :                  ;p++)
    2141             :             {
    2142           0 :                 if (nsCRT::IsAsciiAlpha(*p) || nsCRT::IsAsciiDigit(*p)
    2143           0 :                     || *p == '.' || *p == '-' ||  *p == '_' || (*p == ' '))
    2144             :                 {
    2145           0 :                     fileName.Append(char16_t(*p));
    2146           0 :                     if (++nameLength == kDefaultMaxFilenameLength)
    2147             :                     {
    2148             :                         // Note:
    2149             :                         // There is no point going any further since it will be
    2150             :                         // truncated in CalculateUniqueFilename anyway.
    2151             :                         // More importantly, certain implementations of
    2152             :                         // nsIFile (e.g. the Mac impl) might truncate
    2153             :                         // names in undesirable ways, such as truncating from
    2154             :                         // the middle, inserting ellipsis and so on.
    2155           0 :                         break;
    2156             :                     }
    2157             :                 }
    2158             :             }
    2159             :         }
    2160             :     }
    2161             : 
    2162             :     // Empty filenames can confuse the local file object later
    2163             :     // when it attempts to set the leaf name in CalculateUniqueFilename
    2164             :     // for duplicates and ends up replacing the parent dir. To avoid
    2165             :     // the problem, all filenames are made at least one character long.
    2166           0 :     if (fileName.IsEmpty())
    2167             :     {
    2168           0 :         fileName.Append(char16_t('a')); // 'a' is for arbitrary
    2169             :     }
    2170             : 
    2171           0 :     aFilename = fileName;
    2172           0 :     return NS_OK;
    2173             : }
    2174             : 
    2175             : 
    2176             : nsresult
    2177           0 : nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
    2178             : {
    2179             :     nsresult rv;
    2180             : 
    2181           0 :     if (!mMIMEService)
    2182             :     {
    2183           0 :         mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
    2184           0 :         NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
    2185             :     }
    2186             : 
    2187           0 :     nsAutoCString contentType;
    2188             : 
    2189             :     // Get the content type from the channel
    2190           0 :     aChannel->GetContentType(contentType);
    2191             : 
    2192             :     // Get the content type from the MIME service
    2193           0 :     if (contentType.IsEmpty())
    2194             :     {
    2195           0 :         nsCOMPtr<nsIURI> uri;
    2196           0 :         aChannel->GetOriginalURI(getter_AddRefs(uri));
    2197           0 :         mMIMEService->GetTypeFromURI(uri, contentType);
    2198             :     }
    2199             : 
    2200             :     // Append the extension onto the file
    2201           0 :     if (!contentType.IsEmpty())
    2202             :     {
    2203           0 :         nsCOMPtr<nsIMIMEInfo> mimeInfo;
    2204           0 :         mMIMEService->GetFromTypeAndExtension(
    2205           0 :             contentType, EmptyCString(), getter_AddRefs(mimeInfo));
    2206             : 
    2207           0 :         nsCOMPtr<nsIFile> localFile;
    2208           0 :         GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
    2209             : 
    2210           0 :         if (mimeInfo)
    2211             :         {
    2212           0 :             nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
    2213           0 :             NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
    2214             : 
    2215           0 :             nsAutoCString newFileName;
    2216           0 :             url->GetFileName(newFileName);
    2217             : 
    2218             :             // Test if the current extension is current for the mime type
    2219           0 :             bool hasExtension = false;
    2220           0 :             int32_t ext = newFileName.RFind(".");
    2221           0 :             if (ext != -1)
    2222             :             {
    2223           0 :                 mimeInfo->ExtensionExists(Substring(newFileName, ext + 1), &hasExtension);
    2224             :             }
    2225             : 
    2226             :             // Append the mime file extension
    2227           0 :             nsAutoCString fileExt;
    2228           0 :             if (!hasExtension)
    2229             :             {
    2230             :                 // Test if previous extension is acceptable
    2231           0 :                 nsCOMPtr<nsIURL> oldurl(do_QueryInterface(aOriginalURIWithExtension));
    2232           0 :                 NS_ENSURE_TRUE(oldurl, NS_ERROR_FAILURE);
    2233           0 :                 oldurl->GetFileExtension(fileExt);
    2234           0 :                 bool useOldExt = false;
    2235           0 :                 if (!fileExt.IsEmpty())
    2236             :                 {
    2237           0 :                     mimeInfo->ExtensionExists(fileExt, &useOldExt);
    2238             :                 }
    2239             : 
    2240             :                 // can't use old extension so use primary extension
    2241           0 :                 if (!useOldExt)
    2242             :                 {
    2243           0 :                     mimeInfo->GetPrimaryExtension(fileExt);
    2244             :                 }
    2245             : 
    2246           0 :                 if (!fileExt.IsEmpty())
    2247             :                 {
    2248           0 :                     uint32_t newLength = newFileName.Length() + fileExt.Length() + 1;
    2249           0 :                     if (newLength > kDefaultMaxFilenameLength)
    2250             :                     {
    2251           0 :                         if (fileExt.Length() > kDefaultMaxFilenameLength/2)
    2252           0 :                             fileExt.Truncate(kDefaultMaxFilenameLength/2);
    2253             : 
    2254             :                         uint32_t diff = kDefaultMaxFilenameLength - 1 -
    2255           0 :                                         fileExt.Length();
    2256           0 :                         if (newFileName.Length() > diff)
    2257           0 :                             newFileName.Truncate(diff);
    2258             :                     }
    2259           0 :                     newFileName.Append('.');
    2260           0 :                     newFileName.Append(fileExt);
    2261             :                 }
    2262             : 
    2263           0 :                 if (localFile)
    2264             :                 {
    2265           0 :                     localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
    2266             : 
    2267             :                     // Resync the URI with the file after the extension has been appended
    2268           0 :                     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
    2269           0 :                     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2270           0 :                     fileURL->SetFile(localFile);  // this should recalculate uri
    2271             :                 }
    2272             :                 else
    2273             :                 {
    2274           0 :                     url->SetFileName(newFileName);
    2275             :                 }
    2276             :             }
    2277             : 
    2278             :         }
    2279             :     }
    2280             : 
    2281           0 :     return NS_OK;
    2282             : }
    2283             : 
    2284             : nsresult
    2285           0 : nsWebBrowserPersist::MakeOutputStream(
    2286             :     nsIURI *aURI, nsIOutputStream **aOutputStream)
    2287             : {
    2288             :     nsresult rv;
    2289             : 
    2290           0 :     nsCOMPtr<nsIFile> localFile;
    2291           0 :     GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
    2292           0 :     if (localFile)
    2293           0 :         rv = MakeOutputStreamFromFile(localFile, aOutputStream);
    2294             :     else
    2295           0 :         rv = MakeOutputStreamFromURI(aURI, aOutputStream);
    2296             : 
    2297           0 :     return rv;
    2298             : }
    2299             : 
    2300             : nsresult
    2301           0 : nsWebBrowserPersist::MakeOutputStreamFromFile(
    2302             :     nsIFile *aFile, nsIOutputStream **aOutputStream)
    2303             : {
    2304           0 :     nsresult rv = NS_OK;
    2305             : 
    2306             :     nsCOMPtr<nsIFileOutputStream> fileOutputStream =
    2307           0 :         do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
    2308           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2309             : 
    2310             :     // XXX brade:  get the right flags here!
    2311           0 :     int32_t ioFlags = -1;
    2312           0 :     if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE)
    2313           0 :       ioFlags = PR_APPEND | PR_CREATE_FILE | PR_WRONLY;
    2314           0 :     rv = fileOutputStream->Init(aFile, ioFlags, -1, 0);
    2315           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2316             : 
    2317           0 :     *aOutputStream = NS_BufferOutputStream(fileOutputStream,
    2318           0 :                                            BUFFERED_OUTPUT_SIZE).take();
    2319             : 
    2320           0 :     if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
    2321             :     {
    2322             :         // Add to cleanup list in event of failure
    2323           0 :         auto *cleanupData = new CleanupData;
    2324           0 :         if (!cleanupData) {
    2325           0 :           NS_RELEASE(*aOutputStream);
    2326           0 :           return NS_ERROR_OUT_OF_MEMORY;
    2327             :         }
    2328           0 :         cleanupData->mFile = aFile;
    2329           0 :         cleanupData->mIsDirectory = false;
    2330           0 :         mCleanupList.AppendElement(cleanupData);
    2331             :     }
    2332             : 
    2333           0 :     return NS_OK;
    2334             : }
    2335             : 
    2336             : nsresult
    2337           0 : nsWebBrowserPersist::MakeOutputStreamFromURI(
    2338             :     nsIURI *aURI, nsIOutputStream  **aOutputStream)
    2339             : {
    2340           0 :     uint32_t segsize = 8192;
    2341           0 :     uint32_t maxsize = uint32_t(-1);
    2342           0 :     nsCOMPtr<nsIStorageStream> storStream;
    2343           0 :     nsresult rv = NS_NewStorageStream(segsize, maxsize, getter_AddRefs(storStream));
    2344           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2345             : 
    2346           0 :     NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
    2347           0 :     return NS_OK;
    2348             : }
    2349             : 
    2350             : void
    2351           0 : nsWebBrowserPersist::FinishDownload()
    2352             : {
    2353           0 :     EndDownload(NS_OK);
    2354           0 : }
    2355             : 
    2356             : void
    2357           0 : nsWebBrowserPersist::EndDownload(nsresult aResult)
    2358             : {
    2359             :     // Store the error code in the result if it is an error
    2360           0 :     if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(aResult))
    2361             :     {
    2362           0 :         mPersistResult = aResult;
    2363             :     }
    2364             : 
    2365             :     // mCompleted needs to be set before issuing the stop notification.
    2366             :     // (Bug 1224437)
    2367           0 :     mCompleted = true;
    2368             :     // State stop notification
    2369           0 :     if (mProgressListener) {
    2370           0 :         mProgressListener->OnStateChange(nullptr, nullptr,
    2371             :             nsIWebProgressListener::STATE_STOP
    2372           0 :             | nsIWebProgressListener::STATE_IS_NETWORK, mPersistResult);
    2373             :     }
    2374             : 
    2375             :     // Do file cleanup if required
    2376           0 :     if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
    2377             :     {
    2378           0 :         CleanupLocalFiles();
    2379             :     }
    2380             : 
    2381             :     // Cleanup the channels
    2382           0 :     Cleanup();
    2383             : 
    2384           0 :     mProgressListener = nullptr;
    2385           0 :     mProgressListener2 = nullptr;
    2386           0 :     mEventSink = nullptr;
    2387           0 : }
    2388             : 
    2389             : nsresult
    2390           0 : nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
    2391             : {
    2392           0 :     NS_ENSURE_ARG_POINTER(aNewChannel);
    2393             : 
    2394             :     // Iterate through existing open channels looking for one with a URI
    2395             :     // matching the one specified.
    2396           0 :     nsCOMPtr<nsIURI> originalURI;
    2397           0 :     aNewChannel->GetOriginalURI(getter_AddRefs(originalURI));
    2398           0 :     nsISupports* matchingKey = nullptr;
    2399           0 :     for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
    2400           0 :         nsISupports* key = iter.Key();
    2401           0 :         nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(key);
    2402           0 :         nsCOMPtr<nsIURI> thisURI;
    2403             : 
    2404           0 :         thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
    2405             : 
    2406             :         // Compare this channel's URI to the one passed in.
    2407           0 :         bool matchingURI = false;
    2408           0 :         thisURI->Equals(originalURI, &matchingURI);
    2409           0 :         if (matchingURI) {
    2410           0 :             matchingKey = key;
    2411           0 :             break;
    2412             :         }
    2413             :     }
    2414             : 
    2415           0 :     if (matchingKey) {
    2416             :         // If a match was found, remove the data entry with the old channel
    2417             :         // key and re-add it with the new channel key.
    2418           0 :         nsAutoPtr<OutputData> outputData;
    2419           0 :         mOutputMap.Remove(matchingKey, &outputData);
    2420           0 :         NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
    2421             : 
    2422             :         // Store data again with new channel unless told to ignore redirects.
    2423           0 :         if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA)) {
    2424           0 :             nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
    2425           0 :             mOutputMap.Put(keyPtr, outputData.forget());
    2426             :         }
    2427             :     }
    2428             : 
    2429           0 :     return NS_OK;
    2430             : }
    2431             : 
    2432             : void
    2433           0 : nsWebBrowserPersist::CalcTotalProgress()
    2434             : {
    2435           0 :     mTotalCurrentProgress = 0;
    2436           0 :     mTotalMaxProgress = 0;
    2437             : 
    2438           0 :     if (mOutputMap.Count() > 0) {
    2439             :         // Total up the progress of each output stream
    2440           0 :         for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
    2441             :             // Only count toward total progress if destination file is local.
    2442           0 :             OutputData* data = iter.UserData();
    2443           0 :             nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(data->mFile);
    2444           0 :             if (fileURL) {
    2445           0 :                 mTotalCurrentProgress += data->mSelfProgress;
    2446           0 :                 mTotalMaxProgress += data->mSelfProgressMax;
    2447             :             }
    2448             :         }
    2449             :     }
    2450             : 
    2451           0 :     if (mUploadList.Count() > 0) {
    2452             :         // Total up the progress of each upload
    2453           0 :         for (auto iter = mUploadList.Iter(); !iter.Done(); iter.Next()) {
    2454           0 :             UploadData* data = iter.UserData();
    2455           0 :             if (data) {
    2456           0 :                 mTotalCurrentProgress += data->mSelfProgress;
    2457           0 :                 mTotalMaxProgress += data->mSelfProgressMax;
    2458             :             }
    2459             :         }
    2460             :     }
    2461             : 
    2462             :     // XXX this code seems pretty bogus and pointless
    2463           0 :     if (mTotalCurrentProgress == 0 && mTotalMaxProgress == 0)
    2464             :     {
    2465             :         // No output streams so we must be complete
    2466           0 :         mTotalCurrentProgress = 10000;
    2467           0 :         mTotalMaxProgress = 10000;
    2468             :     }
    2469           0 : }
    2470             : 
    2471             : nsresult
    2472           0 : nsWebBrowserPersist::StoreURI(
    2473             :     const char *aURI, bool aNeedsPersisting, URIData **aData)
    2474             : {
    2475           0 :     NS_ENSURE_ARG_POINTER(aURI);
    2476             : 
    2477           0 :     nsCOMPtr<nsIURI> uri;
    2478           0 :     nsresult rv = NS_NewURI(getter_AddRefs(uri),
    2479           0 :                             nsDependentCString(aURI),
    2480             :                             mCurrentCharset.get(),
    2481           0 :                             mCurrentBaseURI);
    2482           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2483             : 
    2484           0 :     return StoreURI(uri, aNeedsPersisting, aData);
    2485             : }
    2486             : 
    2487             : nsresult
    2488           0 : nsWebBrowserPersist::StoreURI(
    2489             :     nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
    2490             : {
    2491           0 :     NS_ENSURE_ARG_POINTER(aURI);
    2492           0 :     if (aData)
    2493             :     {
    2494           0 :         *aData = nullptr;
    2495             :     }
    2496             : 
    2497             :     // Test if this URI should be persisted. By default
    2498             :     // we should assume the URI  is persistable.
    2499             :     bool doNotPersistURI;
    2500             :     nsresult rv = NS_URIChainHasFlags(aURI,
    2501             :                                       nsIProtocolHandler::URI_NON_PERSISTABLE,
    2502           0 :                                       &doNotPersistURI);
    2503           0 :     if (NS_FAILED(rv))
    2504             :     {
    2505           0 :         doNotPersistURI = false;
    2506             :     }
    2507             : 
    2508           0 :     if (doNotPersistURI)
    2509             :     {
    2510           0 :         return NS_OK;
    2511             :     }
    2512             : 
    2513           0 :     URIData *data = nullptr;
    2514           0 :     MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
    2515           0 :     if (aData)
    2516             :     {
    2517           0 :         *aData = data;
    2518             :     }
    2519             : 
    2520           0 :     return NS_OK;
    2521             : }
    2522             : 
    2523             : nsresult
    2524           0 : nsWebBrowserPersist::URIData::GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut)
    2525             : {
    2526           0 :     aSpecOut.SetIsVoid(true);
    2527           0 :     if (!mNeedsFixup) {
    2528           0 :         return NS_OK;
    2529             :     }
    2530             :     nsresult rv;
    2531           0 :     nsCOMPtr<nsIURI> fileAsURI;
    2532           0 :     if (mFile) {
    2533           0 :         rv = mFile->Clone(getter_AddRefs(fileAsURI));
    2534           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2535             :     } else {
    2536           0 :         rv = mDataPath->Clone(getter_AddRefs(fileAsURI));
    2537           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2538           0 :         rv = AppendPathToURI(fileAsURI, mFilename);
    2539           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2540             :     }
    2541             : 
    2542             :     // remove username/password if present
    2543           0 :     fileAsURI->SetUserPass(EmptyCString());
    2544             : 
    2545             :     // reset node attribute
    2546             :     // Use relative or absolute links
    2547           0 :     if (mDataPathIsRelative) {
    2548           0 :         bool isEqual = false;
    2549           0 :         if (NS_SUCCEEDED(mRelativeDocumentURI->Equals(targetBaseURI, &isEqual)) && isEqual) {
    2550           0 :             nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
    2551           0 :             if (!url) {
    2552           0 :                 return NS_ERROR_FAILURE;
    2553             :             }
    2554             : 
    2555           0 :             nsAutoCString filename;
    2556           0 :             url->GetFileName(filename);
    2557             : 
    2558           0 :             nsAutoCString rawPathURL(mRelativePathToData);
    2559           0 :             rawPathURL.Append(filename);
    2560             : 
    2561           0 :             rv = NS_EscapeURL(rawPathURL, esc_FilePath, aSpecOut, fallible);
    2562           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2563             :         } else {
    2564           0 :             nsAutoCString rawPathURL;
    2565             : 
    2566           0 :             nsCOMPtr<nsIFile> dataFile;
    2567           0 :             rv = GetLocalFileFromURI(mFile, getter_AddRefs(dataFile));
    2568           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2569             : 
    2570           0 :             nsCOMPtr<nsIFile> docFile;
    2571           0 :             rv = GetLocalFileFromURI(targetBaseURI, getter_AddRefs(docFile));
    2572           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2573             : 
    2574           0 :             nsCOMPtr<nsIFile> parentDir;
    2575           0 :             rv = docFile->GetParent(getter_AddRefs(parentDir));
    2576           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2577             : 
    2578           0 :             rv = dataFile->GetRelativePath(parentDir, rawPathURL);
    2579           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2580             : 
    2581           0 :             rv = NS_EscapeURL(rawPathURL, esc_FilePath, aSpecOut, fallible);
    2582           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2583             :         }
    2584             :     } else {
    2585           0 :         fileAsURI->GetSpec(aSpecOut);
    2586             :     }
    2587           0 :     if (mIsSubFrame) {
    2588           0 :         AppendUTF16toUTF8(mSubFrameExt, aSpecOut);
    2589             :     }
    2590             : 
    2591           0 :     return NS_OK;
    2592             : }
    2593             : 
    2594             : bool
    2595           0 : nsWebBrowserPersist::DocumentEncoderExists(const char *aContentType)
    2596             : {
    2597             :     // Check if there is an encoder for the desired content type.
    2598           0 :     nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
    2599           0 :     contractID.Append(aContentType);
    2600             : 
    2601           0 :     nsCOMPtr<nsIComponentRegistrar> registrar;
    2602           0 :     NS_GetComponentRegistrar(getter_AddRefs(registrar));
    2603           0 :     if (registrar)
    2604             :     {
    2605             :         bool result;
    2606           0 :         nsresult rv = registrar->IsContractIDRegistered(contractID.get(),
    2607           0 :                                                         &result);
    2608           0 :         if (NS_SUCCEEDED(rv) && result)
    2609             :         {
    2610           0 :             return true;
    2611             :         }
    2612             :     }
    2613           0 :     return false;
    2614             : }
    2615             : 
    2616             : nsresult
    2617           0 : nsWebBrowserPersist::SaveSubframeContent(
    2618             :     nsIWebBrowserPersistDocument *aFrameContent,
    2619             :     const nsCString& aURISpec,
    2620             :     URIData *aData)
    2621             : {
    2622           0 :     NS_ENSURE_ARG_POINTER(aData);
    2623             : 
    2624             :     // Extract the content type for the frame's contents.
    2625           0 :     nsAutoCString contentType;
    2626           0 :     nsresult rv = aFrameContent->GetContentType(contentType);
    2627           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2628             : 
    2629           0 :     nsXPIDLString ext;
    2630           0 :     GetExtensionForContentType(NS_ConvertASCIItoUTF16(contentType).get(),
    2631           0 :                                getter_Copies(ext));
    2632             : 
    2633             :     // We must always have an extension so we will try to re-assign
    2634             :     // the original extension if GetExtensionForContentType fails.
    2635           0 :     if (ext.IsEmpty()) {
    2636           0 :         nsCOMPtr<nsIURI> docURI;
    2637           0 :         rv = NS_NewURI(getter_AddRefs(docURI), aURISpec, mCurrentCharset.get());
    2638           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2639             : 
    2640           0 :         nsCOMPtr<nsIURL> url(do_QueryInterface(docURI, &rv));
    2641           0 :         nsAutoCString extension;
    2642           0 :         if (NS_SUCCEEDED(rv)) {
    2643           0 :             url->GetFileExtension(extension);
    2644             :         } else {
    2645           0 :             extension.AssignLiteral("htm");
    2646             :         }
    2647           0 :         aData->mSubFrameExt.Assign(char16_t('.'));
    2648           0 :         AppendUTF8toUTF16(extension, aData->mSubFrameExt);
    2649             :     } else {
    2650           0 :         aData->mSubFrameExt.Assign(char16_t('.'));
    2651           0 :         aData->mSubFrameExt.Append(ext);
    2652             :     }
    2653             : 
    2654           0 :     nsString filenameWithExt = aData->mFilename;
    2655           0 :     filenameWithExt.Append(aData->mSubFrameExt);
    2656             : 
    2657             :     // Work out the path for the subframe
    2658           0 :     nsCOMPtr<nsIURI> frameURI;
    2659           0 :     rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
    2660           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2661           0 :     rv = AppendPathToURI(frameURI, filenameWithExt);
    2662           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2663             : 
    2664             :     // Work out the path for the subframe data
    2665           0 :     nsCOMPtr<nsIURI> frameDataURI;
    2666           0 :     rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
    2667           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2668           0 :     nsAutoString newFrameDataPath(aData->mFilename);
    2669             : 
    2670             :     // Append _data
    2671           0 :     newFrameDataPath.AppendLiteral("_data");
    2672           0 :     rv = AppendPathToURI(frameDataURI, newFrameDataPath);
    2673           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2674             : 
    2675             :     // Make frame document & data path conformant and unique
    2676           0 :     rv = CalculateUniqueFilename(frameURI);
    2677           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2678           0 :     rv = CalculateUniqueFilename(frameDataURI);
    2679           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2680             : 
    2681           0 :     mCurrentThingsToPersist++;
    2682             : 
    2683             :     // We shouldn't use SaveDocumentInternal for the contents
    2684             :     // of frames that are not documents, e.g. images.
    2685           0 :     if (DocumentEncoderExists(contentType.get())) {
    2686           0 :         auto toWalk = mozilla::MakeUnique<WalkData>();
    2687           0 :         toWalk->mDocument = aFrameContent;
    2688           0 :         toWalk->mFile = frameURI;
    2689           0 :         toWalk->mDataPath = frameDataURI;
    2690           0 :         mWalkStack.AppendElement(mozilla::Move(toWalk));
    2691             :     } else {
    2692           0 :         rv = StoreURI(aURISpec.get());
    2693             :     }
    2694           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2695             : 
    2696             :     // Store the updated uri to the frame
    2697           0 :     aData->mFile = frameURI;
    2698           0 :     aData->mSubFrameExt.Truncate(); // we already put this in frameURI
    2699             : 
    2700           0 :     return NS_OK;
    2701             : }
    2702             : 
    2703             : nsresult
    2704           0 : nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
    2705             : {
    2706           0 :     nsresult rv = NS_OK;
    2707           0 :     *aChannel = nullptr;
    2708             : 
    2709           0 :     rv = NS_NewChannel(aChannel,
    2710             :                        aURI,
    2711             :                        nsContentUtils::GetSystemPrincipal(),
    2712             :                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    2713           0 :                        nsIContentPolicy::TYPE_OTHER);
    2714           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2715           0 :     NS_ENSURE_ARG_POINTER(*aChannel);
    2716             : 
    2717           0 :     rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
    2718           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2719           0 :     return NS_OK;
    2720             : }
    2721             : 
    2722             : 
    2723             : // we store the current location as the key (absolutized version of domnode's attribute's value)
    2724             : nsresult
    2725           0 : nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
    2726             :     nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
    2727             : {
    2728           0 :     NS_ENSURE_ARG_POINTER(aURI);
    2729             : 
    2730           0 :     nsAutoCString spec;
    2731           0 :     nsresult rv = aURI->GetSpec(spec);
    2732           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2733             : 
    2734             :     // Create a sensibly named filename for the URI and store in the URI map
    2735             :     URIData *data;
    2736           0 :     if (mURIMap.Get(spec, &data))
    2737             :     {
    2738           0 :         if (aNeedsPersisting)
    2739             :         {
    2740           0 :           data->mNeedsPersisting = true;
    2741             :         }
    2742           0 :         if (aData)
    2743             :         {
    2744           0 :             *aData = data;
    2745             :         }
    2746           0 :         return NS_OK;
    2747             :     }
    2748             : 
    2749             :     // Create a unique file name for the uri
    2750           0 :     nsString filename;
    2751           0 :     rv = MakeFilenameFromURI(aURI, filename);
    2752           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2753             : 
    2754             :     // Store the file name
    2755           0 :     data = new URIData;
    2756           0 :     NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
    2757             : 
    2758           0 :     data->mNeedsPersisting = aNeedsPersisting;
    2759           0 :     data->mNeedsFixup = true;
    2760           0 :     data->mFilename = filename;
    2761           0 :     data->mSaved = false;
    2762           0 :     data->mIsSubFrame = false;
    2763           0 :     data->mDataPath = mCurrentDataPath;
    2764           0 :     data->mDataPathIsRelative = mCurrentDataPathIsRelative;
    2765           0 :     data->mRelativePathToData = mCurrentRelativePathToData;
    2766           0 :     data->mRelativeDocumentURI = mTargetBaseURI;
    2767           0 :     data->mCharset = mCurrentCharset;
    2768             : 
    2769           0 :     if (aNeedsPersisting)
    2770           0 :         mCurrentThingsToPersist++;
    2771             : 
    2772           0 :     mURIMap.Put(spec, data);
    2773           0 :     if (aData)
    2774             :     {
    2775           0 :         *aData = data;
    2776             :     }
    2777             : 
    2778           0 :     return NS_OK;
    2779             : }
    2780             : 
    2781             : // Decide if we need to apply conversion to the passed channel.
    2782           0 : void nsWebBrowserPersist::SetApplyConversionIfNeeded(nsIChannel *aChannel)
    2783             : {
    2784           0 :     nsresult rv = NS_OK;
    2785           0 :     nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aChannel, &rv);
    2786           0 :     if (NS_FAILED(rv))
    2787           0 :         return;
    2788             : 
    2789             :     // Set the default conversion preference:
    2790           0 :     encChannel->SetApplyConversion(false);
    2791             : 
    2792           0 :     nsCOMPtr<nsIURI> thisURI;
    2793           0 :     aChannel->GetURI(getter_AddRefs(thisURI));
    2794           0 :     nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(thisURI));
    2795           0 :     if (!sourceURL)
    2796           0 :         return;
    2797           0 :     nsAutoCString extension;
    2798           0 :     sourceURL->GetFileExtension(extension);
    2799             : 
    2800           0 :     nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
    2801           0 :     encChannel->GetContentEncodings(getter_AddRefs(encEnum));
    2802           0 :     if (!encEnum)
    2803           0 :         return;
    2804             :     nsCOMPtr<nsIExternalHelperAppService> helperAppService =
    2805           0 :         do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
    2806           0 :     if (NS_FAILED(rv))
    2807           0 :         return;
    2808             :     bool hasMore;
    2809           0 :     rv = encEnum->HasMore(&hasMore);
    2810           0 :     if (NS_SUCCEEDED(rv) && hasMore)
    2811             :     {
    2812           0 :         nsAutoCString encType;
    2813           0 :         rv = encEnum->GetNext(encType);
    2814           0 :         if (NS_SUCCEEDED(rv))
    2815             :         {
    2816           0 :             bool applyConversion = false;
    2817           0 :             rv = helperAppService->ApplyDecodingForExtension(extension, encType,
    2818           0 :                                                              &applyConversion);
    2819           0 :             if (NS_SUCCEEDED(rv))
    2820           0 :                 encChannel->SetApplyConversion(applyConversion);
    2821             :         }
    2822             :     }
    2823             : }

Generated by: LCOV version 1.13