embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4070 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/ArrayUtils.h"
    1.10 +
    1.11 +#include "nspr.h"
    1.12 +
    1.13 +#include "nsIFileStreams.h"       // New Necko file streams
    1.14 +#include <algorithm>
    1.15 +
    1.16 +#include "nsNetUtil.h"
    1.17 +#include "nsComponentManagerUtils.h"
    1.18 +#include "nsIComponentRegistrar.h"
    1.19 +#include "nsIStorageStream.h"
    1.20 +#include "nsISeekableStream.h"
    1.21 +#include "nsIHttpChannel.h"
    1.22 +#include "nsIHttpChannelInternal.h"
    1.23 +#include "nsIEncodedChannel.h"
    1.24 +#include "nsIUploadChannel.h"
    1.25 +#include "nsICachingChannel.h"
    1.26 +#include "nsIFileChannel.h"
    1.27 +#include "nsEscape.h"
    1.28 +#include "nsUnicharUtils.h"
    1.29 +#include "nsIStringEnumerator.h"
    1.30 +#include "nsCRT.h"
    1.31 +#include "nsSupportsArray.h"
    1.32 +#include "nsContentCID.h"
    1.33 +#include "nsStreamUtils.h"
    1.34 +
    1.35 +#include "nsCExternalHandlerService.h"
    1.36 +
    1.37 +#include "nsIURL.h"
    1.38 +#include "nsIFileURL.h"
    1.39 +#include "nsIDocument.h"
    1.40 +#include "nsIDOMDocument.h"
    1.41 +#include "nsIDOMXMLDocument.h"
    1.42 +#include "nsIDOMTreeWalker.h"
    1.43 +#include "nsIDOMNode.h"
    1.44 +#include "nsIDOMComment.h"
    1.45 +#include "nsIDOMMozNamedAttrMap.h"
    1.46 +#include "nsIDOMAttr.h"
    1.47 +#include "nsIDOMNodeList.h"
    1.48 +#include "nsIWebProgressListener.h"
    1.49 +#include "nsIAuthPrompt.h"
    1.50 +#include "nsIPrompt.h"
    1.51 +#include "nsISHEntry.h"
    1.52 +#include "nsIWebPageDescriptor.h"
    1.53 +#include "nsIFormControl.h"
    1.54 +#include "nsContentUtils.h"
    1.55 +
    1.56 +#include "nsIDOMNodeFilter.h"
    1.57 +#include "nsIDOMProcessingInstruction.h"
    1.58 +#include "nsIDOMHTMLAnchorElement.h"
    1.59 +#include "nsIDOMHTMLAreaElement.h"
    1.60 +#include "nsIDOMHTMLCollection.h"
    1.61 +#include "nsIDOMHTMLImageElement.h"
    1.62 +#include "nsIDOMHTMLScriptElement.h"
    1.63 +#include "nsIDOMHTMLLinkElement.h"
    1.64 +#include "nsIDOMHTMLBaseElement.h"
    1.65 +#include "nsIDOMHTMLFrameElement.h"
    1.66 +#include "nsIDOMHTMLIFrameElement.h"
    1.67 +#include "nsIDOMHTMLInputElement.h"
    1.68 +#include "nsIDOMHTMLEmbedElement.h"
    1.69 +#include "nsIDOMHTMLObjectElement.h"
    1.70 +#include "nsIDOMHTMLAppletElement.h"
    1.71 +#include "nsIDOMHTMLOptionElement.h"
    1.72 +#include "nsIDOMHTMLTextAreaElement.h"
    1.73 +#include "nsIDOMHTMLDocument.h"
    1.74 +#include "nsIDOMHTMLSourceElement.h"
    1.75 +#include "nsIDOMHTMLMediaElement.h"
    1.76 +
    1.77 +#include "nsIImageLoadingContent.h"
    1.78 +
    1.79 +#include "ftpCore.h"
    1.80 +#include "nsITransport.h"
    1.81 +#include "nsISocketTransport.h"
    1.82 +#include "nsIStringBundle.h"
    1.83 +#include "nsIProtocolHandler.h"
    1.84 +
    1.85 +#include "nsWebBrowserPersist.h"
    1.86 +
    1.87 +#include "nsIContent.h"
    1.88 +#include "nsIMIMEInfo.h"
    1.89 +#include "mozilla/dom/HTMLInputElement.h"
    1.90 +#include "mozilla/dom/HTMLSharedElement.h"
    1.91 +#include "mozilla/dom/HTMLSharedObjectElement.h"
    1.92 +
    1.93 +using namespace mozilla;
    1.94 +using namespace mozilla::dom;
    1.95 +
    1.96 +// Buffer file writes in 32kb chunks
    1.97 +#define BUFFERED_OUTPUT_SIZE (1024 * 32)
    1.98 +
    1.99 +// Information about a DOM document
   1.100 +struct DocData
   1.101 +{
   1.102 +    nsCOMPtr<nsIURI> mBaseURI;
   1.103 +    nsCOMPtr<nsIDOMDocument> mDocument;
   1.104 +    nsCOMPtr<nsIURI> mFile;
   1.105 +    nsCOMPtr<nsIURI> mDataPath;
   1.106 +    bool mDataPathIsRelative;
   1.107 +    nsCString mRelativePathToData;
   1.108 +    nsCString mCharset;
   1.109 +};
   1.110 +
   1.111 +// Information about a URI
   1.112 +struct URIData
   1.113 +{
   1.114 +    bool mNeedsPersisting;
   1.115 +    bool mSaved;
   1.116 +    bool mIsSubFrame;
   1.117 +    bool mDataPathIsRelative;
   1.118 +    bool mNeedsFixup;
   1.119 +    nsString mFilename;
   1.120 +    nsString mSubFrameExt;
   1.121 +    nsCOMPtr<nsIURI> mFile;
   1.122 +    nsCOMPtr<nsIURI> mDataPath;
   1.123 +    nsCString mRelativePathToData;
   1.124 +    nsCString mCharset;
   1.125 +};
   1.126 +
   1.127 +// Information about the output stream
   1.128 +struct OutputData
   1.129 +{
   1.130 +    nsCOMPtr<nsIURI> mFile;
   1.131 +    nsCOMPtr<nsIURI> mOriginalLocation;
   1.132 +    nsCOMPtr<nsIOutputStream> mStream;
   1.133 +    int64_t mSelfProgress;
   1.134 +    int64_t mSelfProgressMax;
   1.135 +    bool mCalcFileExt;
   1.136 +
   1.137 +    OutputData(nsIURI *aFile, nsIURI *aOriginalLocation, bool aCalcFileExt) :
   1.138 +        mFile(aFile),
   1.139 +        mOriginalLocation(aOriginalLocation),
   1.140 +        mSelfProgress(0),
   1.141 +        mSelfProgressMax(10000),
   1.142 +        mCalcFileExt(aCalcFileExt)
   1.143 +    {
   1.144 +    }
   1.145 +    ~OutputData()
   1.146 +    {
   1.147 +        if (mStream)
   1.148 +        {
   1.149 +            mStream->Close();
   1.150 +        }
   1.151 +    }
   1.152 +};
   1.153 +
   1.154 +struct UploadData
   1.155 +{
   1.156 +    nsCOMPtr<nsIURI> mFile;
   1.157 +    int64_t mSelfProgress;
   1.158 +    int64_t mSelfProgressMax;
   1.159 +
   1.160 +    UploadData(nsIURI *aFile) :
   1.161 +        mFile(aFile),
   1.162 +        mSelfProgress(0),
   1.163 +        mSelfProgressMax(10000)
   1.164 +    {
   1.165 +    }
   1.166 +};
   1.167 +
   1.168 +struct CleanupData
   1.169 +{
   1.170 +    nsCOMPtr<nsIFile> mFile;
   1.171 +    // Snapshot of what the file actually is at the time of creation so that if
   1.172 +    // it transmutes into something else later on it can be ignored. For example,
   1.173 +    // catch files that turn into dirs or vice versa.
   1.174 +    bool mIsDirectory;
   1.175 +};
   1.176 +
   1.177 +// Maximum file length constant. The max file name length is
   1.178 +// volume / server dependent but it is difficult to obtain
   1.179 +// that information. Instead this constant is a reasonable value that
   1.180 +// modern systems should able to cope with.
   1.181 +const uint32_t kDefaultMaxFilenameLength = 64;
   1.182 +
   1.183 +// Default flags for persistence
   1.184 +const uint32_t kDefaultPersistFlags =
   1.185 +    nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
   1.186 +    nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
   1.187 +
   1.188 +// String bundle where error messages come from
   1.189 +const char *kWebBrowserPersistStringBundle =
   1.190 +    "chrome://global/locale/nsWebBrowserPersist.properties";
   1.191 +
   1.192 +nsWebBrowserPersist::nsWebBrowserPersist() :
   1.193 +    mCurrentThingsToPersist(0),
   1.194 +    mFirstAndOnlyUse(true),
   1.195 +    mCancel(false),
   1.196 +    mJustStartedLoading(true),
   1.197 +    mCompleted(false),
   1.198 +    mStartSaving(false),
   1.199 +    mReplaceExisting(true),
   1.200 +    mSerializingOutput(false),
   1.201 +    mIsPrivate(false),
   1.202 +    mPersistFlags(kDefaultPersistFlags),
   1.203 +    mPersistResult(NS_OK),
   1.204 +    mTotalCurrentProgress(0),
   1.205 +    mTotalMaxProgress(0),
   1.206 +    mWrapColumn(72),
   1.207 +    mEncodingFlags(0)
   1.208 +{
   1.209 +}
   1.210 +
   1.211 +nsWebBrowserPersist::~nsWebBrowserPersist()
   1.212 +{
   1.213 +    Cleanup();
   1.214 +}
   1.215 +
   1.216 +//*****************************************************************************
   1.217 +// nsWebBrowserPersist::nsISupports
   1.218 +//*****************************************************************************
   1.219 +
   1.220 +NS_IMPL_ADDREF(nsWebBrowserPersist)
   1.221 +NS_IMPL_RELEASE(nsWebBrowserPersist)
   1.222 +
   1.223 +NS_INTERFACE_MAP_BEGIN(nsWebBrowserPersist)
   1.224 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersist)
   1.225 +    NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersist)
   1.226 +    NS_INTERFACE_MAP_ENTRY(nsICancelable)
   1.227 +    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   1.228 +    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1.229 +    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   1.230 +    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   1.231 +    NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   1.232 +NS_INTERFACE_MAP_END
   1.233 +
   1.234 +
   1.235 +//*****************************************************************************
   1.236 +// nsWebBrowserPersist::nsIInterfaceRequestor
   1.237 +//*****************************************************************************
   1.238 +
   1.239 +NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
   1.240 +{
   1.241 +    NS_ENSURE_ARG_POINTER(aIFace);
   1.242 +
   1.243 +    *aIFace = nullptr;
   1.244 +
   1.245 +    nsresult rv = QueryInterface(aIID, aIFace);
   1.246 +    if (NS_SUCCEEDED(rv))
   1.247 +    {
   1.248 +        return rv;
   1.249 +    }
   1.250 +
   1.251 +    if (mProgressListener && (aIID.Equals(NS_GET_IID(nsIAuthPrompt))
   1.252 +                             || aIID.Equals(NS_GET_IID(nsIPrompt))))
   1.253 +    {
   1.254 +        mProgressListener->QueryInterface(aIID, aIFace);
   1.255 +        if (*aIFace)
   1.256 +            return NS_OK;
   1.257 +    }
   1.258 +
   1.259 +    nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
   1.260 +    if (req)
   1.261 +    {
   1.262 +        return req->GetInterface(aIID, aIFace);
   1.263 +    }
   1.264 +
   1.265 +    return NS_ERROR_NO_INTERFACE;
   1.266 +}
   1.267 +
   1.268 +
   1.269 +//*****************************************************************************
   1.270 +// nsWebBrowserPersist::nsIWebBrowserPersist
   1.271 +//*****************************************************************************
   1.272 +
   1.273 +/* attribute unsigned long persistFlags; */
   1.274 +NS_IMETHODIMP nsWebBrowserPersist::GetPersistFlags(uint32_t *aPersistFlags)
   1.275 +{
   1.276 +    NS_ENSURE_ARG_POINTER(aPersistFlags);
   1.277 +    *aPersistFlags = mPersistFlags;
   1.278 +    return NS_OK;
   1.279 +}
   1.280 +NS_IMETHODIMP nsWebBrowserPersist::SetPersistFlags(uint32_t aPersistFlags)
   1.281 +{
   1.282 +    mPersistFlags = aPersistFlags;
   1.283 +    mReplaceExisting = (mPersistFlags & PERSIST_FLAGS_REPLACE_EXISTING_FILES) ? true : false;
   1.284 +    mSerializingOutput = (mPersistFlags & PERSIST_FLAGS_SERIALIZE_OUTPUT) ? true : false;
   1.285 +    return NS_OK;
   1.286 +}
   1.287 +
   1.288 +/* readonly attribute unsigned long currentState; */
   1.289 +NS_IMETHODIMP nsWebBrowserPersist::GetCurrentState(uint32_t *aCurrentState)
   1.290 +{
   1.291 +    NS_ENSURE_ARG_POINTER(aCurrentState);
   1.292 +    if (mCompleted)
   1.293 +    {
   1.294 +        *aCurrentState = PERSIST_STATE_FINISHED;
   1.295 +    }
   1.296 +    else if (mFirstAndOnlyUse)
   1.297 +    {
   1.298 +        *aCurrentState = PERSIST_STATE_SAVING;
   1.299 +    }
   1.300 +    else
   1.301 +    {
   1.302 +        *aCurrentState = PERSIST_STATE_READY;
   1.303 +    }
   1.304 +    return NS_OK;
   1.305 +}
   1.306 +
   1.307 +/* readonly attribute unsigned long result; */
   1.308 +NS_IMETHODIMP nsWebBrowserPersist::GetResult(nsresult *aResult)
   1.309 +{
   1.310 +    NS_ENSURE_ARG_POINTER(aResult);
   1.311 +    *aResult = mPersistResult;
   1.312 +    return NS_OK;
   1.313 +}
   1.314 +
   1.315 +/* attribute nsIWebBrowserPersistProgress progressListener; */
   1.316 +NS_IMETHODIMP nsWebBrowserPersist::GetProgressListener(
   1.317 +    nsIWebProgressListener * *aProgressListener)
   1.318 +{
   1.319 +    NS_ENSURE_ARG_POINTER(aProgressListener);
   1.320 +    *aProgressListener = mProgressListener;
   1.321 +    NS_IF_ADDREF(*aProgressListener);
   1.322 +    return NS_OK;
   1.323 +}
   1.324 +
   1.325 +NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
   1.326 +    nsIWebProgressListener * aProgressListener)
   1.327 +{
   1.328 +    mProgressListener = aProgressListener;
   1.329 +    mProgressListener2 = do_QueryInterface(aProgressListener);
   1.330 +    mEventSink = do_GetInterface(aProgressListener);
   1.331 +    return NS_OK;
   1.332 +}
   1.333 +
   1.334 +/* void saveURI (in nsIURI aURI, in nsISupports aCacheKey, in nsIURI aReferrer,
   1.335 +   in nsIInputStream aPostData, in wstring aExtraHeaders,
   1.336 +   in nsISupports aFile, in nsILoadContext aPrivayContext); */
   1.337 +NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
   1.338 +    nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, nsILoadContext* aPrivacyContext)
   1.339 +{
   1.340 +    return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, aFile,
   1.341 +                               aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
   1.342 +}
   1.343 +
   1.344 +NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
   1.345 +    nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, bool aIsPrivate)
   1.346 +{
   1.347 +    NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   1.348 +    mFirstAndOnlyUse = false; // Stop people from reusing this object!
   1.349 +
   1.350 +    nsCOMPtr<nsIURI> fileAsURI;
   1.351 +    nsresult rv;
   1.352 +    rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   1.353 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   1.354 +
   1.355 +    // SaveURI doesn't like broken uris.
   1.356 +    mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
   1.357 +    rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
   1.358 +    return NS_FAILED(rv) ? rv : NS_OK;
   1.359 +}
   1.360 +
   1.361 +/* void saveChannel (in nsIChannel aChannel, in nsISupports aFile); */
   1.362 +NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
   1.363 +    nsIChannel *aChannel, nsISupports *aFile)
   1.364 +{
   1.365 +    NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   1.366 +    mFirstAndOnlyUse = false; // Stop people from reusing this object!
   1.367 +
   1.368 +    nsCOMPtr<nsIURI> fileAsURI;
   1.369 +    nsresult rv;
   1.370 +    rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   1.371 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   1.372 +
   1.373 +    rv = aChannel->GetURI(getter_AddRefs(mURI));
   1.374 +    NS_ENSURE_SUCCESS(rv, rv);
   1.375 +
   1.376 +    // SaveURI doesn't like broken uris.
   1.377 +    mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
   1.378 +    rv = SaveChannelInternal(aChannel, fileAsURI, false);
   1.379 +    return NS_FAILED(rv) ? rv : NS_OK;
   1.380 +}
   1.381 +
   1.382 +
   1.383 +/* void saveDocument (in nsIDOMDocument aDocument, in nsIURI aFileURI,
   1.384 +   in nsIURI aDataPathURI, in string aOutputContentType,
   1.385 +   in unsigned long aEncodingFlags, in unsigned long aWrapColumn); */
   1.386 +NS_IMETHODIMP nsWebBrowserPersist::SaveDocument(
   1.387 +    nsIDOMDocument *aDocument, nsISupports *aFile, nsISupports *aDataPath,
   1.388 +    const char *aOutputContentType, uint32_t aEncodingFlags, uint32_t aWrapColumn)
   1.389 +{
   1.390 +    NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   1.391 +    mFirstAndOnlyUse = false; // Stop people from reusing this object!
   1.392 +
   1.393 +    nsCOMPtr<nsIURI> fileAsURI;
   1.394 +    nsCOMPtr<nsIURI> datapathAsURI;
   1.395 +    nsresult rv;
   1.396 +
   1.397 +    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
   1.398 +    nsCOMPtr<nsILoadContext> privacyContext = doc ? doc->GetLoadContext() : nullptr;
   1.399 +    mIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
   1.400 +
   1.401 +    rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   1.402 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   1.403 +    if (aDataPath)
   1.404 +    {
   1.405 +        rv = GetValidURIFromObject(aDataPath, getter_AddRefs(datapathAsURI));
   1.406 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   1.407 +    }
   1.408 +
   1.409 +    mWrapColumn = aWrapColumn;
   1.410 +
   1.411 +    // Produce nsIDocumentEncoder encoding flags
   1.412 +    mEncodingFlags = 0;
   1.413 +    if (aEncodingFlags & ENCODE_FLAGS_SELECTION_ONLY)
   1.414 +        mEncodingFlags |= nsIDocumentEncoder::OutputSelectionOnly;
   1.415 +    if (aEncodingFlags & ENCODE_FLAGS_FORMATTED)
   1.416 +        mEncodingFlags |= nsIDocumentEncoder::OutputFormatted;
   1.417 +    if (aEncodingFlags & ENCODE_FLAGS_RAW)
   1.418 +        mEncodingFlags |= nsIDocumentEncoder::OutputRaw;
   1.419 +    if (aEncodingFlags & ENCODE_FLAGS_BODY_ONLY)
   1.420 +        mEncodingFlags |= nsIDocumentEncoder::OutputBodyOnly;
   1.421 +    if (aEncodingFlags & ENCODE_FLAGS_PREFORMATTED)
   1.422 +        mEncodingFlags |= nsIDocumentEncoder::OutputPreformatted;
   1.423 +    if (aEncodingFlags & ENCODE_FLAGS_WRAP)
   1.424 +        mEncodingFlags |= nsIDocumentEncoder::OutputWrap;
   1.425 +    if (aEncodingFlags & ENCODE_FLAGS_FORMAT_FLOWED)
   1.426 +        mEncodingFlags |= nsIDocumentEncoder::OutputFormatFlowed;
   1.427 +    if (aEncodingFlags & ENCODE_FLAGS_ABSOLUTE_LINKS)
   1.428 +        mEncodingFlags |= nsIDocumentEncoder::OutputAbsoluteLinks;
   1.429 +    if (aEncodingFlags & ENCODE_FLAGS_ENCODE_BASIC_ENTITIES)
   1.430 +        mEncodingFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities;
   1.431 +    if (aEncodingFlags & ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES)
   1.432 +        mEncodingFlags |= nsIDocumentEncoder::OutputEncodeLatin1Entities;
   1.433 +    if (aEncodingFlags & ENCODE_FLAGS_ENCODE_HTML_ENTITIES)
   1.434 +        mEncodingFlags |= nsIDocumentEncoder::OutputEncodeHTMLEntities;
   1.435 +    if (aEncodingFlags & ENCODE_FLAGS_ENCODE_W3C_ENTITIES)
   1.436 +        mEncodingFlags |= nsIDocumentEncoder::OutputEncodeW3CEntities;
   1.437 +    if (aEncodingFlags & ENCODE_FLAGS_CR_LINEBREAKS)
   1.438 +        mEncodingFlags |= nsIDocumentEncoder::OutputCRLineBreak;
   1.439 +    if (aEncodingFlags & ENCODE_FLAGS_LF_LINEBREAKS)
   1.440 +        mEncodingFlags |= nsIDocumentEncoder::OutputLFLineBreak;
   1.441 +    if (aEncodingFlags & ENCODE_FLAGS_NOSCRIPT_CONTENT)
   1.442 +        mEncodingFlags |= nsIDocumentEncoder::OutputNoScriptContent;
   1.443 +    if (aEncodingFlags & ENCODE_FLAGS_NOFRAMES_CONTENT)
   1.444 +        mEncodingFlags |= nsIDocumentEncoder::OutputNoFramesContent;
   1.445 +
   1.446 +    if (aOutputContentType)
   1.447 +    {
   1.448 +        mContentType.AssignASCII(aOutputContentType);
   1.449 +    }
   1.450 +
   1.451 +    rv = SaveDocumentInternal(aDocument, fileAsURI, datapathAsURI);
   1.452 +
   1.453 +    // Now save the URIs that have been gathered
   1.454 +
   1.455 +    if (NS_SUCCEEDED(rv) && datapathAsURI)
   1.456 +    {
   1.457 +        rv = SaveGatheredURIs(fileAsURI);
   1.458 +    }
   1.459 +    else if (mProgressListener)
   1.460 +    {
   1.461 +        // tell the listener we're done
   1.462 +        mProgressListener->OnStateChange(nullptr, nullptr,
   1.463 +                                         nsIWebProgressListener::STATE_START |
   1.464 +                                         nsIWebProgressListener::STATE_IS_NETWORK,
   1.465 +                                         NS_OK);
   1.466 +        mProgressListener->OnStateChange(nullptr, nullptr,
   1.467 +                                         nsIWebProgressListener::STATE_STOP |
   1.468 +                                         nsIWebProgressListener::STATE_IS_NETWORK,
   1.469 +                                         rv);
   1.470 +    }
   1.471 +
   1.472 +    return rv;
   1.473 +}
   1.474 +
   1.475 +/* void cancel(nsresult aReason); */
   1.476 +NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
   1.477 +{
   1.478 +    mCancel = true;
   1.479 +    EndDownload(aReason);
   1.480 +    return NS_OK;
   1.481 +}
   1.482 +
   1.483 +
   1.484 +/* void cancelSave(); */
   1.485 +NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
   1.486 +{
   1.487 +    return Cancel(NS_BINDING_ABORTED);
   1.488 +}
   1.489 +
   1.490 +
   1.491 +nsresult
   1.492 +nsWebBrowserPersist::StartUpload(nsIStorageStream *storStream,
   1.493 +    nsIURI *aDestinationURI, const nsACString &aContentType)
   1.494 +{
   1.495 +     // setup the upload channel if the destination is not local
   1.496 +    nsCOMPtr<nsIInputStream> inputstream;
   1.497 +    nsresult rv = storStream->NewInputStream(0, getter_AddRefs(inputstream));
   1.498 +    NS_ENSURE_TRUE(inputstream, NS_ERROR_FAILURE);
   1.499 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.500 +    return StartUpload(inputstream, aDestinationURI, aContentType);
   1.501 +}
   1.502 +
   1.503 +nsresult
   1.504 +nsWebBrowserPersist::StartUpload(nsIInputStream *aInputStream,
   1.505 +    nsIURI *aDestinationURI, const nsACString &aContentType)
   1.506 +{
   1.507 +    nsCOMPtr<nsIChannel> destChannel;
   1.508 +    CreateChannelFromURI(aDestinationURI, getter_AddRefs(destChannel));
   1.509 +    nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(destChannel));
   1.510 +    NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
   1.511 +
   1.512 +    // Set the upload stream
   1.513 +    // NOTE: ALL data must be available in "inputstream"
   1.514 +    nsresult rv = uploadChannel->SetUploadStream(aInputStream, aContentType, -1);
   1.515 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.516 +    rv = destChannel->AsyncOpen(this, nullptr);
   1.517 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.518 +
   1.519 +    // add this to the upload list
   1.520 +    nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
   1.521 +    mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
   1.522 +
   1.523 +    return NS_OK;
   1.524 +}
   1.525 +
   1.526 +nsresult
   1.527 +nsWebBrowserPersist::SaveGatheredURIs(nsIURI *aFileAsURI)
   1.528 +{
   1.529 +    nsresult rv = NS_OK;
   1.530 +
   1.531 +    // Count how many URIs in the URI map require persisting
   1.532 +    uint32_t urisToPersist = 0;
   1.533 +    if (mURIMap.Count() > 0)
   1.534 +    {
   1.535 +        mURIMap.EnumerateRead(EnumCountURIsToPersist, &urisToPersist);
   1.536 +    }
   1.537 +
   1.538 +    if (urisToPersist > 0)
   1.539 +    {
   1.540 +        // Persist each file in the uri map. The document(s)
   1.541 +        // will be saved after the last one of these is saved.
   1.542 +        mURIMap.EnumerateRead(EnumPersistURIs, this);
   1.543 +    }
   1.544 +
   1.545 +    // if we don't have anything in mOutputMap (added from above enumeration)
   1.546 +    // then we build the doc list (SaveDocuments)
   1.547 +    if (mOutputMap.Count() == 0)
   1.548 +    {
   1.549 +        // There are no URIs to save, so just save the document(s)
   1.550 +
   1.551 +        // State start notification
   1.552 +        uint32_t addToStateFlags = 0;
   1.553 +        if (mProgressListener)
   1.554 +        {
   1.555 +            if (mJustStartedLoading)
   1.556 +            {
   1.557 +                addToStateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   1.558 +            }
   1.559 +            mProgressListener->OnStateChange(nullptr, nullptr,
   1.560 +                nsIWebProgressListener::STATE_START | addToStateFlags, NS_OK);
   1.561 +        }
   1.562 +
   1.563 +        rv = SaveDocuments();
   1.564 +        if (NS_FAILED(rv))
   1.565 +            EndDownload(rv);
   1.566 +        else if (aFileAsURI)
   1.567 +        {
   1.568 +            // local files won't trigger OnStopRequest so we call EndDownload here
   1.569 +            bool isFile = false;
   1.570 +            aFileAsURI->SchemeIs("file", &isFile);
   1.571 +            if (isFile)
   1.572 +                EndDownload(NS_OK);
   1.573 +        }
   1.574 +
   1.575 +        // State stop notification
   1.576 +        if (mProgressListener)
   1.577 +        {
   1.578 +            mProgressListener->OnStateChange(nullptr, nullptr,
   1.579 +                nsIWebProgressListener::STATE_STOP | addToStateFlags, rv);
   1.580 +        }
   1.581 +    }
   1.582 +
   1.583 +    return rv;
   1.584 +}
   1.585 +
   1.586 +// this method returns true if there is another file to persist and false if not
   1.587 +bool
   1.588 +nsWebBrowserPersist::SerializeNextFile()
   1.589 +{
   1.590 +    if (!mSerializingOutput)
   1.591 +    {
   1.592 +        return false;
   1.593 +    }
   1.594 +
   1.595 +    nsresult rv = SaveGatheredURIs(nullptr);
   1.596 +    if (NS_FAILED(rv))
   1.597 +    {
   1.598 +        return false;
   1.599 +    }
   1.600 +
   1.601 +    return (mURIMap.Count()
   1.602 +        || mUploadList.Count()
   1.603 +        || mDocList.Length()
   1.604 +        || mOutputMap.Count());
   1.605 +}
   1.606 +
   1.607 +
   1.608 +//*****************************************************************************
   1.609 +// nsWebBrowserPersist::nsIRequestObserver
   1.610 +//*****************************************************************************
   1.611 +
   1.612 +NS_IMETHODIMP nsWebBrowserPersist::OnStartRequest(
   1.613 +    nsIRequest* request, nsISupports *ctxt)
   1.614 +{
   1.615 +    if (mProgressListener)
   1.616 +    {
   1.617 +        uint32_t stateFlags = nsIWebProgressListener::STATE_START |
   1.618 +                              nsIWebProgressListener::STATE_IS_REQUEST;
   1.619 +        if (mJustStartedLoading)
   1.620 +        {
   1.621 +            stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   1.622 +        }
   1.623 +        mProgressListener->OnStateChange(nullptr, request, stateFlags, NS_OK);
   1.624 +    }
   1.625 +
   1.626 +    mJustStartedLoading = false;
   1.627 +
   1.628 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   1.629 +    NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   1.630 +
   1.631 +    nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   1.632 +    OutputData *data = mOutputMap.Get(keyPtr);
   1.633 +
   1.634 +    // NOTE: This code uses the channel as a hash key so it will not
   1.635 +    //       recognize redirected channels because the key is not the same.
   1.636 +    //       When that happens we remove and add the data entry to use the
   1.637 +    //       new channel as the hash key.
   1.638 +    if (!data)
   1.639 +    {
   1.640 +        UploadData *upData = mUploadList.Get(keyPtr);
   1.641 +        if (!upData)
   1.642 +        {
   1.643 +            // Redirect? Try and fixup the output table
   1.644 +            nsresult rv = FixRedirectedChannelEntry(channel);
   1.645 +            NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.646 +
   1.647 +            // Should be able to find the data after fixup unless redirects
   1.648 +            // are disabled.
   1.649 +            data = mOutputMap.Get(keyPtr);
   1.650 +            if (!data)
   1.651 +            {
   1.652 +                return NS_ERROR_FAILURE;
   1.653 +            }
   1.654 +        }
   1.655 +    }
   1.656 +
   1.657 +    if (data && data->mFile)
   1.658 +    {
   1.659 +        // If PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION is set in mPersistFlags,
   1.660 +        // try to determine whether this channel needs to apply Content-Encoding
   1.661 +        // conversions.
   1.662 +        NS_ASSERTION(!((mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION) &&
   1.663 +                      (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)),
   1.664 +                     "Conflict in persist flags: both AUTODETECT and NO_CONVERSION set");
   1.665 +        if (mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION)
   1.666 +            SetApplyConversionIfNeeded(channel);
   1.667 +
   1.668 +        if (data->mCalcFileExt && !(mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES))
   1.669 +        {
   1.670 +            // this is the first point at which the server can tell us the mimetype
   1.671 +            CalculateAndAppendFileExt(data->mFile, channel, data->mOriginalLocation);
   1.672 +
   1.673 +            // now make filename conformant and unique
   1.674 +            CalculateUniqueFilename(data->mFile);
   1.675 +        }
   1.676 +
   1.677 +        // compare uris and bail before we add to output map if they are equal
   1.678 +        bool isEqual = false;
   1.679 +        if (NS_SUCCEEDED(data->mFile->Equals(data->mOriginalLocation, &isEqual))
   1.680 +            && isEqual)
   1.681 +        {
   1.682 +            // remove from output map
   1.683 +            mOutputMap.Remove(keyPtr);
   1.684 +
   1.685 +            // cancel; we don't need to know any more
   1.686 +            // stop request will get called
   1.687 +            request->Cancel(NS_BINDING_ABORTED);
   1.688 +        }
   1.689 +    }
   1.690 +
   1.691 +    return NS_OK;
   1.692 +}
   1.693 +
   1.694 +NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest(
   1.695 +    nsIRequest* request, nsISupports *ctxt, nsresult status)
   1.696 +{
   1.697 +    nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   1.698 +    OutputData *data = mOutputMap.Get(keyPtr);
   1.699 +    if (data)
   1.700 +    {
   1.701 +        if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(status))
   1.702 +            SendErrorStatusChange(true, status, request, data->mFile);
   1.703 +
   1.704 +        // This will automatically close the output stream
   1.705 +        mOutputMap.Remove(keyPtr);
   1.706 +    }
   1.707 +    else
   1.708 +    {
   1.709 +        // if we didn't find the data in mOutputMap, try mUploadList
   1.710 +        UploadData *upData = mUploadList.Get(keyPtr);
   1.711 +        if (upData)
   1.712 +        {
   1.713 +            mUploadList.Remove(keyPtr);
   1.714 +        }
   1.715 +    }
   1.716 +
   1.717 +    // ensure we call SaveDocuments if we:
   1.718 +    // 1) aren't canceling
   1.719 +    // 2) we haven't triggered the save (which we only want to trigger once)
   1.720 +    // 3) we aren't serializing (which will call it inside SerializeNextFile)
   1.721 +    if (mOutputMap.Count() == 0 && !mCancel && !mStartSaving && !mSerializingOutput)
   1.722 +    {
   1.723 +        nsresult rv = SaveDocuments();
   1.724 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.725 +    }
   1.726 +
   1.727 +    bool completed = false;
   1.728 +    if (mOutputMap.Count() == 0 && mUploadList.Count() == 0 && !mCancel)
   1.729 +    {
   1.730 +        // if no documents left in mDocList, --> done
   1.731 +        // if we have no files left to serialize and no error result, --> done
   1.732 +        if (mDocList.Length() == 0
   1.733 +            || (!SerializeNextFile() && NS_SUCCEEDED(mPersistResult)))
   1.734 +        {
   1.735 +            completed = true;
   1.736 +        }
   1.737 +    }
   1.738 +
   1.739 +    if (completed)
   1.740 +    {
   1.741 +        // we're all done, do our cleanup
   1.742 +        EndDownload(status);
   1.743 +    }
   1.744 +
   1.745 +    if (mProgressListener)
   1.746 +    {
   1.747 +        uint32_t stateFlags = nsIWebProgressListener::STATE_STOP |
   1.748 +                              nsIWebProgressListener::STATE_IS_REQUEST;
   1.749 +        if (completed)
   1.750 +        {
   1.751 +            stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   1.752 +        }
   1.753 +        mProgressListener->OnStateChange(nullptr, request, stateFlags, status);
   1.754 +    }
   1.755 +    if (completed)
   1.756 +    {
   1.757 +        mProgressListener = nullptr;
   1.758 +        mProgressListener2 = nullptr;
   1.759 +        mEventSink = nullptr;
   1.760 +    }
   1.761 +
   1.762 +    return NS_OK;
   1.763 +}
   1.764 +
   1.765 +//*****************************************************************************
   1.766 +// nsWebBrowserPersist::nsIStreamListener
   1.767 +//*****************************************************************************
   1.768 +
   1.769 +NS_IMETHODIMP
   1.770 +nsWebBrowserPersist::OnDataAvailable(
   1.771 +    nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream,
   1.772 +    uint64_t aOffset, uint32_t aLength)
   1.773 +{
   1.774 +    bool cancel = mCancel;
   1.775 +    if (!cancel)
   1.776 +    {
   1.777 +        nsresult rv = NS_OK;
   1.778 +        uint32_t bytesRemaining = aLength;
   1.779 +
   1.780 +        nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   1.781 +        NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   1.782 +
   1.783 +        nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   1.784 +        OutputData *data = mOutputMap.Get(keyPtr);
   1.785 +        if (!data) {
   1.786 +            // might be uploadData; consume necko's buffer and bail...
   1.787 +            uint32_t n;
   1.788 +            return aIStream->ReadSegments(NS_DiscardSegment, nullptr, aLength, &n);
   1.789 +        }
   1.790 +
   1.791 +        bool readError = true;
   1.792 +
   1.793 +        // Make the output stream
   1.794 +        if (!data->mStream)
   1.795 +        {
   1.796 +            rv = MakeOutputStream(data->mFile, getter_AddRefs(data->mStream));
   1.797 +            if (NS_FAILED(rv))
   1.798 +            {
   1.799 +                readError = false;
   1.800 +                cancel = true;
   1.801 +            }
   1.802 +        }
   1.803 +
   1.804 +        // Read data from the input and write to the output
   1.805 +        char buffer[8192];
   1.806 +        uint32_t bytesRead;
   1.807 +        while (!cancel && bytesRemaining)
   1.808 +        {
   1.809 +            readError = true;
   1.810 +            rv = aIStream->Read(buffer,
   1.811 +                                std::min(uint32_t(sizeof(buffer)), bytesRemaining),
   1.812 +                                &bytesRead);
   1.813 +            if (NS_SUCCEEDED(rv))
   1.814 +            {
   1.815 +                readError = false;
   1.816 +                // Write out the data until something goes wrong, or, it is
   1.817 +                // all written.  We loop because for some errors (e.g., disk
   1.818 +                // full), we get NS_OK with some bytes written, then an error.
   1.819 +                // So, we want to write again in that case to get the actual
   1.820 +                // error code.
   1.821 +                const char *bufPtr = buffer; // Where to write from.
   1.822 +                while (NS_SUCCEEDED(rv) && bytesRead)
   1.823 +                {
   1.824 +                    uint32_t bytesWritten = 0;
   1.825 +                    rv = data->mStream->Write(bufPtr, bytesRead, &bytesWritten);
   1.826 +                    if (NS_SUCCEEDED(rv))
   1.827 +                    {
   1.828 +                        bytesRead -= bytesWritten;
   1.829 +                        bufPtr += bytesWritten;
   1.830 +                        bytesRemaining -= bytesWritten;
   1.831 +                        // Force an error if (for some reason) we get NS_OK but
   1.832 +                        // no bytes written.
   1.833 +                        if (!bytesWritten)
   1.834 +                        {
   1.835 +                            rv = NS_ERROR_FAILURE;
   1.836 +                            cancel = true;
   1.837 +                        }
   1.838 +                    }
   1.839 +                    else
   1.840 +                    {
   1.841 +                        // Disaster - can't write out the bytes - disk full / permission?
   1.842 +                        cancel = true;
   1.843 +                    }
   1.844 +                }
   1.845 +            }
   1.846 +            else
   1.847 +            {
   1.848 +                // Disaster - can't read the bytes - broken link / file error?
   1.849 +                cancel = true;
   1.850 +            }
   1.851 +        }
   1.852 +
   1.853 +        int64_t channelContentLength = -1;
   1.854 +        if (!cancel &&
   1.855 +            NS_SUCCEEDED(channel->GetContentLength(&channelContentLength)))
   1.856 +        {
   1.857 +            // if we get -1 at this point, we didn't get content-length header
   1.858 +            // assume that we got all of the data and push what we have;
   1.859 +            // that's the best we can do now
   1.860 +            if ((-1 == channelContentLength) ||
   1.861 +                ((channelContentLength - (aOffset + aLength)) == 0))
   1.862 +            {
   1.863 +                NS_WARN_IF_FALSE(channelContentLength != -1,
   1.864 +                    "nsWebBrowserPersist::OnDataAvailable() no content length "
   1.865 +                    "header, pushing what we have");
   1.866 +                // we're done with this pass; see if we need to do upload
   1.867 +                nsAutoCString contentType;
   1.868 +                channel->GetContentType(contentType);
   1.869 +                // if we don't have the right type of output stream then it's a local file
   1.870 +                nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(data->mStream));
   1.871 +                if (storStream)
   1.872 +                {
   1.873 +                    data->mStream->Close();
   1.874 +                    data->mStream = nullptr; // null out stream so we don't close it later
   1.875 +                    rv = StartUpload(storStream, data->mFile, contentType);
   1.876 +                    if (NS_FAILED(rv))
   1.877 +                    {
   1.878 +                        readError = false;
   1.879 +                        cancel = true;
   1.880 +                    }
   1.881 +                }
   1.882 +            }
   1.883 +        }
   1.884 +
   1.885 +        // Notify listener if an error occurred.
   1.886 +        if (cancel)
   1.887 +        {
   1.888 +            SendErrorStatusChange(readError, rv,
   1.889 +                readError ? request : nullptr, data->mFile);
   1.890 +        }
   1.891 +    }
   1.892 +
   1.893 +    // Cancel reading?
   1.894 +    if (cancel)
   1.895 +    {
   1.896 +        EndDownload(NS_BINDING_ABORTED);
   1.897 +    }
   1.898 +
   1.899 +    return NS_OK;
   1.900 +}
   1.901 +
   1.902 +
   1.903 +//*****************************************************************************
   1.904 +// nsWebBrowserPersist::nsIProgressEventSink
   1.905 +//*****************************************************************************
   1.906 +
   1.907 +/* void onProgress (in nsIRequest request, in nsISupports ctxt,
   1.908 +    in unsigned long long aProgress, in unsigned long long aProgressMax); */
   1.909 +NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
   1.910 +    nsIRequest *request, nsISupports *ctxt, uint64_t aProgress,
   1.911 +    uint64_t aProgressMax)
   1.912 +{
   1.913 +    if (!mProgressListener)
   1.914 +    {
   1.915 +        return NS_OK;
   1.916 +    }
   1.917 +
   1.918 +    // Store the progress of this request
   1.919 +    nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   1.920 +    OutputData *data = mOutputMap.Get(keyPtr);
   1.921 +    if (data)
   1.922 +    {
   1.923 +        data->mSelfProgress = int64_t(aProgress);
   1.924 +        data->mSelfProgressMax = int64_t(aProgressMax);
   1.925 +    }
   1.926 +    else
   1.927 +    {
   1.928 +        UploadData *upData = mUploadList.Get(keyPtr);
   1.929 +        if (upData)
   1.930 +        {
   1.931 +            upData->mSelfProgress = int64_t(aProgress);
   1.932 +            upData->mSelfProgressMax = int64_t(aProgressMax);
   1.933 +        }
   1.934 +    }
   1.935 +
   1.936 +    // Notify listener of total progress
   1.937 +    CalcTotalProgress();
   1.938 +    if (mProgressListener2)
   1.939 +    {
   1.940 +      mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
   1.941 +            aProgressMax, mTotalCurrentProgress, mTotalMaxProgress);
   1.942 +    }
   1.943 +    else
   1.944 +    {
   1.945 +      // have to truncate 64-bit to 32bit
   1.946 +      mProgressListener->OnProgressChange(nullptr, request, uint64_t(aProgress),
   1.947 +              uint64_t(aProgressMax), mTotalCurrentProgress, mTotalMaxProgress);
   1.948 +    }
   1.949 +
   1.950 +    // If our progress listener implements nsIProgressEventSink,
   1.951 +    // forward the notification
   1.952 +    if (mEventSink)
   1.953 +    {
   1.954 +        mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
   1.955 +    }
   1.956 +
   1.957 +    return NS_OK;
   1.958 +}
   1.959 +
   1.960 +/* void onStatus (in nsIRequest request, in nsISupports ctxt,
   1.961 +    in nsresult status, in wstring statusArg); */
   1.962 +NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
   1.963 +    nsIRequest *request, nsISupports *ctxt, nsresult status,
   1.964 +    const char16_t *statusArg)
   1.965 +{
   1.966 +    if (mProgressListener)
   1.967 +    {
   1.968 +        // We need to filter out non-error error codes.
   1.969 +        // Is the only NS_SUCCEEDED value NS_OK?
   1.970 +        switch ( status )
   1.971 +        {
   1.972 +        case NS_NET_STATUS_RESOLVING_HOST:
   1.973 +        case NS_NET_STATUS_RESOLVED_HOST:
   1.974 +        case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
   1.975 +        case NS_NET_STATUS_END_FTP_TRANSACTION:
   1.976 +        case NS_NET_STATUS_CONNECTING_TO:
   1.977 +        case NS_NET_STATUS_CONNECTED_TO:
   1.978 +        case NS_NET_STATUS_SENDING_TO:
   1.979 +        case NS_NET_STATUS_RECEIVING_FROM:
   1.980 +        case NS_NET_STATUS_WAITING_FOR:
   1.981 +        case NS_NET_STATUS_READING:
   1.982 +        case NS_NET_STATUS_WRITING:
   1.983 +            break;
   1.984 +
   1.985 +        default:
   1.986 +            // Pass other notifications (for legitimate errors) along.
   1.987 +            mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
   1.988 +            break;
   1.989 +        }
   1.990 +
   1.991 +    }
   1.992 +
   1.993 +    // If our progress listener implements nsIProgressEventSink,
   1.994 +    // forward the notification
   1.995 +    if (mEventSink)
   1.996 +    {
   1.997 +        mEventSink->OnStatus(request, ctxt, status, statusArg);
   1.998 +    }
   1.999 +
  1.1000 +    return NS_OK;
  1.1001 +}
  1.1002 +
  1.1003 +
  1.1004 +//*****************************************************************************
  1.1005 +// nsWebBrowserPersist private methods
  1.1006 +//*****************************************************************************
  1.1007 +
  1.1008 +// Convert error info into proper message text and send OnStatusChange notification
  1.1009 +// to the web progress listener.
  1.1010 +nsresult nsWebBrowserPersist::SendErrorStatusChange(
  1.1011 +    bool aIsReadError, nsresult aResult, nsIRequest *aRequest, nsIURI *aURI)
  1.1012 +{
  1.1013 +    NS_ENSURE_ARG_POINTER(aURI);
  1.1014 +
  1.1015 +    if (!mProgressListener)
  1.1016 +    {
  1.1017 +        // Do nothing
  1.1018 +        return NS_OK;
  1.1019 +    }
  1.1020 +
  1.1021 +    // Get the file path or spec from the supplied URI
  1.1022 +    nsCOMPtr<nsIFile> file;
  1.1023 +    GetLocalFileFromURI(aURI, getter_AddRefs(file));
  1.1024 +    nsAutoString path;
  1.1025 +    if (file)
  1.1026 +    {
  1.1027 +        file->GetPath(path);
  1.1028 +    }
  1.1029 +    else
  1.1030 +    {
  1.1031 +        nsAutoCString fileurl;
  1.1032 +        aURI->GetSpec(fileurl);
  1.1033 +        AppendUTF8toUTF16(fileurl, path);
  1.1034 +    }
  1.1035 +
  1.1036 +    nsAutoString msgId;
  1.1037 +    switch(aResult)
  1.1038 +    {
  1.1039 +    case NS_ERROR_FILE_NAME_TOO_LONG:
  1.1040 +        // File name too long.
  1.1041 +        msgId.AssignLiteral("fileNameTooLongError");
  1.1042 +        break;
  1.1043 +    case NS_ERROR_FILE_ALREADY_EXISTS:
  1.1044 +        // File exists with same name as directory.
  1.1045 +        msgId.AssignLiteral("fileAlreadyExistsError");
  1.1046 +        break;
  1.1047 +    case NS_ERROR_FILE_DISK_FULL:
  1.1048 +    case NS_ERROR_FILE_NO_DEVICE_SPACE:
  1.1049 +        // Out of space on target volume.
  1.1050 +        msgId.AssignLiteral("diskFull");
  1.1051 +        break;
  1.1052 +
  1.1053 +    case NS_ERROR_FILE_READ_ONLY:
  1.1054 +        // Attempt to write to read/only file.
  1.1055 +        msgId.AssignLiteral("readOnly");
  1.1056 +        break;
  1.1057 +
  1.1058 +    case NS_ERROR_FILE_ACCESS_DENIED:
  1.1059 +        // Attempt to write without sufficient permissions.
  1.1060 +        msgId.AssignLiteral("accessError");
  1.1061 +        break;
  1.1062 +
  1.1063 +    default:
  1.1064 +        // Generic read/write error message.
  1.1065 +        if (aIsReadError)
  1.1066 +            msgId.AssignLiteral("readError");
  1.1067 +        else
  1.1068 +            msgId.AssignLiteral("writeError");
  1.1069 +        break;
  1.1070 +    }
  1.1071 +    // Get properties file bundle and extract status string.
  1.1072 +    nsresult rv;
  1.1073 +    nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
  1.1074 +    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
  1.1075 +
  1.1076 +    nsCOMPtr<nsIStringBundle> bundle;
  1.1077 +    rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
  1.1078 +    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
  1.1079 +
  1.1080 +    nsXPIDLString msgText;
  1.1081 +    const char16_t *strings[1];
  1.1082 +    strings[0] = path.get();
  1.1083 +    rv = bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText));
  1.1084 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1085 +
  1.1086 +    mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
  1.1087 +
  1.1088 +    return NS_OK;
  1.1089 +}
  1.1090 +
  1.1091 +nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
  1.1092 +{
  1.1093 +    NS_ENSURE_ARG_POINTER(aObject);
  1.1094 +    NS_ENSURE_ARG_POINTER(aURI);
  1.1095 +
  1.1096 +    nsCOMPtr<nsIFile> objAsFile = do_QueryInterface(aObject);
  1.1097 +    if (objAsFile)
  1.1098 +    {
  1.1099 +        return NS_NewFileURI(aURI, objAsFile);
  1.1100 +    }
  1.1101 +    nsCOMPtr<nsIURI> objAsURI = do_QueryInterface(aObject);
  1.1102 +    if (objAsURI)
  1.1103 +    {
  1.1104 +        *aURI = objAsURI;
  1.1105 +        NS_ADDREF(*aURI);
  1.1106 +        return NS_OK;
  1.1107 +    }
  1.1108 +
  1.1109 +    return NS_ERROR_FAILURE;
  1.1110 +}
  1.1111 +
  1.1112 +nsresult nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const
  1.1113 +{
  1.1114 +    nsresult rv;
  1.1115 +
  1.1116 +    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  1.1117 +    if (NS_FAILED(rv))
  1.1118 +        return rv;
  1.1119 +
  1.1120 +    nsCOMPtr<nsIFile> file;
  1.1121 +    rv = fileURL->GetFile(getter_AddRefs(file));
  1.1122 +    if (NS_FAILED(rv)) {
  1.1123 +        return rv;
  1.1124 +    }
  1.1125 +
  1.1126 +    file.forget(aLocalFile);
  1.1127 +    return NS_OK;
  1.1128 +}
  1.1129 +
  1.1130 +nsresult nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath) const
  1.1131 +{
  1.1132 +    NS_ENSURE_ARG_POINTER(aURI);
  1.1133 +
  1.1134 +    nsAutoCString newPath;
  1.1135 +    nsresult rv = aURI->GetPath(newPath);
  1.1136 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1137 +
  1.1138 +    // Append a forward slash if necessary
  1.1139 +    int32_t len = newPath.Length();
  1.1140 +    if (len > 0 && newPath.CharAt(len - 1) != '/')
  1.1141 +    {
  1.1142 +        newPath.Append('/');
  1.1143 +    }
  1.1144 +
  1.1145 +    // Store the path back on the URI
  1.1146 +    AppendUTF16toUTF8(aPath, newPath);
  1.1147 +    aURI->SetPath(newPath);
  1.1148 +
  1.1149 +    return NS_OK;
  1.1150 +}
  1.1151 +
  1.1152 +nsresult nsWebBrowserPersist::SaveURIInternal(
  1.1153 +    nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
  1.1154 +    nsIInputStream *aPostData, const char *aExtraHeaders,
  1.1155 +    nsIURI *aFile, bool aCalcFileExt, bool aIsPrivate)
  1.1156 +{
  1.1157 +    NS_ENSURE_ARG_POINTER(aURI);
  1.1158 +    NS_ENSURE_ARG_POINTER(aFile);
  1.1159 +
  1.1160 +    nsresult rv = NS_OK;
  1.1161 +
  1.1162 +    mURI = aURI;
  1.1163 +
  1.1164 +    nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
  1.1165 +    if (mPersistFlags & PERSIST_FLAGS_BYPASS_CACHE)
  1.1166 +    {
  1.1167 +        loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
  1.1168 +    }
  1.1169 +    else if (mPersistFlags & PERSIST_FLAGS_FROM_CACHE)
  1.1170 +    {
  1.1171 +        loadFlags |= nsIRequest::LOAD_FROM_CACHE;
  1.1172 +    }
  1.1173 +
  1.1174 +    // Extract the cache key
  1.1175 +    nsCOMPtr<nsISupports> cacheKey;
  1.1176 +    if (aCacheKey)
  1.1177 +    {
  1.1178 +        // Test if the cache key is actually a web page descriptor (docshell)
  1.1179 +        // or session history entry.
  1.1180 +        nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(aCacheKey);
  1.1181 +        if (!shEntry)
  1.1182 +        {
  1.1183 +            nsCOMPtr<nsIWebPageDescriptor> webPageDescriptor =
  1.1184 +                do_QueryInterface(aCacheKey);
  1.1185 +            if (webPageDescriptor)
  1.1186 +            {
  1.1187 +                nsCOMPtr<nsISupports> currentDescriptor;
  1.1188 +                webPageDescriptor->GetCurrentDescriptor(getter_AddRefs(currentDescriptor));
  1.1189 +                shEntry = do_QueryInterface(currentDescriptor);
  1.1190 +            }
  1.1191 +        }
  1.1192 +
  1.1193 +        if (shEntry)
  1.1194 +        {
  1.1195 +            shEntry->GetCacheKey(getter_AddRefs(cacheKey));
  1.1196 +        }
  1.1197 +        else
  1.1198 +        {
  1.1199 +            // Assume a plain cache key
  1.1200 +            cacheKey = aCacheKey;
  1.1201 +        }
  1.1202 +    }
  1.1203 +
  1.1204 +    // Open a channel to the URI
  1.1205 +    nsCOMPtr<nsIChannel> inputChannel;
  1.1206 +    rv = NS_NewChannel(getter_AddRefs(inputChannel), aURI,
  1.1207 +            nullptr, nullptr, static_cast<nsIInterfaceRequestor*>(this),
  1.1208 +            loadFlags);
  1.1209 +
  1.1210 +    nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
  1.1211 +    if (pbChannel)
  1.1212 +    {
  1.1213 +        pbChannel->SetPrivate(aIsPrivate);
  1.1214 +    }
  1.1215 +
  1.1216 +    if (NS_FAILED(rv) || inputChannel == nullptr)
  1.1217 +    {
  1.1218 +        EndDownload(NS_ERROR_FAILURE);
  1.1219 +        return NS_ERROR_FAILURE;
  1.1220 +    }
  1.1221 +
  1.1222 +    // Disable content conversion
  1.1223 +    if (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)
  1.1224 +    {
  1.1225 +        nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
  1.1226 +        if (encodedChannel)
  1.1227 +        {
  1.1228 +            encodedChannel->SetApplyConversion(false);
  1.1229 +        }
  1.1230 +    }
  1.1231 +
  1.1232 +    if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
  1.1233 +    {
  1.1234 +        nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
  1.1235 +                do_QueryInterface(inputChannel);
  1.1236 +        if (httpChannelInternal)
  1.1237 +            httpChannelInternal->SetForceAllowThirdPartyCookie(true);
  1.1238 +    }
  1.1239 +
  1.1240 +    // Set the referrer, post data and headers if any
  1.1241 +    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
  1.1242 +    if (httpChannel)
  1.1243 +    {
  1.1244 +        // Referrer
  1.1245 +        if (aReferrer)
  1.1246 +        {
  1.1247 +            httpChannel->SetReferrer(aReferrer);
  1.1248 +        }
  1.1249 +
  1.1250 +        // Post data
  1.1251 +        if (aPostData)
  1.1252 +        {
  1.1253 +            nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
  1.1254 +            if (stream)
  1.1255 +            {
  1.1256 +                // Rewind the postdata stream
  1.1257 +                stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  1.1258 +                nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
  1.1259 +                NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
  1.1260 +                // Attach the postdata to the http channel
  1.1261 +                uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
  1.1262 +            }
  1.1263 +        }
  1.1264 +
  1.1265 +        // Cache key
  1.1266 +        nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
  1.1267 +        if (cacheChannel && cacheKey)
  1.1268 +        {
  1.1269 +            cacheChannel->SetCacheKey(cacheKey);
  1.1270 +        }
  1.1271 +
  1.1272 +        // Headers
  1.1273 +        if (aExtraHeaders)
  1.1274 +        {
  1.1275 +            nsAutoCString oneHeader;
  1.1276 +            nsAutoCString headerName;
  1.1277 +            nsAutoCString headerValue;
  1.1278 +            int32_t crlf = 0;
  1.1279 +            int32_t colon = 0;
  1.1280 +            const char *kWhitespace = "\b\t\r\n ";
  1.1281 +            nsAutoCString extraHeaders(aExtraHeaders);
  1.1282 +            while (true)
  1.1283 +            {
  1.1284 +                crlf = extraHeaders.Find("\r\n", true);
  1.1285 +                if (crlf == -1)
  1.1286 +                    break;
  1.1287 +                extraHeaders.Mid(oneHeader, 0, crlf);
  1.1288 +                extraHeaders.Cut(0, crlf + 2);
  1.1289 +                colon = oneHeader.Find(":");
  1.1290 +                if (colon == -1)
  1.1291 +                    break; // Should have a colon
  1.1292 +                oneHeader.Left(headerName, colon);
  1.1293 +                colon++;
  1.1294 +                oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
  1.1295 +                headerName.Trim(kWhitespace);
  1.1296 +                headerValue.Trim(kWhitespace);
  1.1297 +                // Add the header (merging if required)
  1.1298 +                rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
  1.1299 +                if (NS_FAILED(rv))
  1.1300 +                {
  1.1301 +                    EndDownload(NS_ERROR_FAILURE);
  1.1302 +                    return NS_ERROR_FAILURE;
  1.1303 +                }
  1.1304 +            }
  1.1305 +        }
  1.1306 +    }
  1.1307 +    return SaveChannelInternal(inputChannel, aFile, aCalcFileExt);
  1.1308 +}
  1.1309 +
  1.1310 +nsresult nsWebBrowserPersist::SaveChannelInternal(
  1.1311 +    nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
  1.1312 +{
  1.1313 +    NS_ENSURE_ARG_POINTER(aChannel);
  1.1314 +    NS_ENSURE_ARG_POINTER(aFile);
  1.1315 +
  1.1316 +    // The default behaviour of SaveChannelInternal is to download the source
  1.1317 +    // into a storage stream and upload that to the target. MakeOutputStream
  1.1318 +    // special-cases a file target and creates a file output stream directly.
  1.1319 +    // We want to special-case a file source and create a file input stream,
  1.1320 +    // but we don't need to do this in the case of a file target.
  1.1321 +    nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
  1.1322 +    nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
  1.1323 +    if (fc && !fu) {
  1.1324 +        nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
  1.1325 +        nsresult rv = aChannel->Open(getter_AddRefs(fileInputStream));
  1.1326 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1327 +        rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
  1.1328 +                                       fileInputStream, BUFFERED_OUTPUT_SIZE);
  1.1329 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1330 +        nsAutoCString contentType;
  1.1331 +        aChannel->GetContentType(contentType);
  1.1332 +        return StartUpload(bufferedInputStream, aFile, contentType);
  1.1333 +    }
  1.1334 +
  1.1335 +    // Read from the input channel
  1.1336 +    nsresult rv = aChannel->AsyncOpen(this, nullptr);
  1.1337 +    if (rv == NS_ERROR_NO_CONTENT)
  1.1338 +    {
  1.1339 +        // Assume this is a protocol such as mailto: which does not feed out
  1.1340 +        // data and just ignore it.
  1.1341 +        return NS_SUCCESS_DONT_FIXUP;
  1.1342 +    }
  1.1343 +
  1.1344 +    if (NS_FAILED(rv))
  1.1345 +    {
  1.1346 +        // Opening failed, but do we care?
  1.1347 +        if (mPersistFlags & PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS)
  1.1348 +        {
  1.1349 +            SendErrorStatusChange(true, rv, aChannel, aFile);
  1.1350 +            EndDownload(NS_ERROR_FAILURE);
  1.1351 +            return NS_ERROR_FAILURE;
  1.1352 +        }
  1.1353 +        return NS_SUCCESS_DONT_FIXUP;
  1.1354 +    }
  1.1355 +
  1.1356 +    // Add the output transport to the output map with the channel as the key
  1.1357 +    nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aChannel);
  1.1358 +    mOutputMap.Put(keyPtr, new OutputData(aFile, mURI, aCalcFileExt));
  1.1359 +
  1.1360 +    return NS_OK;
  1.1361 +}
  1.1362 +
  1.1363 +nsresult
  1.1364 +nsWebBrowserPersist::GetExtensionForContentType(const char16_t *aContentType, char16_t **aExt)
  1.1365 +{
  1.1366 +    NS_ENSURE_ARG_POINTER(aContentType);
  1.1367 +    NS_ENSURE_ARG_POINTER(aExt);
  1.1368 +
  1.1369 +    *aExt = nullptr;
  1.1370 +
  1.1371 +    nsresult rv;
  1.1372 +    if (!mMIMEService)
  1.1373 +    {
  1.1374 +        mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
  1.1375 +        NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
  1.1376 +    }
  1.1377 +
  1.1378 +    nsCOMPtr<nsIMIMEInfo> mimeInfo;
  1.1379 +    nsAutoCString contentType;
  1.1380 +    contentType.AssignWithConversion(aContentType);
  1.1381 +    nsAutoCString ext;
  1.1382 +    rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
  1.1383 +    if (NS_SUCCEEDED(rv))
  1.1384 +    {
  1.1385 +        *aExt = UTF8ToNewUnicode(ext);
  1.1386 +        NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
  1.1387 +        return NS_OK;
  1.1388 +    }
  1.1389 +
  1.1390 +    return NS_ERROR_FAILURE;
  1.1391 +}
  1.1392 +
  1.1393 +nsresult
  1.1394 +nsWebBrowserPersist::GetDocumentExtension(nsIDOMDocument *aDocument, char16_t **aExt)
  1.1395 +{
  1.1396 +    NS_ENSURE_ARG_POINTER(aDocument);
  1.1397 +    NS_ENSURE_ARG_POINTER(aExt);
  1.1398 +
  1.1399 +    nsXPIDLString contentType;
  1.1400 +    nsresult rv = GetDocEncoderContentType(aDocument, nullptr, getter_Copies(contentType));
  1.1401 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1402 +    return GetExtensionForContentType(contentType.get(), aExt);
  1.1403 +}
  1.1404 +
  1.1405 +nsresult
  1.1406 +nsWebBrowserPersist::GetDocEncoderContentType(nsIDOMDocument *aDocument, const char16_t *aContentType, char16_t **aRealContentType)
  1.1407 +{
  1.1408 +    NS_ENSURE_ARG_POINTER(aDocument);
  1.1409 +    NS_ENSURE_ARG_POINTER(aRealContentType);
  1.1410 +
  1.1411 +    *aRealContentType = nullptr;
  1.1412 +
  1.1413 +    nsAutoString defaultContentType(NS_LITERAL_STRING("text/html"));
  1.1414 +
  1.1415 +    // Get the desired content type for the document, either by using the one
  1.1416 +    // supplied or from the document itself.
  1.1417 +
  1.1418 +    nsAutoString contentType;
  1.1419 +    if (aContentType)
  1.1420 +    {
  1.1421 +        contentType.Assign(aContentType);
  1.1422 +    }
  1.1423 +    else
  1.1424 +    {
  1.1425 +        // Get the content type from the document
  1.1426 +        nsAutoString type;
  1.1427 +        if (NS_SUCCEEDED(aDocument->GetContentType(type)) && !type.IsEmpty())
  1.1428 +            contentType.Assign(type);
  1.1429 +    }
  1.1430 +
  1.1431 +    // Check that an encoder actually exists for the desired output type. The
  1.1432 +    // following content types will usually yield an encoder.
  1.1433 +    //
  1.1434 +    //   text/xml
  1.1435 +    //   application/xml
  1.1436 +    //   application/xhtml+xml
  1.1437 +    //   image/svg+xml
  1.1438 +    //   text/html
  1.1439 +    //   text/plain
  1.1440 +
  1.1441 +    if (!contentType.IsEmpty() &&
  1.1442 +        !contentType.Equals(defaultContentType, nsCaseInsensitiveStringComparator()))
  1.1443 +    {
  1.1444 +        // Check if there is an encoder for the desired content type
  1.1445 +        nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  1.1446 +        AppendUTF16toUTF8(contentType, contractID);
  1.1447 +
  1.1448 +        nsCOMPtr<nsIComponentRegistrar> registrar;
  1.1449 +        NS_GetComponentRegistrar(getter_AddRefs(registrar));
  1.1450 +        if (registrar)
  1.1451 +        {
  1.1452 +            bool result;
  1.1453 +            nsresult rv = registrar->IsContractIDRegistered(contractID.get(), &result);
  1.1454 +            if (NS_SUCCEEDED(rv) && result)
  1.1455 +            {
  1.1456 +                *aRealContentType = ToNewUnicode(contentType);
  1.1457 +            }
  1.1458 +        }
  1.1459 +    }
  1.1460 +
  1.1461 +    // Use the default if no encoder exists for the desired one
  1.1462 +    if (!*aRealContentType)
  1.1463 +    {
  1.1464 +        *aRealContentType = ToNewUnicode(defaultContentType);
  1.1465 +    }
  1.1466 +
  1.1467 +    NS_ENSURE_TRUE(*aRealContentType, NS_ERROR_OUT_OF_MEMORY);
  1.1468 +
  1.1469 +    return NS_OK;
  1.1470 +}
  1.1471 +
  1.1472 +nsresult nsWebBrowserPersist::SaveDocumentInternal(
  1.1473 +    nsIDOMDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
  1.1474 +{
  1.1475 +    NS_ENSURE_ARG_POINTER(aDocument);
  1.1476 +    NS_ENSURE_ARG_POINTER(aFile);
  1.1477 +
  1.1478 +    // See if we can get the local file representation of this URI
  1.1479 +    nsCOMPtr<nsIFile> localFile;
  1.1480 +    nsresult rv = GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
  1.1481 +
  1.1482 +    nsCOMPtr<nsIFile> localDataPath;
  1.1483 +    if (NS_SUCCEEDED(rv) && aDataPath)
  1.1484 +    {
  1.1485 +        // See if we can get the local file representation of this URI
  1.1486 +        rv = GetLocalFileFromURI(aDataPath, getter_AddRefs(localDataPath));
  1.1487 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1488 +    }
  1.1489 +
  1.1490 +    nsCOMPtr<nsIDOMNode> docAsNode = do_QueryInterface(aDocument);
  1.1491 +
  1.1492 +    // Persist the main document
  1.1493 +    nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDocument));
  1.1494 +    if (!doc) {
  1.1495 +        return NS_ERROR_UNEXPECTED;
  1.1496 +    }
  1.1497 +    mURI = doc->GetDocumentURI();
  1.1498 +
  1.1499 +    nsCOMPtr<nsIURI> oldBaseURI = mCurrentBaseURI;
  1.1500 +    nsAutoCString oldCharset(mCurrentCharset);
  1.1501 +
  1.1502 +    // Store the base URI and the charset
  1.1503 +    mCurrentBaseURI = doc->GetBaseURI();
  1.1504 +    mCurrentCharset = doc->GetDocumentCharacterSet();
  1.1505 +
  1.1506 +    // Does the caller want to fixup the referenced URIs and save those too?
  1.1507 +    if (aDataPath)
  1.1508 +    {
  1.1509 +        // Basic steps are these.
  1.1510 +        //
  1.1511 +        // 1. Iterate through the document (and subdocuments) building a list
  1.1512 +        //    of unique URIs.
  1.1513 +        // 2. For each URI create an OutputData entry and open a channel to save
  1.1514 +        //    it. As each URI is saved, discover the mime type and fix up the
  1.1515 +        //    local filename with the correct extension.
  1.1516 +        // 3. Store the document in a list and wait for URI persistence to finish
  1.1517 +        // 4. After URI persistence completes save the list of documents,
  1.1518 +        //    fixing it up as it goes out to file.
  1.1519 +
  1.1520 +        nsCOMPtr<nsIURI> oldDataPath = mCurrentDataPath;
  1.1521 +        bool oldDataPathIsRelative = mCurrentDataPathIsRelative;
  1.1522 +        nsCString oldCurrentRelativePathToData = mCurrentRelativePathToData;
  1.1523 +        uint32_t oldThingsToPersist = mCurrentThingsToPersist;
  1.1524 +
  1.1525 +        mCurrentDataPathIsRelative = false;
  1.1526 +        mCurrentDataPath = aDataPath;
  1.1527 +        mCurrentRelativePathToData = "";
  1.1528 +        mCurrentThingsToPersist = 0;
  1.1529 +
  1.1530 +        // Determine if the specified data path is relative to the
  1.1531 +        // specified file, (e.g. c:\docs\htmldata is relative to
  1.1532 +        // c:\docs\myfile.htm, but not to d:\foo\data.
  1.1533 +
  1.1534 +        // Starting with the data dir work back through its parents
  1.1535 +        // checking if one of them matches the base directory.
  1.1536 +
  1.1537 +        if (localDataPath && localFile)
  1.1538 +        {
  1.1539 +            nsCOMPtr<nsIFile> baseDir;
  1.1540 +            localFile->GetParent(getter_AddRefs(baseDir));
  1.1541 +
  1.1542 +            nsAutoCString relativePathToData;
  1.1543 +            nsCOMPtr<nsIFile> dataDirParent;
  1.1544 +            dataDirParent = localDataPath;
  1.1545 +            while (dataDirParent)
  1.1546 +            {
  1.1547 +                bool sameDir = false;
  1.1548 +                dataDirParent->Equals(baseDir, &sameDir);
  1.1549 +                if (sameDir)
  1.1550 +                {
  1.1551 +                    mCurrentRelativePathToData = relativePathToData;
  1.1552 +                    mCurrentDataPathIsRelative = true;
  1.1553 +                    break;
  1.1554 +                }
  1.1555 +
  1.1556 +                nsAutoString dirName;
  1.1557 +                dataDirParent->GetLeafName(dirName);
  1.1558 +
  1.1559 +                nsAutoCString newRelativePathToData;
  1.1560 +                newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
  1.1561 +                                      + NS_LITERAL_CSTRING("/")
  1.1562 +                                      + relativePathToData;
  1.1563 +                relativePathToData = newRelativePathToData;
  1.1564 +
  1.1565 +                nsCOMPtr<nsIFile> newDataDirParent;
  1.1566 +                rv = dataDirParent->GetParent(getter_AddRefs(newDataDirParent));
  1.1567 +                dataDirParent = newDataDirParent;
  1.1568 +            }
  1.1569 +        }
  1.1570 +        else
  1.1571 +        {
  1.1572 +            // generate a relative path if possible
  1.1573 +            nsCOMPtr<nsIURL> pathToBaseURL(do_QueryInterface(aFile));
  1.1574 +            if (pathToBaseURL)
  1.1575 +            {
  1.1576 +                nsAutoCString relativePath;  // nsACString
  1.1577 +                if (NS_SUCCEEDED(pathToBaseURL->GetRelativeSpec(aDataPath, relativePath)))
  1.1578 +                {
  1.1579 +                    mCurrentDataPathIsRelative = true;
  1.1580 +                    mCurrentRelativePathToData = relativePath;
  1.1581 +                }
  1.1582 +            }
  1.1583 +        }
  1.1584 +
  1.1585 +        // Store the document in a list so when URI persistence is done and the
  1.1586 +        // filenames of saved URIs are known, the documents can be fixed up and
  1.1587 +        // saved
  1.1588 +
  1.1589 +        DocData *docData = new DocData;
  1.1590 +        docData->mBaseURI = mCurrentBaseURI;
  1.1591 +        docData->mCharset = mCurrentCharset;
  1.1592 +        docData->mDocument = aDocument;
  1.1593 +        docData->mFile = aFile;
  1.1594 +        docData->mRelativePathToData = mCurrentRelativePathToData;
  1.1595 +        docData->mDataPath = mCurrentDataPath;
  1.1596 +        docData->mDataPathIsRelative = mCurrentDataPathIsRelative;
  1.1597 +        mDocList.AppendElement(docData);
  1.1598 +
  1.1599 +        // Walk the DOM gathering a list of externally referenced URIs in the uri map
  1.1600 +        nsCOMPtr<nsIDOMTreeWalker> walker;
  1.1601 +        rv = aDocument->CreateTreeWalker(docAsNode,
  1.1602 +            nsIDOMNodeFilter::SHOW_ELEMENT |
  1.1603 +                nsIDOMNodeFilter::SHOW_DOCUMENT |
  1.1604 +                nsIDOMNodeFilter::SHOW_PROCESSING_INSTRUCTION,
  1.1605 +            nullptr, 1, getter_AddRefs(walker));
  1.1606 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1607 +
  1.1608 +        nsCOMPtr<nsIDOMNode> currentNode;
  1.1609 +        walker->GetCurrentNode(getter_AddRefs(currentNode));
  1.1610 +        while (currentNode)
  1.1611 +        {
  1.1612 +            OnWalkDOMNode(currentNode);
  1.1613 +            walker->NextNode(getter_AddRefs(currentNode));
  1.1614 +        }
  1.1615 +
  1.1616 +        // If there are things to persist, create a directory to hold them
  1.1617 +        if (mCurrentThingsToPersist > 0)
  1.1618 +        {
  1.1619 +            if (localDataPath)
  1.1620 +            {
  1.1621 +                bool exists = false;
  1.1622 +                bool haveDir = false;
  1.1623 +
  1.1624 +                localDataPath->Exists(&exists);
  1.1625 +                if (exists)
  1.1626 +                {
  1.1627 +                    localDataPath->IsDirectory(&haveDir);
  1.1628 +                }
  1.1629 +                if (!haveDir)
  1.1630 +                {
  1.1631 +                    rv = localDataPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
  1.1632 +                    if (NS_SUCCEEDED(rv))
  1.1633 +                        haveDir = true;
  1.1634 +                    else
  1.1635 +                        SendErrorStatusChange(false, rv, nullptr, aFile);
  1.1636 +                }
  1.1637 +                if (!haveDir)
  1.1638 +                {
  1.1639 +                    EndDownload(NS_ERROR_FAILURE);
  1.1640 +                    mCurrentBaseURI = oldBaseURI;
  1.1641 +                    mCurrentCharset = oldCharset;
  1.1642 +                    return NS_ERROR_FAILURE;
  1.1643 +                }
  1.1644 +                if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
  1.1645 +                {
  1.1646 +                    // Add to list of things to delete later if all goes wrong
  1.1647 +                    CleanupData *cleanupData = new CleanupData;
  1.1648 +                    NS_ENSURE_TRUE(cleanupData, NS_ERROR_OUT_OF_MEMORY);
  1.1649 +                    cleanupData->mFile = localDataPath;
  1.1650 +                    cleanupData->mIsDirectory = true;
  1.1651 +                    mCleanupList.AppendElement(cleanupData);
  1.1652 +                }
  1.1653 +            }
  1.1654 +        }
  1.1655 +
  1.1656 +        mCurrentThingsToPersist = oldThingsToPersist;
  1.1657 +        mCurrentDataPath = oldDataPath;
  1.1658 +        mCurrentDataPathIsRelative = oldDataPathIsRelative;
  1.1659 +        mCurrentRelativePathToData = oldCurrentRelativePathToData;
  1.1660 +    }
  1.1661 +    else
  1.1662 +    {
  1.1663 +        // Set the document base to ensure relative links still work
  1.1664 +        SetDocumentBase(aDocument, mCurrentBaseURI);
  1.1665 +
  1.1666 +        // Get the content type to save with
  1.1667 +        nsXPIDLString realContentType;
  1.1668 +        GetDocEncoderContentType(aDocument,
  1.1669 +            !mContentType.IsEmpty() ? mContentType.get() : nullptr,
  1.1670 +            getter_Copies(realContentType));
  1.1671 +
  1.1672 +        nsAutoCString contentType; contentType.AssignWithConversion(realContentType);
  1.1673 +        nsAutoCString charType; // Empty
  1.1674 +
  1.1675 +        // Save the document
  1.1676 +        rv = SaveDocumentWithFixup(
  1.1677 +            aDocument,
  1.1678 +            nullptr,  // no dom fixup
  1.1679 +            aFile,
  1.1680 +            mReplaceExisting,
  1.1681 +            contentType,
  1.1682 +            charType,
  1.1683 +            mEncodingFlags);
  1.1684 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1685 +    }
  1.1686 +
  1.1687 +    mCurrentBaseURI = oldBaseURI;
  1.1688 +    mCurrentCharset = oldCharset;
  1.1689 +
  1.1690 +    return NS_OK;
  1.1691 +}
  1.1692 +
  1.1693 +nsresult nsWebBrowserPersist::SaveDocuments()
  1.1694 +{
  1.1695 +    nsresult rv = NS_OK;
  1.1696 +
  1.1697 +    mStartSaving = true;
  1.1698 +
  1.1699 +    // Iterate through all queued documents, saving them to file and fixing
  1.1700 +    // them up on the way.
  1.1701 +
  1.1702 +    uint32_t i;
  1.1703 +    for (i = 0; i < mDocList.Length(); i++)
  1.1704 +    {
  1.1705 +        DocData *docData = mDocList.ElementAt(i);
  1.1706 +        if (!docData)
  1.1707 +        {
  1.1708 +            rv = NS_ERROR_FAILURE;
  1.1709 +            break;
  1.1710 +        }
  1.1711 +
  1.1712 +        mCurrentBaseURI = docData->mBaseURI;
  1.1713 +        mCurrentCharset = docData->mCharset;
  1.1714 +
  1.1715 +        // Save the document, fixing it up with the new URIs as we do
  1.1716 +
  1.1717 +        nsEncoderNodeFixup *nodeFixup;
  1.1718 +        nodeFixup = new nsEncoderNodeFixup;
  1.1719 +        if (nodeFixup)
  1.1720 +            nodeFixup->mWebBrowserPersist = this;
  1.1721 +
  1.1722 +        // Get the content type
  1.1723 +        nsXPIDLString realContentType;
  1.1724 +        GetDocEncoderContentType(docData->mDocument,
  1.1725 +            !mContentType.IsEmpty() ? mContentType.get() : nullptr,
  1.1726 +            getter_Copies(realContentType));
  1.1727 +
  1.1728 +        nsAutoCString contentType; contentType.AssignWithConversion(realContentType.get());
  1.1729 +        nsAutoCString charType; // Empty
  1.1730 +
  1.1731 +        // Save the document, fixing up the links as it goes out
  1.1732 +        rv = SaveDocumentWithFixup(
  1.1733 +            docData->mDocument,
  1.1734 +            nodeFixup,
  1.1735 +            docData->mFile,
  1.1736 +            mReplaceExisting,
  1.1737 +            contentType,
  1.1738 +            charType,
  1.1739 +            mEncodingFlags);
  1.1740 +
  1.1741 +        if (NS_FAILED(rv))
  1.1742 +            break;
  1.1743 +
  1.1744 +        // if we're serializing, bail after first iteration of loop
  1.1745 +        if (mSerializingOutput)
  1.1746 +            break;
  1.1747 +    }
  1.1748 +
  1.1749 +    // delete, cleanup regardless of errors (bug 132417)
  1.1750 +    for (i = 0; i < mDocList.Length(); i++)
  1.1751 +    {
  1.1752 +        DocData *docData = mDocList.ElementAt(i);
  1.1753 +        delete docData;
  1.1754 +        if (mSerializingOutput)
  1.1755 +        {
  1.1756 +            mDocList.RemoveElementAt(i);
  1.1757 +            break;
  1.1758 +        }
  1.1759 +    }
  1.1760 +
  1.1761 +    if (!mSerializingOutput)
  1.1762 +    {
  1.1763 +        mDocList.Clear();
  1.1764 +    }
  1.1765 +
  1.1766 +    return rv;
  1.1767 +}
  1.1768 +
  1.1769 +void nsWebBrowserPersist::Cleanup()
  1.1770 +{
  1.1771 +    mURIMap.Clear();
  1.1772 +    mOutputMap.EnumerateRead(EnumCleanupOutputMap, this);
  1.1773 +    mOutputMap.Clear();
  1.1774 +    mUploadList.EnumerateRead(EnumCleanupUploadList, this);
  1.1775 +    mUploadList.Clear();
  1.1776 +    uint32_t i;
  1.1777 +    for (i = 0; i < mDocList.Length(); i++)
  1.1778 +    {
  1.1779 +        DocData *docData = mDocList.ElementAt(i);
  1.1780 +        delete docData;
  1.1781 +    }
  1.1782 +    mDocList.Clear();
  1.1783 +    for (i = 0; i < mCleanupList.Length(); i++)
  1.1784 +    {
  1.1785 +        CleanupData *cleanupData = mCleanupList.ElementAt(i);
  1.1786 +        delete cleanupData;
  1.1787 +    }
  1.1788 +    mCleanupList.Clear();
  1.1789 +    mFilenameList.Clear();
  1.1790 +}
  1.1791 +
  1.1792 +void nsWebBrowserPersist::CleanupLocalFiles()
  1.1793 +{
  1.1794 +    // Two passes, the first pass cleans up files, the second pass tests
  1.1795 +    // for and then deletes empty directories. Directories that are not
  1.1796 +    // empty after the first pass must contain files from something else
  1.1797 +    // and are not deleted.
  1.1798 +    int pass;
  1.1799 +    for (pass = 0; pass < 2; pass++)
  1.1800 +    {
  1.1801 +        uint32_t i;
  1.1802 +        for (i = 0; i < mCleanupList.Length(); i++)
  1.1803 +        {
  1.1804 +            CleanupData *cleanupData = mCleanupList.ElementAt(i);
  1.1805 +            nsCOMPtr<nsIFile> file = cleanupData->mFile;
  1.1806 +
  1.1807 +            // Test if the dir / file exists (something in an earlier loop
  1.1808 +            // may have already removed it)
  1.1809 +            bool exists = false;
  1.1810 +            file->Exists(&exists);
  1.1811 +            if (!exists)
  1.1812 +                continue;
  1.1813 +
  1.1814 +            // Test if the file has changed in between creation and deletion
  1.1815 +            // in some way that means it should be ignored
  1.1816 +            bool isDirectory = false;
  1.1817 +            file->IsDirectory(&isDirectory);
  1.1818 +            if (isDirectory != cleanupData->mIsDirectory)
  1.1819 +                continue; // A file has become a dir or vice versa !
  1.1820 +
  1.1821 +            if (pass == 0 && !isDirectory)
  1.1822 +            {
  1.1823 +                file->Remove(false);
  1.1824 +            }
  1.1825 +            else if (pass == 1 && isDirectory) // Directory
  1.1826 +            {
  1.1827 +                // Directories are more complicated. Enumerate through
  1.1828 +                // children looking for files. Any files created by the
  1.1829 +                // persist object would have been deleted by the first
  1.1830 +                // pass so if there are any there at this stage, the dir
  1.1831 +                // cannot be deleted because it has someone else's files
  1.1832 +                // in it. Empty child dirs are deleted but they must be
  1.1833 +                // recursed through to ensure they are actually empty.
  1.1834 +
  1.1835 +                bool isEmptyDirectory = true;
  1.1836 +                nsCOMArray<nsISimpleEnumerator> dirStack;
  1.1837 +                int32_t stackSize = 0;
  1.1838 +
  1.1839 +                // Push the top level enum onto the stack
  1.1840 +                nsCOMPtr<nsISimpleEnumerator> pos;
  1.1841 +                if (NS_SUCCEEDED(file->GetDirectoryEntries(getter_AddRefs(pos))))
  1.1842 +                    dirStack.AppendObject(pos);
  1.1843 +
  1.1844 +                while (isEmptyDirectory && (stackSize = dirStack.Count()))
  1.1845 +                {
  1.1846 +                    // Pop the last element
  1.1847 +                    nsCOMPtr<nsISimpleEnumerator> curPos;
  1.1848 +                    curPos = dirStack[stackSize-1];
  1.1849 +                    dirStack.RemoveObjectAt(stackSize - 1);
  1.1850 +
  1.1851 +                    // Test if the enumerator has any more files in it
  1.1852 +                    bool hasMoreElements = false;
  1.1853 +                    curPos->HasMoreElements(&hasMoreElements);
  1.1854 +                    if (!hasMoreElements)
  1.1855 +                    {
  1.1856 +                        continue;
  1.1857 +                    }
  1.1858 +
  1.1859 +                    // Child files automatically make this code drop out,
  1.1860 +                    // while child dirs keep the loop going.
  1.1861 +                    nsCOMPtr<nsISupports> child;
  1.1862 +                    curPos->GetNext(getter_AddRefs(child));
  1.1863 +                    NS_ASSERTION(child, "No child element, but hasMoreElements says otherwise");
  1.1864 +                    if (!child)
  1.1865 +                        continue;
  1.1866 +                    nsCOMPtr<nsIFile> childAsFile = do_QueryInterface(child);
  1.1867 +                    NS_ASSERTION(childAsFile, "This should be a file but isn't");
  1.1868 +
  1.1869 +                    bool childIsSymlink = false;
  1.1870 +                    childAsFile->IsSymlink(&childIsSymlink);
  1.1871 +                    bool childIsDir = false;
  1.1872 +                    childAsFile->IsDirectory(&childIsDir);
  1.1873 +                    if (!childIsDir || childIsSymlink)
  1.1874 +                    {
  1.1875 +                        // Some kind of file or symlink which means dir
  1.1876 +                        // is not empty so just drop out.
  1.1877 +                        isEmptyDirectory = false;
  1.1878 +                        break;
  1.1879 +                    }
  1.1880 +                    // Push parent enumerator followed by child enumerator
  1.1881 +                    nsCOMPtr<nsISimpleEnumerator> childPos;
  1.1882 +                    childAsFile->GetDirectoryEntries(getter_AddRefs(childPos));
  1.1883 +                    dirStack.AppendObject(curPos);
  1.1884 +                    if (childPos)
  1.1885 +                        dirStack.AppendObject(childPos);
  1.1886 +
  1.1887 +                }
  1.1888 +                dirStack.Clear();
  1.1889 +
  1.1890 +                // If after all that walking the dir is deemed empty, delete it
  1.1891 +                if (isEmptyDirectory)
  1.1892 +                {
  1.1893 +                    file->Remove(true);
  1.1894 +                }
  1.1895 +            }
  1.1896 +        }
  1.1897 +    }
  1.1898 +}
  1.1899 +
  1.1900 +nsresult
  1.1901 +nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
  1.1902 +{
  1.1903 +    nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  1.1904 +    NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
  1.1905 +
  1.1906 +    bool nameHasChanged = false;
  1.1907 +    nsresult rv;
  1.1908 +
  1.1909 +    // Get the old filename
  1.1910 +    nsAutoCString filename;
  1.1911 +    rv = url->GetFileName(filename);
  1.1912 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1913 +    nsAutoCString directory;
  1.1914 +    rv = url->GetDirectory(directory);
  1.1915 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.1916 +
  1.1917 +    // Split the filename into a base and an extension.
  1.1918 +    // e.g. "foo.html" becomes "foo" & ".html"
  1.1919 +    //
  1.1920 +    // The nsIURL methods GetFileBaseName & GetFileExtension don't
  1.1921 +    // preserve the dot whereas this code does to save some effort
  1.1922 +    // later when everything is put back together.
  1.1923 +    int32_t lastDot = filename.RFind(".");
  1.1924 +    nsAutoCString base;
  1.1925 +    nsAutoCString ext;
  1.1926 +    if (lastDot >= 0)
  1.1927 +    {
  1.1928 +        filename.Mid(base, 0, lastDot);
  1.1929 +        filename.Mid(ext, lastDot, filename.Length() - lastDot); // includes dot
  1.1930 +    }
  1.1931 +    else
  1.1932 +    {
  1.1933 +        // filename contains no dot
  1.1934 +        base = filename;
  1.1935 +    }
  1.1936 +
  1.1937 +    // Test if the filename is longer than allowed by the OS
  1.1938 +    int32_t needToChop = filename.Length() - kDefaultMaxFilenameLength;
  1.1939 +    if (needToChop > 0)
  1.1940 +    {
  1.1941 +        // Truncate the base first and then the ext if necessary
  1.1942 +        if (base.Length() > (uint32_t) needToChop)
  1.1943 +        {
  1.1944 +            base.Truncate(base.Length() - needToChop);
  1.1945 +        }
  1.1946 +        else
  1.1947 +        {
  1.1948 +            needToChop -= base.Length() - 1;
  1.1949 +            base.Truncate(1);
  1.1950 +            if (ext.Length() > (uint32_t) needToChop)
  1.1951 +            {
  1.1952 +                ext.Truncate(ext.Length() - needToChop);
  1.1953 +            }
  1.1954 +            else
  1.1955 +            {
  1.1956 +                ext.Truncate(0);
  1.1957 +            }
  1.1958 +            // If kDefaultMaxFilenameLength were 1 we'd be in trouble here,
  1.1959 +            // but that won't happen because it will be set to a sensible
  1.1960 +            // value.
  1.1961 +        }
  1.1962 +
  1.1963 +        filename.Assign(base);
  1.1964 +        filename.Append(ext);
  1.1965 +        nameHasChanged = true;
  1.1966 +    }
  1.1967 +
  1.1968 +    // Ensure the filename is unique
  1.1969 +    // Create a filename if it's empty, or if the filename / datapath is
  1.1970 +    // already taken by another URI and create an alternate name.
  1.1971 +
  1.1972 +    if (base.IsEmpty() || !mFilenameList.IsEmpty())
  1.1973 +    {
  1.1974 +        nsAutoCString tmpPath;
  1.1975 +        nsAutoCString tmpBase;
  1.1976 +        uint32_t duplicateCounter = 1;
  1.1977 +        while (1)
  1.1978 +        {
  1.1979 +            // Make a file name,
  1.1980 +            // Foo become foo_001, foo_002, etc.
  1.1981 +            // Empty files become _001, _002 etc.
  1.1982 +
  1.1983 +            if (base.IsEmpty() || duplicateCounter > 1)
  1.1984 +            {
  1.1985 +                char * tmp = PR_smprintf("_%03d", duplicateCounter);
  1.1986 +                NS_ENSURE_TRUE(tmp, NS_ERROR_OUT_OF_MEMORY);
  1.1987 +                if (filename.Length() < kDefaultMaxFilenameLength - 4)
  1.1988 +                {
  1.1989 +                    tmpBase = base;
  1.1990 +                }
  1.1991 +                else
  1.1992 +                {
  1.1993 +                    base.Mid(tmpBase, 0, base.Length() - 4);
  1.1994 +                }
  1.1995 +                tmpBase.Append(tmp);
  1.1996 +                PR_smprintf_free(tmp);
  1.1997 +            }
  1.1998 +            else
  1.1999 +            {
  1.2000 +                tmpBase = base;
  1.2001 +            }
  1.2002 +
  1.2003 +            tmpPath.Assign(directory);
  1.2004 +            tmpPath.Append(tmpBase);
  1.2005 +            tmpPath.Append(ext);
  1.2006 +
  1.2007 +            // Test if the name is a duplicate
  1.2008 +            if (!mFilenameList.Contains(tmpPath))
  1.2009 +            {
  1.2010 +                if (!base.Equals(tmpBase))
  1.2011 +                {
  1.2012 +                    filename.Assign(tmpBase);
  1.2013 +                    filename.Append(ext);
  1.2014 +                    nameHasChanged = true;
  1.2015 +                }
  1.2016 +                break;
  1.2017 +            }
  1.2018 +            duplicateCounter++;
  1.2019 +        }
  1.2020 +    }
  1.2021 +
  1.2022 +    // Add name to list of those already used
  1.2023 +    nsAutoCString newFilepath(directory);
  1.2024 +    newFilepath.Append(filename);
  1.2025 +    mFilenameList.AppendElement(newFilepath);
  1.2026 +
  1.2027 +    // Update the uri accordingly if the filename actually changed
  1.2028 +    if (nameHasChanged)
  1.2029 +    {
  1.2030 +        // Final sanity test
  1.2031 +        if (filename.Length() > kDefaultMaxFilenameLength)
  1.2032 +        {
  1.2033 +            NS_WARNING("Filename wasn't truncated less than the max file length - how can that be?");
  1.2034 +            return NS_ERROR_FAILURE;
  1.2035 +        }
  1.2036 +
  1.2037 +        nsCOMPtr<nsIFile> localFile;
  1.2038 +        GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  1.2039 +
  1.2040 +        if (localFile)
  1.2041 +        {
  1.2042 +            nsAutoString filenameAsUnichar;
  1.2043 +            filenameAsUnichar.AssignWithConversion(filename.get());
  1.2044 +            localFile->SetLeafName(filenameAsUnichar);
  1.2045 +
  1.2046 +            // Resync the URI with the file after the extension has been appended
  1.2047 +            nsresult rv;
  1.2048 +            nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  1.2049 +            NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.2050 +            fileURL->SetFile(localFile);  // this should recalculate uri
  1.2051 +        }
  1.2052 +        else
  1.2053 +        {
  1.2054 +            url->SetFileName(filename);
  1.2055 +        }
  1.2056 +    }
  1.2057 +
  1.2058 +    return NS_OK;
  1.2059 +}
  1.2060 +
  1.2061 +
  1.2062 +nsresult
  1.2063 +nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
  1.2064 +{
  1.2065 +    // Try to get filename from the URI.
  1.2066 +    nsAutoString fileName;
  1.2067 +
  1.2068 +    // Get a suggested file name from the URL but strip it of characters
  1.2069 +    // likely to cause the name to be illegal.
  1.2070 +
  1.2071 +    nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  1.2072 +    if (url)
  1.2073 +    {
  1.2074 +        nsAutoCString nameFromURL;
  1.2075 +        url->GetFileName(nameFromURL);
  1.2076 +        if (mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES)
  1.2077 +        {
  1.2078 +            fileName.AssignWithConversion(NS_UnescapeURL(nameFromURL).get());
  1.2079 +            goto end;
  1.2080 +        }
  1.2081 +        if (!nameFromURL.IsEmpty())
  1.2082 +        {
  1.2083 +            // Unescape the file name (GetFileName escapes it)
  1.2084 +            NS_UnescapeURL(nameFromURL);
  1.2085 +            uint32_t nameLength = 0;
  1.2086 +            const char *p = nameFromURL.get();
  1.2087 +            for (;*p && *p != ';' && *p != '?' && *p != '#' && *p != '.'
  1.2088 +                 ;p++)
  1.2089 +            {
  1.2090 +                if (nsCRT::IsAsciiAlpha(*p) || nsCRT::IsAsciiDigit(*p)
  1.2091 +                    || *p == '.' || *p == '-' ||  *p == '_' || (*p == ' '))
  1.2092 +                {
  1.2093 +                    fileName.Append(char16_t(*p));
  1.2094 +                    if (++nameLength == kDefaultMaxFilenameLength)
  1.2095 +                    {
  1.2096 +                        // Note:
  1.2097 +                        // There is no point going any further since it will be
  1.2098 +                        // truncated in CalculateUniqueFilename anyway.
  1.2099 +                        // More importantly, certain implementations of
  1.2100 +                        // nsIFile (e.g. the Mac impl) might truncate
  1.2101 +                        // names in undesirable ways, such as truncating from
  1.2102 +                        // the middle, inserting ellipsis and so on.
  1.2103 +                        break;
  1.2104 +                    }
  1.2105 +                }
  1.2106 +            }
  1.2107 +        }
  1.2108 +    }
  1.2109 +
  1.2110 +    // Empty filenames can confuse the local file object later
  1.2111 +    // when it attempts to set the leaf name in CalculateUniqueFilename
  1.2112 +    // for duplicates and ends up replacing the parent dir. To avoid
  1.2113 +    // the problem, all filenames are made at least one character long.
  1.2114 +    if (fileName.IsEmpty())
  1.2115 +    {
  1.2116 +        fileName.Append(char16_t('a')); // 'a' is for arbitrary
  1.2117 +    }
  1.2118 +
  1.2119 +end:
  1.2120 +    aFilename = fileName;
  1.2121 +    return NS_OK;
  1.2122 +}
  1.2123 +
  1.2124 +
  1.2125 +nsresult
  1.2126 +nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
  1.2127 +{
  1.2128 +    nsresult rv;
  1.2129 +
  1.2130 +    if (!mMIMEService)
  1.2131 +    {
  1.2132 +        mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
  1.2133 +        NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
  1.2134 +    }
  1.2135 +
  1.2136 +    nsAutoCString contentType;
  1.2137 +
  1.2138 +    // Get the content type from the channel
  1.2139 +    aChannel->GetContentType(contentType);
  1.2140 +
  1.2141 +    // Get the content type from the MIME service
  1.2142 +    if (contentType.IsEmpty())
  1.2143 +    {
  1.2144 +        nsCOMPtr<nsIURI> uri;
  1.2145 +        aChannel->GetOriginalURI(getter_AddRefs(uri));
  1.2146 +        mMIMEService->GetTypeFromURI(uri, contentType);
  1.2147 +    }
  1.2148 +
  1.2149 +    // Append the extension onto the file
  1.2150 +    if (!contentType.IsEmpty())
  1.2151 +    {
  1.2152 +        nsCOMPtr<nsIMIMEInfo> mimeInfo;
  1.2153 +        mMIMEService->GetFromTypeAndExtension(
  1.2154 +            contentType, EmptyCString(), getter_AddRefs(mimeInfo));
  1.2155 +
  1.2156 +        nsCOMPtr<nsIFile> localFile;
  1.2157 +        GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  1.2158 +
  1.2159 +        if (mimeInfo)
  1.2160 +        {
  1.2161 +            nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  1.2162 +            NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
  1.2163 +
  1.2164 +            nsAutoCString newFileName;
  1.2165 +            url->GetFileName(newFileName);
  1.2166 +
  1.2167 +            // Test if the current extension is current for the mime type
  1.2168 +            bool hasExtension = false;
  1.2169 +            int32_t ext = newFileName.RFind(".");
  1.2170 +            if (ext != -1)
  1.2171 +            {
  1.2172 +                mimeInfo->ExtensionExists(Substring(newFileName, ext + 1), &hasExtension);
  1.2173 +            }
  1.2174 +
  1.2175 +            // Append the mime file extension
  1.2176 +            nsAutoCString fileExt;
  1.2177 +            if (!hasExtension)
  1.2178 +            {
  1.2179 +                // Test if previous extension is acceptable
  1.2180 +                nsCOMPtr<nsIURL> oldurl(do_QueryInterface(aOriginalURIWithExtension));
  1.2181 +                NS_ENSURE_TRUE(oldurl, NS_ERROR_FAILURE);
  1.2182 +                oldurl->GetFileExtension(fileExt);
  1.2183 +                bool useOldExt = false;
  1.2184 +                if (!fileExt.IsEmpty())
  1.2185 +                {
  1.2186 +                    mimeInfo->ExtensionExists(fileExt, &useOldExt);
  1.2187 +                }
  1.2188 +
  1.2189 +                // can't use old extension so use primary extension
  1.2190 +                if (!useOldExt)
  1.2191 +                {
  1.2192 +                    mimeInfo->GetPrimaryExtension(fileExt);
  1.2193 +                }
  1.2194 +
  1.2195 +                if (!fileExt.IsEmpty())
  1.2196 +                {
  1.2197 +                    uint32_t newLength = newFileName.Length() + fileExt.Length() + 1;
  1.2198 +                    if (newLength > kDefaultMaxFilenameLength)
  1.2199 +                    {
  1.2200 +                        if (fileExt.Length() > kDefaultMaxFilenameLength/2)
  1.2201 +                            fileExt.Truncate(kDefaultMaxFilenameLength/2);
  1.2202 +
  1.2203 +                        uint32_t diff = kDefaultMaxFilenameLength - 1 -
  1.2204 +                                        fileExt.Length();
  1.2205 +                        if (newFileName.Length() > diff)
  1.2206 +                            newFileName.Truncate(diff);
  1.2207 +                    }
  1.2208 +                    newFileName.Append(".");
  1.2209 +                    newFileName.Append(fileExt);
  1.2210 +                }
  1.2211 +
  1.2212 +                if (localFile)
  1.2213 +                {
  1.2214 +                    localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
  1.2215 +
  1.2216 +                    // Resync the URI with the file after the extension has been appended
  1.2217 +                    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  1.2218 +                    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.2219 +                    fileURL->SetFile(localFile);  // this should recalculate uri
  1.2220 +                }
  1.2221 +                else
  1.2222 +                {
  1.2223 +                    url->SetFileName(newFileName);
  1.2224 +                }
  1.2225 +            }
  1.2226 +
  1.2227 +        }
  1.2228 +    }
  1.2229 +
  1.2230 +    return NS_OK;
  1.2231 +}
  1.2232 +
  1.2233 +nsresult
  1.2234 +nsWebBrowserPersist::MakeOutputStream(
  1.2235 +    nsIURI *aURI, nsIOutputStream **aOutputStream)
  1.2236 +{
  1.2237 +    nsresult rv;
  1.2238 +
  1.2239 +    nsCOMPtr<nsIFile> localFile;
  1.2240 +    GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  1.2241 +    if (localFile)
  1.2242 +        rv = MakeOutputStreamFromFile(localFile, aOutputStream);
  1.2243 +    else
  1.2244 +        rv = MakeOutputStreamFromURI(aURI, aOutputStream);
  1.2245 +
  1.2246 +    return rv;
  1.2247 +}
  1.2248 +
  1.2249 +nsresult
  1.2250 +nsWebBrowserPersist::MakeOutputStreamFromFile(
  1.2251 +    nsIFile *aFile, nsIOutputStream **aOutputStream)
  1.2252 +{
  1.2253 +    nsresult rv = NS_OK;
  1.2254 +
  1.2255 +    nsCOMPtr<nsIFileOutputStream> fileOutputStream =
  1.2256 +        do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
  1.2257 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.2258 +
  1.2259 +    // XXX brade:  get the right flags here!
  1.2260 +    int32_t ioFlags = -1;
  1.2261 +    if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE)
  1.2262 +      ioFlags = PR_APPEND | PR_CREATE_FILE | PR_WRONLY;
  1.2263 +    rv = fileOutputStream->Init(aFile, ioFlags, -1, 0);
  1.2264 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2265 +
  1.2266 +    *aOutputStream = NS_BufferOutputStream(fileOutputStream,
  1.2267 +                                           BUFFERED_OUTPUT_SIZE).take();
  1.2268 +
  1.2269 +    if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
  1.2270 +    {
  1.2271 +        // Add to cleanup list in event of failure
  1.2272 +        CleanupData *cleanupData = new CleanupData;
  1.2273 +        if (!cleanupData) {
  1.2274 +          NS_RELEASE(*aOutputStream);
  1.2275 +          return NS_ERROR_OUT_OF_MEMORY;
  1.2276 +        }
  1.2277 +        cleanupData->mFile = aFile;
  1.2278 +        cleanupData->mIsDirectory = false;
  1.2279 +        mCleanupList.AppendElement(cleanupData);
  1.2280 +    }
  1.2281 +
  1.2282 +    return NS_OK;
  1.2283 +}
  1.2284 +
  1.2285 +nsresult
  1.2286 +nsWebBrowserPersist::MakeOutputStreamFromURI(
  1.2287 +    nsIURI *aURI, nsIOutputStream  **aOutputStream)
  1.2288 +{
  1.2289 +    uint32_t segsize = 8192;
  1.2290 +    uint32_t maxsize = uint32_t(-1);
  1.2291 +    nsCOMPtr<nsIStorageStream> storStream;
  1.2292 +    nsresult rv = NS_NewStorageStream(segsize, maxsize, getter_AddRefs(storStream));
  1.2293 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2294 +
  1.2295 +    NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
  1.2296 +    return NS_OK;
  1.2297 +}
  1.2298 +
  1.2299 +void
  1.2300 +nsWebBrowserPersist::EndDownload(nsresult aResult)
  1.2301 +{
  1.2302 +    // Store the error code in the result if it is an error
  1.2303 +    if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(aResult))
  1.2304 +    {
  1.2305 +        mPersistResult = aResult;
  1.2306 +    }
  1.2307 +
  1.2308 +    // Do file cleanup if required
  1.2309 +    if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
  1.2310 +    {
  1.2311 +        CleanupLocalFiles();
  1.2312 +    }
  1.2313 +
  1.2314 +    // Cleanup the channels
  1.2315 +    mCompleted = true;
  1.2316 +    Cleanup();
  1.2317 +}
  1.2318 +
  1.2319 +struct MOZ_STACK_CLASS FixRedirectData
  1.2320 +{
  1.2321 +    nsCOMPtr<nsIChannel> mNewChannel;
  1.2322 +    nsCOMPtr<nsIURI> mOriginalURI;
  1.2323 +    nsCOMPtr<nsISupports> mMatchingKey;
  1.2324 +};
  1.2325 +
  1.2326 +nsresult
  1.2327 +nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
  1.2328 +{
  1.2329 +    NS_ENSURE_ARG_POINTER(aNewChannel);
  1.2330 +    nsCOMPtr<nsIURI> originalURI;
  1.2331 +
  1.2332 +    // Enumerate through existing open channels looking for one with
  1.2333 +    // a URI matching the one specified.
  1.2334 +
  1.2335 +    FixRedirectData data;
  1.2336 +    data.mNewChannel = aNewChannel;
  1.2337 +    data.mNewChannel->GetOriginalURI(getter_AddRefs(data.mOriginalURI));
  1.2338 +    mOutputMap.EnumerateRead(EnumFixRedirect, &data);
  1.2339 +
  1.2340 +    // If a match is found, remove the data entry with the old channel key
  1.2341 +    // and re-add it with the new channel key.
  1.2342 +
  1.2343 +    if (data.mMatchingKey)
  1.2344 +    {
  1.2345 +        nsAutoPtr<OutputData> outputData;
  1.2346 +        mOutputMap.RemoveAndForget(data.mMatchingKey, outputData);
  1.2347 +        NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
  1.2348 +
  1.2349 +        // Store data again with new channel unless told to ignore redirects
  1.2350 +        if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA))
  1.2351 +        {
  1.2352 +            nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
  1.2353 +            mOutputMap.Put(keyPtr, outputData.forget());
  1.2354 +        }
  1.2355 +    }
  1.2356 +
  1.2357 +    return NS_OK;
  1.2358 +}
  1.2359 +
  1.2360 +PLDHashOperator
  1.2361 +nsWebBrowserPersist::EnumFixRedirect(nsISupports *aKey, OutputData *aData, void* aClosure)
  1.2362 +{
  1.2363 +    FixRedirectData *data = static_cast<FixRedirectData*>(aClosure);
  1.2364 +
  1.2365 +    nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(aKey);
  1.2366 +    nsCOMPtr<nsIURI> thisURI;
  1.2367 +
  1.2368 +    thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
  1.2369 +
  1.2370 +    // Compare this channel's URI to the one passed in.
  1.2371 +    bool matchingURI = false;
  1.2372 +    thisURI->Equals(data->mOriginalURI, &matchingURI);
  1.2373 +    if (matchingURI)
  1.2374 +    {
  1.2375 +        data->mMatchingKey = aKey;
  1.2376 +        return PL_DHASH_STOP;
  1.2377 +    }
  1.2378 +
  1.2379 +    return PL_DHASH_NEXT;
  1.2380 +}
  1.2381 +
  1.2382 +void
  1.2383 +nsWebBrowserPersist::CalcTotalProgress()
  1.2384 +{
  1.2385 +    mTotalCurrentProgress = 0;
  1.2386 +    mTotalMaxProgress = 0;
  1.2387 +
  1.2388 +    if (mOutputMap.Count() > 0)
  1.2389 +    {
  1.2390 +        // Total up the progress of each output stream
  1.2391 +        mOutputMap.EnumerateRead(EnumCalcProgress, this);
  1.2392 +    }
  1.2393 +
  1.2394 +    if (mUploadList.Count() > 0)
  1.2395 +    {
  1.2396 +        // Total up the progress of each upload
  1.2397 +        mUploadList.EnumerateRead(EnumCalcUploadProgress, this);
  1.2398 +    }
  1.2399 +
  1.2400 +    // XXX this code seems pretty bogus and pointless
  1.2401 +    if (mTotalCurrentProgress == 0 && mTotalMaxProgress == 0)
  1.2402 +    {
  1.2403 +        // No output streams so we must be complete
  1.2404 +        mTotalCurrentProgress = 10000;
  1.2405 +        mTotalMaxProgress = 10000;
  1.2406 +    }
  1.2407 +}
  1.2408 +
  1.2409 +PLDHashOperator
  1.2410 +nsWebBrowserPersist::EnumCalcProgress(nsISupports *aKey, OutputData *aData, void* aClosure)
  1.2411 +{
  1.2412 +    nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  1.2413 +
  1.2414 +    // only count toward total progress if destination file is local
  1.2415 +    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aData->mFile);
  1.2416 +    if (fileURL)
  1.2417 +    {
  1.2418 +        pthis->mTotalCurrentProgress += aData->mSelfProgress;
  1.2419 +        pthis->mTotalMaxProgress += aData->mSelfProgressMax;
  1.2420 +    }
  1.2421 +    return PL_DHASH_NEXT;
  1.2422 +}
  1.2423 +
  1.2424 +PLDHashOperator
  1.2425 +nsWebBrowserPersist::EnumCalcUploadProgress(nsISupports *aKey, UploadData *aData, void* aClosure)
  1.2426 +{
  1.2427 +    if (aData && aClosure)
  1.2428 +    {
  1.2429 +        nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  1.2430 +        pthis->mTotalCurrentProgress += aData->mSelfProgress;
  1.2431 +        pthis->mTotalMaxProgress += aData->mSelfProgressMax;
  1.2432 +    }
  1.2433 +    return PL_DHASH_NEXT;
  1.2434 +}
  1.2435 +
  1.2436 +PLDHashOperator
  1.2437 +nsWebBrowserPersist::EnumCountURIsToPersist(const nsACString &aKey, URIData *aData, void* aClosure)
  1.2438 +{
  1.2439 +    uint32_t *count = static_cast<uint32_t*>(aClosure);
  1.2440 +    if (aData->mNeedsPersisting && !aData->mSaved)
  1.2441 +    {
  1.2442 +        (*count)++;
  1.2443 +    }
  1.2444 +    return PL_DHASH_NEXT;
  1.2445 +}
  1.2446 +
  1.2447 +PLDHashOperator
  1.2448 +nsWebBrowserPersist::EnumPersistURIs(const nsACString &aKey, URIData *aData, void* aClosure)
  1.2449 +{
  1.2450 +    if (!aData->mNeedsPersisting || aData->mSaved)
  1.2451 +    {
  1.2452 +        return PL_DHASH_NEXT;
  1.2453 +    }
  1.2454 +
  1.2455 +    nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  1.2456 +    nsresult rv;
  1.2457 +
  1.2458 +    // Create a URI from the key
  1.2459 +    nsAutoCString key = nsAutoCString(aKey);
  1.2460 +    nsCOMPtr<nsIURI> uri;
  1.2461 +    rv = NS_NewURI(getter_AddRefs(uri),
  1.2462 +                   nsDependentCString(key.get(), key.Length()),
  1.2463 +                   aData->mCharset.get());
  1.2464 +    NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  1.2465 +
  1.2466 +    // Make a URI to save the data to
  1.2467 +    nsCOMPtr<nsIURI> fileAsURI;
  1.2468 +    rv = aData->mDataPath->Clone(getter_AddRefs(fileAsURI));
  1.2469 +    NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  1.2470 +    rv = pthis->AppendPathToURI(fileAsURI, aData->mFilename);
  1.2471 +    NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  1.2472 +
  1.2473 +    rv = pthis->SaveURIInternal(uri, nullptr, nullptr, nullptr, nullptr, fileAsURI, true,
  1.2474 +                                pthis->mIsPrivate);
  1.2475 +    // if SaveURIInternal fails, then it will have called EndDownload,
  1.2476 +    // which means that |aData| is no longer valid memory.  we MUST bail.
  1.2477 +    NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  1.2478 +
  1.2479 +    if (rv == NS_OK)
  1.2480 +    {
  1.2481 +        // Store the actual object because once it's persisted this
  1.2482 +        // will be fixed up with the right file extension.
  1.2483 +
  1.2484 +        aData->mFile = fileAsURI;
  1.2485 +        aData->mSaved = true;
  1.2486 +    }
  1.2487 +    else
  1.2488 +    {
  1.2489 +        aData->mNeedsFixup = false;
  1.2490 +    }
  1.2491 +
  1.2492 +    if (pthis->mSerializingOutput)
  1.2493 +        return PL_DHASH_STOP;
  1.2494 +
  1.2495 +    return PL_DHASH_NEXT;
  1.2496 +}
  1.2497 +
  1.2498 +PLDHashOperator
  1.2499 +nsWebBrowserPersist::EnumCleanupOutputMap(nsISupports *aKey, OutputData *aData, void* aClosure)
  1.2500 +{
  1.2501 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
  1.2502 +    if (channel)
  1.2503 +    {
  1.2504 +        channel->Cancel(NS_BINDING_ABORTED);
  1.2505 +    }
  1.2506 +    return PL_DHASH_NEXT;
  1.2507 +}
  1.2508 +
  1.2509 +PLDHashOperator
  1.2510 +nsWebBrowserPersist::EnumCleanupUploadList(nsISupports *aKey, UploadData *aData, void* aClosure)
  1.2511 +{
  1.2512 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
  1.2513 +    if (channel)
  1.2514 +    {
  1.2515 +        channel->Cancel(NS_BINDING_ABORTED);
  1.2516 +    }
  1.2517 +    return PL_DHASH_NEXT;
  1.2518 +}
  1.2519 +
  1.2520 +nsresult nsWebBrowserPersist::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, const nsAString &aHref)
  1.2521 +{
  1.2522 +    NS_ENSURE_ARG_POINTER(aPI);
  1.2523 +    nsresult rv = NS_OK;
  1.2524 +
  1.2525 +    nsAutoString data;
  1.2526 +    rv = aPI->GetData(data);
  1.2527 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.2528 +
  1.2529 +    nsAutoString href;
  1.2530 +    nsContentUtils::GetPseudoAttributeValue(data,
  1.2531 +                                            nsGkAtoms::href,
  1.2532 +                                            href);
  1.2533 +
  1.2534 +    // Construct and set a new data value for the xml-stylesheet
  1.2535 +    if (!aHref.IsEmpty() && !href.IsEmpty())
  1.2536 +    {
  1.2537 +        nsAutoString alternate;
  1.2538 +        nsAutoString charset;
  1.2539 +        nsAutoString title;
  1.2540 +        nsAutoString type;
  1.2541 +        nsAutoString media;
  1.2542 +
  1.2543 +        nsContentUtils::GetPseudoAttributeValue(data,
  1.2544 +                                                nsGkAtoms::alternate,
  1.2545 +                                                alternate);
  1.2546 +        nsContentUtils::GetPseudoAttributeValue(data,
  1.2547 +                                                nsGkAtoms::charset,
  1.2548 +                                                charset);
  1.2549 +        nsContentUtils::GetPseudoAttributeValue(data,
  1.2550 +                                                nsGkAtoms::title,
  1.2551 +                                                title);
  1.2552 +        nsContentUtils::GetPseudoAttributeValue(data,
  1.2553 +                                                nsGkAtoms::type,
  1.2554 +                                                type);
  1.2555 +        nsContentUtils::GetPseudoAttributeValue(data,
  1.2556 +                                                nsGkAtoms::media,
  1.2557 +                                                media);
  1.2558 +
  1.2559 +        NS_NAMED_LITERAL_STRING(kCloseAttr, "\" ");
  1.2560 +        nsAutoString newData;
  1.2561 +        newData += NS_LITERAL_STRING("href=\"") + aHref + kCloseAttr;
  1.2562 +        if (!title.IsEmpty())
  1.2563 +        {
  1.2564 +            newData += NS_LITERAL_STRING("title=\"") + title + kCloseAttr;
  1.2565 +        }
  1.2566 +        if (!media.IsEmpty())
  1.2567 +        {
  1.2568 +            newData += NS_LITERAL_STRING("media=\"") + media + kCloseAttr;
  1.2569 +        }
  1.2570 +        if (!type.IsEmpty())
  1.2571 +        {
  1.2572 +            newData += NS_LITERAL_STRING("type=\"") + type + kCloseAttr;
  1.2573 +        }
  1.2574 +        if (!charset.IsEmpty())
  1.2575 +        {
  1.2576 +            newData += NS_LITERAL_STRING("charset=\"") + charset + kCloseAttr;
  1.2577 +        }
  1.2578 +        if (!alternate.IsEmpty())
  1.2579 +        {
  1.2580 +            newData += NS_LITERAL_STRING("alternate=\"") + alternate + kCloseAttr;
  1.2581 +        }
  1.2582 +        newData.Truncate(newData.Length() - 1);  // Remove the extra space on the end.
  1.2583 +        aPI->SetData(newData);
  1.2584 +    }
  1.2585 +
  1.2586 +    return rv;
  1.2587 +}
  1.2588 +
  1.2589 +nsresult nsWebBrowserPersist::GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
  1.2590 +{
  1.2591 +    NS_ENSURE_ARG_POINTER(aPI);
  1.2592 +
  1.2593 +    nsresult rv = NS_OK;
  1.2594 +    nsAutoString data;
  1.2595 +    rv = aPI->GetData(data);
  1.2596 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.2597 +
  1.2598 +    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
  1.2599 +
  1.2600 +    return NS_OK;
  1.2601 +}
  1.2602 +
  1.2603 +nsresult nsWebBrowserPersist::OnWalkDOMNode(nsIDOMNode *aNode)
  1.2604 +{
  1.2605 +    // Fixup xml-stylesheet processing instructions
  1.2606 +    nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNode);
  1.2607 +    if (nodeAsPI)
  1.2608 +    {
  1.2609 +        nsAutoString target;
  1.2610 +        nodeAsPI->GetTarget(target);
  1.2611 +        if (target.EqualsLiteral("xml-stylesheet"))
  1.2612 +        {
  1.2613 +            nsAutoString href;
  1.2614 +            GetXMLStyleSheetLink(nodeAsPI, href);
  1.2615 +            if (!href.IsEmpty())
  1.2616 +            {
  1.2617 +                StoreURI(NS_ConvertUTF16toUTF8(href).get());
  1.2618 +            }
  1.2619 +        }
  1.2620 +        return NS_OK;
  1.2621 +    }
  1.2622 +
  1.2623 +    nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  1.2624 +    if (!content)
  1.2625 +    {
  1.2626 +        return NS_OK;
  1.2627 +    }
  1.2628 +
  1.2629 +    // Test the node to see if it's an image, frame, iframe, css, js
  1.2630 +    nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNode);
  1.2631 +    if (nodeAsImage)
  1.2632 +    {
  1.2633 +        StoreURIAttribute(aNode, "src");
  1.2634 +        return NS_OK;
  1.2635 +    }
  1.2636 +
  1.2637 +    if (content->IsSVG(nsGkAtoms::img))
  1.2638 +    {
  1.2639 +        StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
  1.2640 +        return NS_OK;
  1.2641 +    }
  1.2642 +
  1.2643 +    nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNode);
  1.2644 +    if (nodeAsMedia)
  1.2645 +    {
  1.2646 +        StoreURIAttribute(aNode, "src");
  1.2647 +        return NS_OK;
  1.2648 +    }
  1.2649 +    nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNode);
  1.2650 +    if (nodeAsSource)
  1.2651 +    {
  1.2652 +        StoreURIAttribute(aNode, "src");
  1.2653 +        return NS_OK;
  1.2654 +    }
  1.2655 +
  1.2656 +    if (content->IsHTML(nsGkAtoms::body)) {
  1.2657 +        StoreURIAttribute(aNode, "background");
  1.2658 +        return NS_OK;
  1.2659 +    }
  1.2660 +
  1.2661 +    if (content->IsHTML(nsGkAtoms::table)) {
  1.2662 +        StoreURIAttribute(aNode, "background");
  1.2663 +        return NS_OK;
  1.2664 +    }
  1.2665 +
  1.2666 +    if (content->IsHTML(nsGkAtoms::tr)) {
  1.2667 +        StoreURIAttribute(aNode, "background");
  1.2668 +        return NS_OK;
  1.2669 +    }
  1.2670 +
  1.2671 +    if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
  1.2672 +        StoreURIAttribute(aNode, "background");
  1.2673 +        return NS_OK;
  1.2674 +    }
  1.2675 +
  1.2676 +    nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
  1.2677 +    if (nodeAsScript)
  1.2678 +    {
  1.2679 +        StoreURIAttribute(aNode, "src");
  1.2680 +        return NS_OK;
  1.2681 +    }
  1.2682 +
  1.2683 +    if (content->IsSVG(nsGkAtoms::script))
  1.2684 +    {
  1.2685 +        StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
  1.2686 +        return NS_OK;
  1.2687 +    }
  1.2688 +
  1.2689 +    nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
  1.2690 +    if (nodeAsEmbed)
  1.2691 +    {
  1.2692 +        StoreURIAttribute(aNode, "src");
  1.2693 +        return NS_OK;
  1.2694 +    }
  1.2695 +
  1.2696 +    nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
  1.2697 +    if (nodeAsObject)
  1.2698 +    {
  1.2699 +        StoreURIAttribute(aNode, "data");
  1.2700 +        return NS_OK;
  1.2701 +    }
  1.2702 +
  1.2703 +    nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNode);
  1.2704 +    if (nodeAsApplet)
  1.2705 +    {
  1.2706 +        // For an applet, relative URIs are resolved relative to the
  1.2707 +        // codebase (which is resolved relative to the base URI).
  1.2708 +        nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
  1.2709 +        nsAutoString codebase;
  1.2710 +        nodeAsApplet->GetCodeBase(codebase);
  1.2711 +        if (!codebase.IsEmpty()) {
  1.2712 +            nsCOMPtr<nsIURI> baseURI;
  1.2713 +            NS_NewURI(getter_AddRefs(baseURI), codebase,
  1.2714 +                      mCurrentCharset.get(), mCurrentBaseURI);
  1.2715 +            if (baseURI) {
  1.2716 +                mCurrentBaseURI = baseURI;
  1.2717 +            }
  1.2718 +        }
  1.2719 +
  1.2720 +        URIData *archiveURIData = nullptr;
  1.2721 +        StoreURIAttribute(aNode, "archive", true, &archiveURIData);
  1.2722 +        // We only store 'code' locally if there is no 'archive',
  1.2723 +        // otherwise we assume the archive file(s) contains it (bug 430283).
  1.2724 +        if (!archiveURIData)
  1.2725 +            StoreURIAttribute(aNode, "code");
  1.2726 +
  1.2727 +        // restore the base URI we really want to have
  1.2728 +        mCurrentBaseURI = oldBase;
  1.2729 +        return NS_OK;
  1.2730 +    }
  1.2731 +
  1.2732 +    nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNode);
  1.2733 +    if (nodeAsLink)
  1.2734 +    {
  1.2735 +        // Test if the link has a rel value indicating it to be a stylesheet
  1.2736 +        nsAutoString linkRel;
  1.2737 +        if (NS_SUCCEEDED(nodeAsLink->GetRel(linkRel)) && !linkRel.IsEmpty())
  1.2738 +        {
  1.2739 +            nsReadingIterator<char16_t> start;
  1.2740 +            nsReadingIterator<char16_t> end;
  1.2741 +            nsReadingIterator<char16_t> current;
  1.2742 +
  1.2743 +            linkRel.BeginReading(start);
  1.2744 +            linkRel.EndReading(end);
  1.2745 +
  1.2746 +            // Walk through space delimited string looking for "stylesheet"
  1.2747 +            for (current = start; current != end; ++current)
  1.2748 +            {
  1.2749 +                // Ignore whitespace
  1.2750 +                if (nsCRT::IsAsciiSpace(*current))
  1.2751 +                    continue;
  1.2752 +
  1.2753 +                // Grab the next space delimited word
  1.2754 +                nsReadingIterator<char16_t> startWord = current;
  1.2755 +                do {
  1.2756 +                    ++current;
  1.2757 +                } while (current != end && !nsCRT::IsAsciiSpace(*current));
  1.2758 +
  1.2759 +                // Store the link for fix up if it says "stylesheet"
  1.2760 +                if (Substring(startWord, current)
  1.2761 +                        .LowerCaseEqualsLiteral("stylesheet"))
  1.2762 +                {
  1.2763 +                    StoreURIAttribute(aNode, "href");
  1.2764 +                    return NS_OK;
  1.2765 +                }
  1.2766 +                if (current == end)
  1.2767 +                    break;
  1.2768 +            }
  1.2769 +        }
  1.2770 +        return NS_OK;
  1.2771 +    }
  1.2772 +
  1.2773 +    nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
  1.2774 +    if (nodeAsFrame)
  1.2775 +    {
  1.2776 +        URIData *data = nullptr;
  1.2777 +        StoreURIAttribute(aNode, "src", false, &data);
  1.2778 +        if (data)
  1.2779 +        {
  1.2780 +            data->mIsSubFrame = true;
  1.2781 +            // Save the frame content
  1.2782 +            nsCOMPtr<nsIDOMDocument> content;
  1.2783 +            nodeAsFrame->GetContentDocument(getter_AddRefs(content));
  1.2784 +            if (content)
  1.2785 +            {
  1.2786 +                SaveSubframeContent(content, data);
  1.2787 +            }
  1.2788 +        }
  1.2789 +        return NS_OK;
  1.2790 +    }
  1.2791 +
  1.2792 +    nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
  1.2793 +    if (nodeAsIFrame && !(mPersistFlags & PERSIST_FLAGS_IGNORE_IFRAMES))
  1.2794 +    {
  1.2795 +        URIData *data = nullptr;
  1.2796 +        StoreURIAttribute(aNode, "src", false, &data);
  1.2797 +        if (data)
  1.2798 +        {
  1.2799 +            data->mIsSubFrame = true;
  1.2800 +            // Save the frame content
  1.2801 +            nsCOMPtr<nsIDOMDocument> content;
  1.2802 +            nodeAsIFrame->GetContentDocument(getter_AddRefs(content));
  1.2803 +            if (content)
  1.2804 +            {
  1.2805 +                SaveSubframeContent(content, data);
  1.2806 +            }
  1.2807 +        }
  1.2808 +        return NS_OK;
  1.2809 +    }
  1.2810 +
  1.2811 +    nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
  1.2812 +    if (nodeAsInput)
  1.2813 +    {
  1.2814 +        StoreURIAttribute(aNode, "src");
  1.2815 +        return NS_OK;
  1.2816 +    }
  1.2817 +
  1.2818 +    return NS_OK;
  1.2819 +}
  1.2820 +
  1.2821 +nsresult
  1.2822 +nsWebBrowserPersist::GetNodeToFixup(nsIDOMNode *aNodeIn, nsIDOMNode **aNodeOut)
  1.2823 +{
  1.2824 +    if (!(mPersistFlags & PERSIST_FLAGS_FIXUP_ORIGINAL_DOM))
  1.2825 +    {
  1.2826 +        nsresult rv = aNodeIn->CloneNode(false, 1, aNodeOut);
  1.2827 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2828 +    }
  1.2829 +    else
  1.2830 +    {
  1.2831 +        NS_ADDREF(*aNodeOut = aNodeIn);
  1.2832 +    }
  1.2833 +    nsCOMPtr<nsIDOMHTMLElement> element(do_QueryInterface(*aNodeOut));
  1.2834 +    if (element) {
  1.2835 +        // Make sure this is not XHTML
  1.2836 +        nsAutoString namespaceURI;
  1.2837 +        element->GetNamespaceURI(namespaceURI);
  1.2838 +        if (namespaceURI.IsEmpty()) {
  1.2839 +            // This is a tag-soup node.  It may have a _base_href attribute
  1.2840 +            // stuck on it by the parser, but since we're fixing up all URIs
  1.2841 +            // relative to the overall document base that will screw us up.
  1.2842 +            // Just remove the _base_href.
  1.2843 +            element->RemoveAttribute(NS_LITERAL_STRING("_base_href"));
  1.2844 +        }
  1.2845 +    }
  1.2846 +    return NS_OK;
  1.2847 +}
  1.2848 +
  1.2849 +nsresult
  1.2850 +nsWebBrowserPersist::CloneNodeWithFixedUpAttributes(
  1.2851 +    nsIDOMNode *aNodeIn, bool *aSerializeCloneKids, nsIDOMNode **aNodeOut)
  1.2852 +{
  1.2853 +    nsresult rv;
  1.2854 +    *aNodeOut = nullptr;
  1.2855 +    *aSerializeCloneKids = false;
  1.2856 +
  1.2857 +    // Fixup xml-stylesheet processing instructions
  1.2858 +    nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNodeIn);
  1.2859 +    if (nodeAsPI)
  1.2860 +    {
  1.2861 +        nsAutoString target;
  1.2862 +        nodeAsPI->GetTarget(target);
  1.2863 +        if (target.EqualsLiteral("xml-stylesheet"))
  1.2864 +        {
  1.2865 +            rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2866 +            if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2867 +            {
  1.2868 +                nsCOMPtr<nsIDOMProcessingInstruction> outNode = do_QueryInterface(*aNodeOut);
  1.2869 +                nsAutoString href;
  1.2870 +                GetXMLStyleSheetLink(nodeAsPI, href);
  1.2871 +                if (!href.IsEmpty())
  1.2872 +                {
  1.2873 +                    FixupURI(href);
  1.2874 +                    FixupXMLStyleSheetLink(outNode, href);
  1.2875 +                }
  1.2876 +            }
  1.2877 +        }
  1.2878 +    }
  1.2879 +
  1.2880 +    // BASE elements are replaced by a comment so relative links are not hosed.
  1.2881 +
  1.2882 +    if (!(mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS))
  1.2883 +    {
  1.2884 +        nsCOMPtr<nsIDOMHTMLBaseElement> nodeAsBase = do_QueryInterface(aNodeIn);
  1.2885 +        if (nodeAsBase)
  1.2886 +        {
  1.2887 +            nsCOMPtr<nsIDOMDocument> ownerDocument;
  1.2888 +            HTMLSharedElement* base = static_cast<HTMLSharedElement*>(nodeAsBase.get());
  1.2889 +            base->GetOwnerDocument(getter_AddRefs(ownerDocument));
  1.2890 +            if (ownerDocument)
  1.2891 +            {
  1.2892 +                nsAutoString href;
  1.2893 +                base->GetHref(href); // Doesn't matter if this fails
  1.2894 +                nsCOMPtr<nsIDOMComment> comment;
  1.2895 +                nsAutoString commentText; commentText.AssignLiteral(" base ");
  1.2896 +                if (!href.IsEmpty())
  1.2897 +                {
  1.2898 +                    commentText += NS_LITERAL_STRING("href=\"") + href + NS_LITERAL_STRING("\" ");
  1.2899 +                }
  1.2900 +                rv = ownerDocument->CreateComment(commentText, getter_AddRefs(comment));
  1.2901 +                if (comment)
  1.2902 +                {
  1.2903 +                    return CallQueryInterface(comment, aNodeOut);
  1.2904 +                }
  1.2905 +            }
  1.2906 +        }
  1.2907 +    }
  1.2908 +
  1.2909 +    nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
  1.2910 +    if (!content)
  1.2911 +    {
  1.2912 +        return NS_OK;
  1.2913 +    }
  1.2914 +
  1.2915 +    // Fix up href and file links in the elements
  1.2916 +
  1.2917 +    nsCOMPtr<nsIDOMHTMLAnchorElement> nodeAsAnchor = do_QueryInterface(aNodeIn);
  1.2918 +    if (nodeAsAnchor)
  1.2919 +    {
  1.2920 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2921 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2922 +        {
  1.2923 +            FixupAnchor(*aNodeOut);
  1.2924 +        }
  1.2925 +        return rv;
  1.2926 +    }
  1.2927 +
  1.2928 +    nsCOMPtr<nsIDOMHTMLAreaElement> nodeAsArea = do_QueryInterface(aNodeIn);
  1.2929 +    if (nodeAsArea)
  1.2930 +    {
  1.2931 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2932 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2933 +        {
  1.2934 +            FixupAnchor(*aNodeOut);
  1.2935 +        }
  1.2936 +        return rv;
  1.2937 +    }
  1.2938 +
  1.2939 +    if (content->IsHTML(nsGkAtoms::body)) {
  1.2940 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2941 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2942 +        {
  1.2943 +            FixupNodeAttribute(*aNodeOut, "background");
  1.2944 +        }
  1.2945 +        return rv;
  1.2946 +    }
  1.2947 +
  1.2948 +    if (content->IsHTML(nsGkAtoms::table)) {
  1.2949 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2950 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2951 +        {
  1.2952 +            FixupNodeAttribute(*aNodeOut, "background");
  1.2953 +        }
  1.2954 +        return rv;
  1.2955 +    }
  1.2956 +
  1.2957 +    if (content->IsHTML(nsGkAtoms::tr)) {
  1.2958 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2959 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2960 +        {
  1.2961 +            FixupNodeAttribute(*aNodeOut, "background");
  1.2962 +        }
  1.2963 +        return rv;
  1.2964 +    }
  1.2965 +
  1.2966 +    if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
  1.2967 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2968 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2969 +        {
  1.2970 +            FixupNodeAttribute(*aNodeOut, "background");
  1.2971 +        }
  1.2972 +        return rv;
  1.2973 +    }
  1.2974 +
  1.2975 +    nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNodeIn);
  1.2976 +    if (nodeAsImage)
  1.2977 +    {
  1.2978 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2979 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2980 +        {
  1.2981 +            // Disable image loads
  1.2982 +            nsCOMPtr<nsIImageLoadingContent> imgCon =
  1.2983 +                do_QueryInterface(*aNodeOut);
  1.2984 +            if (imgCon)
  1.2985 +                imgCon->SetLoadingEnabled(false);
  1.2986 +
  1.2987 +            FixupAnchor(*aNodeOut);
  1.2988 +            FixupNodeAttribute(*aNodeOut, "src");
  1.2989 +        }
  1.2990 +        return rv;
  1.2991 +    }
  1.2992 +
  1.2993 +    nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNodeIn);
  1.2994 +    if (nodeAsMedia)
  1.2995 +    {
  1.2996 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.2997 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.2998 +        {
  1.2999 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3000 +        }
  1.3001 +
  1.3002 +        return rv;
  1.3003 +    }
  1.3004 +
  1.3005 +    nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNodeIn);
  1.3006 +    if (nodeAsSource)
  1.3007 +    {
  1.3008 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3009 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3010 +        {
  1.3011 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3012 +        }
  1.3013 +
  1.3014 +        return rv;
  1.3015 +    }
  1.3016 +
  1.3017 +    if (content->IsSVG(nsGkAtoms::img))
  1.3018 +    {
  1.3019 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3020 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3021 +        {
  1.3022 +            // Disable image loads
  1.3023 +            nsCOMPtr<nsIImageLoadingContent> imgCon =
  1.3024 +                do_QueryInterface(*aNodeOut);
  1.3025 +            if (imgCon)
  1.3026 +                imgCon->SetLoadingEnabled(false);
  1.3027 +
  1.3028 +            // FixupAnchor(*aNodeOut);  // XXXjwatt: is this line needed?
  1.3029 +            FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
  1.3030 +        }
  1.3031 +        return rv;
  1.3032 +    }
  1.3033 +
  1.3034 +    nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNodeIn);
  1.3035 +    if (nodeAsScript)
  1.3036 +    {
  1.3037 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3038 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3039 +        {
  1.3040 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3041 +        }
  1.3042 +        return rv;
  1.3043 +    }
  1.3044 +
  1.3045 +    if (content->IsSVG(nsGkAtoms::script))
  1.3046 +    {
  1.3047 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3048 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3049 +        {
  1.3050 +            FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
  1.3051 +        }
  1.3052 +        return rv;
  1.3053 +    }
  1.3054 +
  1.3055 +    nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNodeIn);
  1.3056 +    if (nodeAsEmbed)
  1.3057 +    {
  1.3058 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3059 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3060 +        {
  1.3061 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3062 +        }
  1.3063 +        return rv;
  1.3064 +    }
  1.3065 +
  1.3066 +    nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNodeIn);
  1.3067 +    if (nodeAsObject)
  1.3068 +    {
  1.3069 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3070 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3071 +        {
  1.3072 +            FixupNodeAttribute(*aNodeOut, "data");
  1.3073 +        }
  1.3074 +        return rv;
  1.3075 +    }
  1.3076 +
  1.3077 +    nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNodeIn);
  1.3078 +    if (nodeAsApplet)
  1.3079 +    {
  1.3080 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3081 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3082 +        {
  1.3083 +            nsCOMPtr<nsIDOMHTMLAppletElement> newApplet =
  1.3084 +                do_QueryInterface(*aNodeOut);
  1.3085 +            // For an applet, relative URIs are resolved relative to the
  1.3086 +            // codebase (which is resolved relative to the base URI).
  1.3087 +            nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
  1.3088 +            nsAutoString codebase;
  1.3089 +            nodeAsApplet->GetCodeBase(codebase);
  1.3090 +            if (!codebase.IsEmpty()) {
  1.3091 +                nsCOMPtr<nsIURI> baseURI;
  1.3092 +                NS_NewURI(getter_AddRefs(baseURI), codebase,
  1.3093 +                          mCurrentCharset.get(), mCurrentBaseURI);
  1.3094 +                if (baseURI) {
  1.3095 +                    mCurrentBaseURI = baseURI;
  1.3096 +                }
  1.3097 +            }
  1.3098 +            // Unset the codebase too, since we'll correctly relativize the
  1.3099 +            // code and archive paths.
  1.3100 +            static_cast<HTMLSharedObjectElement*>(newApplet.get())->
  1.3101 +              RemoveAttribute(NS_LITERAL_STRING("codebase"));
  1.3102 +            FixupNodeAttribute(*aNodeOut, "code");
  1.3103 +            FixupNodeAttribute(*aNodeOut, "archive");
  1.3104 +            // restore the base URI we really want to have
  1.3105 +            mCurrentBaseURI = oldBase;
  1.3106 +        }
  1.3107 +        return rv;
  1.3108 +    }
  1.3109 +
  1.3110 +    nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNodeIn);
  1.3111 +    if (nodeAsLink)
  1.3112 +    {
  1.3113 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3114 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3115 +        {
  1.3116 +            // First see if the link represents linked content
  1.3117 +            rv = FixupNodeAttribute(*aNodeOut, "href");
  1.3118 +            if (NS_FAILED(rv))
  1.3119 +            {
  1.3120 +                // Perhaps this link is actually an anchor to related content
  1.3121 +                FixupAnchor(*aNodeOut);
  1.3122 +            }
  1.3123 +            // TODO if "type" attribute == "text/css"
  1.3124 +            //        fixup stylesheet
  1.3125 +        }
  1.3126 +        return rv;
  1.3127 +    }
  1.3128 +
  1.3129 +    nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNodeIn);
  1.3130 +    if (nodeAsFrame)
  1.3131 +    {
  1.3132 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3133 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3134 +        {
  1.3135 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3136 +        }
  1.3137 +        return rv;
  1.3138 +    }
  1.3139 +
  1.3140 +    nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNodeIn);
  1.3141 +    if (nodeAsIFrame)
  1.3142 +    {
  1.3143 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3144 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3145 +        {
  1.3146 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3147 +        }
  1.3148 +        return rv;
  1.3149 +    }
  1.3150 +
  1.3151 +    nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNodeIn);
  1.3152 +    if (nodeAsInput)
  1.3153 +    {
  1.3154 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3155 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3156 +        {
  1.3157 +            // Disable image loads
  1.3158 +            nsCOMPtr<nsIImageLoadingContent> imgCon =
  1.3159 +                do_QueryInterface(*aNodeOut);
  1.3160 +            if (imgCon)
  1.3161 +                imgCon->SetLoadingEnabled(false);
  1.3162 +
  1.3163 +            FixupNodeAttribute(*aNodeOut, "src");
  1.3164 +
  1.3165 +            nsAutoString valueStr;
  1.3166 +            NS_NAMED_LITERAL_STRING(valueAttr, "value");
  1.3167 +            // Update element node attributes with user-entered form state
  1.3168 +            nsCOMPtr<nsIContent> content = do_QueryInterface(*aNodeOut);
  1.3169 +            nsRefPtr<HTMLInputElement> outElt =
  1.3170 +              HTMLInputElement::FromContentOrNull(content);
  1.3171 +            nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
  1.3172 +            switch (formControl->GetType()) {
  1.3173 +                case NS_FORM_INPUT_EMAIL:
  1.3174 +                case NS_FORM_INPUT_SEARCH:
  1.3175 +                case NS_FORM_INPUT_TEXT:
  1.3176 +                case NS_FORM_INPUT_TEL:
  1.3177 +                case NS_FORM_INPUT_URL:
  1.3178 +                case NS_FORM_INPUT_NUMBER:
  1.3179 +                case NS_FORM_INPUT_RANGE:
  1.3180 +                case NS_FORM_INPUT_DATE:
  1.3181 +                case NS_FORM_INPUT_TIME:
  1.3182 +                case NS_FORM_INPUT_COLOR:
  1.3183 +                    nodeAsInput->GetValue(valueStr);
  1.3184 +                    // Avoid superfluous value="" serialization
  1.3185 +                    if (valueStr.IsEmpty())
  1.3186 +                      outElt->RemoveAttribute(valueAttr);
  1.3187 +                    else
  1.3188 +                      outElt->SetAttribute(valueAttr, valueStr);
  1.3189 +                    break;
  1.3190 +                case NS_FORM_INPUT_CHECKBOX:
  1.3191 +                case NS_FORM_INPUT_RADIO:
  1.3192 +                    bool checked;
  1.3193 +                    nodeAsInput->GetChecked(&checked);
  1.3194 +                    outElt->SetDefaultChecked(checked);
  1.3195 +                    break;
  1.3196 +                default:
  1.3197 +                    break;
  1.3198 +            }
  1.3199 +        }
  1.3200 +        return rv;
  1.3201 +    }
  1.3202 +
  1.3203 +    nsCOMPtr<nsIDOMHTMLTextAreaElement> nodeAsTextArea = do_QueryInterface(aNodeIn);
  1.3204 +    if (nodeAsTextArea)
  1.3205 +    {
  1.3206 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3207 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3208 +        {
  1.3209 +            // Tell the document encoder to serialize the text child we create below
  1.3210 +            *aSerializeCloneKids = true;
  1.3211 +
  1.3212 +            nsAutoString valueStr;
  1.3213 +            nodeAsTextArea->GetValue(valueStr);
  1.3214 +
  1.3215 +            (*aNodeOut)->SetTextContent(valueStr);
  1.3216 +        }
  1.3217 +        return rv;
  1.3218 +    }
  1.3219 +
  1.3220 +    nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
  1.3221 +    if (nodeAsOption)
  1.3222 +    {
  1.3223 +        rv = GetNodeToFixup(aNodeIn, aNodeOut);
  1.3224 +        if (NS_SUCCEEDED(rv) && *aNodeOut)
  1.3225 +        {
  1.3226 +            nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
  1.3227 +            bool selected;
  1.3228 +            nodeAsOption->GetSelected(&selected);
  1.3229 +            outElt->SetDefaultSelected(selected);
  1.3230 +        }
  1.3231 +        return rv;
  1.3232 +    }
  1.3233 +
  1.3234 +    return NS_OK;
  1.3235 +}
  1.3236 +
  1.3237 +nsresult
  1.3238 +nsWebBrowserPersist::StoreURI(
  1.3239 +    const char *aURI, bool aNeedsPersisting, URIData **aData)
  1.3240 +{
  1.3241 +    NS_ENSURE_ARG_POINTER(aURI);
  1.3242 +
  1.3243 +    nsCOMPtr<nsIURI> uri;
  1.3244 +    nsresult rv = NS_NewURI(getter_AddRefs(uri),
  1.3245 +                            nsDependentCString(aURI),
  1.3246 +                            mCurrentCharset.get(),
  1.3247 +                            mCurrentBaseURI);
  1.3248 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3249 +
  1.3250 +    return StoreURI(uri, aNeedsPersisting, aData);
  1.3251 +}
  1.3252 +
  1.3253 +nsresult
  1.3254 +nsWebBrowserPersist::StoreURI(
  1.3255 +    nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
  1.3256 +{
  1.3257 +    NS_ENSURE_ARG_POINTER(aURI);
  1.3258 +    if (aData)
  1.3259 +    {
  1.3260 +        *aData = nullptr;
  1.3261 +    }
  1.3262 +
  1.3263 +    // Test if this URI should be persisted. By default
  1.3264 +    // we should assume the URI  is persistable.
  1.3265 +    bool doNotPersistURI;
  1.3266 +    nsresult rv = NS_URIChainHasFlags(aURI,
  1.3267 +                                      nsIProtocolHandler::URI_NON_PERSISTABLE,
  1.3268 +                                      &doNotPersistURI);
  1.3269 +    if (NS_FAILED(rv))
  1.3270 +    {
  1.3271 +        doNotPersistURI = false;
  1.3272 +    }
  1.3273 +
  1.3274 +    if (doNotPersistURI)
  1.3275 +    {
  1.3276 +        return NS_OK;
  1.3277 +    }
  1.3278 +
  1.3279 +    URIData *data = nullptr;
  1.3280 +    MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
  1.3281 +    if (aData)
  1.3282 +    {
  1.3283 +        *aData = data;
  1.3284 +    }
  1.3285 +
  1.3286 +    return NS_OK;
  1.3287 +}
  1.3288 +
  1.3289 +nsresult
  1.3290 +nsWebBrowserPersist::StoreURIAttributeNS(
  1.3291 +    nsIDOMNode *aNode, const char *aNamespaceURI, const char *aAttribute,
  1.3292 +    bool aNeedsPersisting, URIData **aData)
  1.3293 +{
  1.3294 +    NS_ENSURE_ARG_POINTER(aNode);
  1.3295 +    NS_ENSURE_ARG_POINTER(aNamespaceURI);
  1.3296 +    NS_ENSURE_ARG_POINTER(aAttribute);
  1.3297 +
  1.3298 +    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  1.3299 +    MOZ_ASSERT(element);
  1.3300 +
  1.3301 +    // Find the named URI attribute on the (element) node and store
  1.3302 +    // a reference to the URI that maps onto a local file name
  1.3303 +
  1.3304 +    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  1.3305 +    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  1.3306 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3307 +
  1.3308 +    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
  1.3309 +    NS_ConvertASCIItoUTF16 attribute(aAttribute);
  1.3310 +    nsCOMPtr<nsIDOMAttr> attr;
  1.3311 +    rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
  1.3312 +    if (attr)
  1.3313 +    {
  1.3314 +        nsAutoString oldValue;
  1.3315 +        attr->GetValue(oldValue);
  1.3316 +        if (!oldValue.IsEmpty())
  1.3317 +        {
  1.3318 +            NS_ConvertUTF16toUTF8 oldCValue(oldValue);
  1.3319 +            return StoreURI(oldCValue.get(), aNeedsPersisting, aData);
  1.3320 +        }
  1.3321 +    }
  1.3322 +
  1.3323 +    return NS_OK;
  1.3324 +}
  1.3325 +
  1.3326 +nsresult
  1.3327 +nsWebBrowserPersist::FixupURI(nsAString &aURI)
  1.3328 +{
  1.3329 +    // get the current location of the file (absolutized)
  1.3330 +    nsCOMPtr<nsIURI> uri;
  1.3331 +    nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
  1.3332 +                            mCurrentCharset.get(), mCurrentBaseURI);
  1.3333 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3334 +    nsAutoCString spec;
  1.3335 +    rv = uri->GetSpec(spec);
  1.3336 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3337 +
  1.3338 +    // Search for the URI in the map and replace it with the local file
  1.3339 +    if (!mURIMap.Contains(spec))
  1.3340 +    {
  1.3341 +        return NS_ERROR_FAILURE;
  1.3342 +    }
  1.3343 +    URIData *data = mURIMap.Get(spec);
  1.3344 +    if (!data->mNeedsFixup)
  1.3345 +    {
  1.3346 +        return NS_OK;
  1.3347 +    }
  1.3348 +    nsCOMPtr<nsIURI> fileAsURI;
  1.3349 +    if (data->mFile)
  1.3350 +    {
  1.3351 +        rv = data->mFile->Clone(getter_AddRefs(fileAsURI));
  1.3352 +        NS_ENSURE_SUCCESS(rv, rv);
  1.3353 +    }
  1.3354 +    else
  1.3355 +    {
  1.3356 +        rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
  1.3357 +        NS_ENSURE_SUCCESS(rv, rv);
  1.3358 +        rv = AppendPathToURI(fileAsURI, data->mFilename);
  1.3359 +        NS_ENSURE_SUCCESS(rv, rv);
  1.3360 +    }
  1.3361 +    nsAutoString newValue;
  1.3362 +
  1.3363 +    // remove username/password if present
  1.3364 +    fileAsURI->SetUserPass(EmptyCString());
  1.3365 +
  1.3366 +    // reset node attribute
  1.3367 +    // Use relative or absolute links
  1.3368 +    if (data->mDataPathIsRelative)
  1.3369 +    {
  1.3370 +        nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
  1.3371 +        if (!url)
  1.3372 +          return NS_ERROR_FAILURE;
  1.3373 +
  1.3374 +        nsAutoCString filename;
  1.3375 +        url->GetFileName(filename);
  1.3376 +
  1.3377 +        nsAutoCString rawPathURL(data->mRelativePathToData);
  1.3378 +        rawPathURL.Append(filename);
  1.3379 +
  1.3380 +        nsAutoCString buf;
  1.3381 +        AppendUTF8toUTF16(NS_EscapeURL(rawPathURL, esc_FilePath, buf),
  1.3382 +                          newValue);
  1.3383 +    }
  1.3384 +    else
  1.3385 +    {
  1.3386 +        nsAutoCString fileurl;
  1.3387 +        fileAsURI->GetSpec(fileurl);
  1.3388 +        AppendUTF8toUTF16(fileurl, newValue);
  1.3389 +    }
  1.3390 +    if (data->mIsSubFrame)
  1.3391 +    {
  1.3392 +        newValue.Append(data->mSubFrameExt);
  1.3393 +    }
  1.3394 +
  1.3395 +    aURI = newValue;
  1.3396 +    return NS_OK;
  1.3397 +}
  1.3398 +
  1.3399 +nsresult
  1.3400 +nsWebBrowserPersist::FixupNodeAttributeNS(nsIDOMNode *aNode,
  1.3401 +                                          const char *aNamespaceURI,
  1.3402 +                                          const char *aAttribute)
  1.3403 +{
  1.3404 +    NS_ENSURE_ARG_POINTER(aNode);
  1.3405 +    NS_ENSURE_ARG_POINTER(aNamespaceURI);
  1.3406 +    NS_ENSURE_ARG_POINTER(aAttribute);
  1.3407 +
  1.3408 +    // Find the named URI attribute on the (element) node and change it to reference
  1.3409 +    // a local file.
  1.3410 +
  1.3411 +    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  1.3412 +    MOZ_ASSERT(element);
  1.3413 +
  1.3414 +    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  1.3415 +    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  1.3416 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3417 +
  1.3418 +    NS_ConvertASCIItoUTF16 attribute(aAttribute);
  1.3419 +    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
  1.3420 +    nsCOMPtr<nsIDOMAttr> attr;
  1.3421 +    rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
  1.3422 +    if (attr) {
  1.3423 +        nsString uri;
  1.3424 +        attr->GetValue(uri);
  1.3425 +        rv = FixupURI(uri);
  1.3426 +        if (NS_SUCCEEDED(rv))
  1.3427 +        {
  1.3428 +            attr->SetValue(uri);
  1.3429 +        }
  1.3430 +    }
  1.3431 +
  1.3432 +    return rv;
  1.3433 +}
  1.3434 +
  1.3435 +nsresult
  1.3436 +nsWebBrowserPersist::FixupAnchor(nsIDOMNode *aNode)
  1.3437 +{
  1.3438 +    NS_ENSURE_ARG_POINTER(aNode);
  1.3439 +
  1.3440 +    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  1.3441 +    MOZ_ASSERT(element);
  1.3442 +
  1.3443 +    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  1.3444 +    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  1.3445 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3446 +
  1.3447 +    if (mPersistFlags & PERSIST_FLAGS_DONT_FIXUP_LINKS)
  1.3448 +    {
  1.3449 +        return NS_OK;
  1.3450 +    }
  1.3451 +
  1.3452 +    // Make all anchor links absolute so they point off onto the Internet
  1.3453 +    nsString attribute(NS_LITERAL_STRING("href"));
  1.3454 +    nsCOMPtr<nsIDOMAttr> attr;
  1.3455 +    rv = attrMap->GetNamedItem(attribute, getter_AddRefs(attr));
  1.3456 +    if (attr)
  1.3457 +    {
  1.3458 +        nsString oldValue;
  1.3459 +        attr->GetValue(oldValue);
  1.3460 +        NS_ConvertUTF16toUTF8 oldCValue(oldValue);
  1.3461 +
  1.3462 +        // Skip empty values and self-referencing bookmarks
  1.3463 +        if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#')
  1.3464 +        {
  1.3465 +            return NS_OK;
  1.3466 +        }
  1.3467 +
  1.3468 +        // if saving file to same location, we don't need to do any fixup
  1.3469 +        bool isEqual = false;
  1.3470 +        if (NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual))
  1.3471 +            && isEqual)
  1.3472 +        {
  1.3473 +            return NS_OK;
  1.3474 +        }
  1.3475 +
  1.3476 +        nsCOMPtr<nsIURI> relativeURI;
  1.3477 +        relativeURI = (mPersistFlags & PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
  1.3478 +                      ? mTargetBaseURI : mCurrentBaseURI;
  1.3479 +        // Make a new URI to replace the current one
  1.3480 +        nsCOMPtr<nsIURI> newURI;
  1.3481 +        rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
  1.3482 +                       mCurrentCharset.get(), relativeURI);
  1.3483 +        if (NS_SUCCEEDED(rv) && newURI)
  1.3484 +        {
  1.3485 +            newURI->SetUserPass(EmptyCString());
  1.3486 +            nsAutoCString uriSpec;
  1.3487 +            newURI->GetSpec(uriSpec);
  1.3488 +            attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec));
  1.3489 +        }
  1.3490 +    }
  1.3491 +
  1.3492 +    return NS_OK;
  1.3493 +}
  1.3494 +
  1.3495 +nsresult
  1.3496 +nsWebBrowserPersist::StoreAndFixupStyleSheet(nsIStyleSheet *aStyleSheet)
  1.3497 +{
  1.3498 +    // TODO go through the style sheet fixing up all links
  1.3499 +    return NS_OK;
  1.3500 +}
  1.3501 +
  1.3502 +bool
  1.3503 +nsWebBrowserPersist::DocumentEncoderExists(const char16_t *aContentType)
  1.3504 +{
  1.3505 +    // Check if there is an encoder for the desired content type.
  1.3506 +    nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  1.3507 +    AppendUTF16toUTF8(aContentType, contractID);
  1.3508 +
  1.3509 +    nsCOMPtr<nsIComponentRegistrar> registrar;
  1.3510 +    NS_GetComponentRegistrar(getter_AddRefs(registrar));
  1.3511 +    if (registrar)
  1.3512 +    {
  1.3513 +        bool result;
  1.3514 +        nsresult rv = registrar->IsContractIDRegistered(contractID.get(),
  1.3515 +                                                        &result);
  1.3516 +        if (NS_SUCCEEDED(rv) && result)
  1.3517 +        {
  1.3518 +            return true;
  1.3519 +        }
  1.3520 +    }
  1.3521 +    return false;
  1.3522 +}
  1.3523 +
  1.3524 +nsresult
  1.3525 +nsWebBrowserPersist::SaveSubframeContent(
  1.3526 +    nsIDOMDocument *aFrameContent, URIData *aData)
  1.3527 +{
  1.3528 +    NS_ENSURE_ARG_POINTER(aData);
  1.3529 +
  1.3530 +    // Extract the content type for the frame's contents.
  1.3531 +    nsCOMPtr<nsIDocument> frameDoc(do_QueryInterface(aFrameContent));
  1.3532 +    NS_ENSURE_STATE(frameDoc);
  1.3533 +
  1.3534 +    nsAutoString contentType;
  1.3535 +    nsresult rv = frameDoc->GetContentType(contentType);
  1.3536 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3537 +
  1.3538 +    nsXPIDLString ext;
  1.3539 +    GetExtensionForContentType(contentType.get(), getter_Copies(ext));
  1.3540 +
  1.3541 +    // We must always have an extension so we will try to re-assign
  1.3542 +    // the original extension if GetExtensionForContentType fails.
  1.3543 +    if (ext.IsEmpty())
  1.3544 +    {
  1.3545 +        nsCOMPtr<nsIURL> url(do_QueryInterface(frameDoc->GetDocumentURI(),
  1.3546 +                                               &rv));
  1.3547 +        nsAutoCString extension;
  1.3548 +        if (NS_SUCCEEDED(rv))
  1.3549 +        {
  1.3550 +            url->GetFileExtension(extension);
  1.3551 +        }
  1.3552 +        else
  1.3553 +        {
  1.3554 +            extension.AssignLiteral("htm");
  1.3555 +        }
  1.3556 +        aData->mSubFrameExt.Assign(char16_t('.'));
  1.3557 +        AppendUTF8toUTF16(extension, aData->mSubFrameExt);
  1.3558 +    }
  1.3559 +    else
  1.3560 +    {
  1.3561 +        aData->mSubFrameExt.Assign(char16_t('.'));
  1.3562 +        aData->mSubFrameExt.Append(ext);
  1.3563 +    }
  1.3564 +
  1.3565 +    nsString filenameWithExt = aData->mFilename;
  1.3566 +    filenameWithExt.Append(aData->mSubFrameExt);
  1.3567 +
  1.3568 +    // Work out the path for the subframe
  1.3569 +    nsCOMPtr<nsIURI> frameURI;
  1.3570 +    rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
  1.3571 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3572 +    rv = AppendPathToURI(frameURI, filenameWithExt);
  1.3573 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3574 +
  1.3575 +    // Work out the path for the subframe data
  1.3576 +    nsCOMPtr<nsIURI> frameDataURI;
  1.3577 +    rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
  1.3578 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3579 +    nsAutoString newFrameDataPath(aData->mFilename);
  1.3580 +
  1.3581 +    // Append _data
  1.3582 +    newFrameDataPath.AppendLiteral("_data");
  1.3583 +    rv = AppendPathToURI(frameDataURI, newFrameDataPath);
  1.3584 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3585 +
  1.3586 +    // Make frame document & data path conformant and unique
  1.3587 +    rv = CalculateUniqueFilename(frameURI);
  1.3588 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3589 +    rv = CalculateUniqueFilename(frameDataURI);
  1.3590 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3591 +
  1.3592 +    mCurrentThingsToPersist++;
  1.3593 +
  1.3594 +    // We shouldn't use SaveDocumentInternal for the contents
  1.3595 +    // of frames that are not documents, e.g. images.
  1.3596 +    if (DocumentEncoderExists(contentType.get()))
  1.3597 +    {
  1.3598 +        rv = SaveDocumentInternal(aFrameContent, frameURI, frameDataURI);
  1.3599 +    }
  1.3600 +    else
  1.3601 +    {
  1.3602 +        rv = StoreURI(frameDoc->GetDocumentURI());
  1.3603 +    }
  1.3604 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3605 +
  1.3606 +    // Store the updated uri to the frame
  1.3607 +    aData->mFile = frameURI;
  1.3608 +    aData->mSubFrameExt.Truncate(); // we already put this in frameURI
  1.3609 +
  1.3610 +    return NS_OK;
  1.3611 +}
  1.3612 +
  1.3613 +nsresult
  1.3614 +nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
  1.3615 +{
  1.3616 +    nsresult rv = NS_OK;
  1.3617 +    *aChannel = nullptr;
  1.3618 +
  1.3619 +    nsCOMPtr<nsIIOService> ioserv;
  1.3620 +    ioserv = do_GetIOService(&rv);
  1.3621 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3622 +
  1.3623 +    rv = ioserv->NewChannelFromURI(aURI, aChannel);
  1.3624 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3625 +    NS_ENSURE_ARG_POINTER(*aChannel);
  1.3626 +
  1.3627 +    rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
  1.3628 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3629 +    return NS_OK;
  1.3630 +}
  1.3631 +
  1.3632 +nsresult
  1.3633 +nsWebBrowserPersist::SaveDocumentWithFixup(
  1.3634 +    nsIDOMDocument *aDocument, nsIDocumentEncoderNodeFixup *aNodeFixup,
  1.3635 +    nsIURI *aFile, bool aReplaceExisting, const nsACString &aFormatType,
  1.3636 +    const nsCString &aSaveCharset, uint32_t aFlags)
  1.3637 +{
  1.3638 +    NS_ENSURE_ARG_POINTER(aFile);
  1.3639 +
  1.3640 +    nsresult  rv = NS_OK;
  1.3641 +    nsCOMPtr<nsIFile> localFile;
  1.3642 +    GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
  1.3643 +    if (localFile)
  1.3644 +    {
  1.3645 +        // if we're not replacing an existing file but the file
  1.3646 +        // exists, something is wrong
  1.3647 +        bool fileExists = false;
  1.3648 +        rv = localFile->Exists(&fileExists);
  1.3649 +        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3650 +
  1.3651 +        if (!aReplaceExisting && fileExists)
  1.3652 +            return NS_ERROR_FAILURE;                // where are the file I/O errors?
  1.3653 +    }
  1.3654 +
  1.3655 +    nsCOMPtr<nsIOutputStream> outputStream;
  1.3656 +    rv = MakeOutputStream(aFile, getter_AddRefs(outputStream));
  1.3657 +    if (NS_FAILED(rv))
  1.3658 +    {
  1.3659 +        SendErrorStatusChange(false, rv, nullptr, aFile);
  1.3660 +        return NS_ERROR_FAILURE;
  1.3661 +    }
  1.3662 +    NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
  1.3663 +
  1.3664 +    // Get a document encoder instance
  1.3665 +    nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  1.3666 +    contractID.Append(aFormatType);
  1.3667 +
  1.3668 +    nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(contractID.get(), &rv);
  1.3669 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3670 +
  1.3671 +    NS_ConvertASCIItoUTF16 newContentType(aFormatType);
  1.3672 +    rv = encoder->Init(aDocument, newContentType, aFlags);
  1.3673 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3674 +
  1.3675 +    mTargetBaseURI = aFile;
  1.3676 +
  1.3677 +    // Set the node fixup callback
  1.3678 +    encoder->SetNodeFixup(aNodeFixup);
  1.3679 +
  1.3680 +    if (mWrapColumn && (aFlags & ENCODE_FLAGS_WRAP))
  1.3681 +        encoder->SetWrapColumn(mWrapColumn);
  1.3682 +
  1.3683 +    nsAutoCString charsetStr(aSaveCharset);
  1.3684 +    if (charsetStr.IsEmpty())
  1.3685 +    {
  1.3686 +        nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
  1.3687 +        NS_ASSERTION(doc, "Need a document");
  1.3688 +        charsetStr = doc->GetDocumentCharacterSet();
  1.3689 +    }
  1.3690 +
  1.3691 +    rv = encoder->SetCharset(charsetStr);
  1.3692 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3693 +
  1.3694 +    rv = encoder->EncodeToStream(outputStream);
  1.3695 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3696 +
  1.3697 +    if (!localFile)
  1.3698 +    {
  1.3699 +        nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(outputStream));
  1.3700 +        if (storStream)
  1.3701 +        {
  1.3702 +            outputStream->Close();
  1.3703 +            rv = StartUpload(storStream, aFile, aFormatType);
  1.3704 +            NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3705 +        }
  1.3706 +    }
  1.3707 +
  1.3708 +    return rv;
  1.3709 +}
  1.3710 +
  1.3711 +
  1.3712 +// we store the current location as the key (absolutized version of domnode's attribute's value)
  1.3713 +nsresult
  1.3714 +nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
  1.3715 +    nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
  1.3716 +{
  1.3717 +    NS_ENSURE_ARG_POINTER(aURI);
  1.3718 +
  1.3719 +    nsAutoCString spec;
  1.3720 +    nsresult rv = aURI->GetSpec(spec);
  1.3721 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3722 +
  1.3723 +    // Create a sensibly named filename for the URI and store in the URI map
  1.3724 +    URIData *data;
  1.3725 +    if (mURIMap.Contains(spec))
  1.3726 +    {
  1.3727 +        data = mURIMap.Get(spec);
  1.3728 +        if (aNeedsPersisting)
  1.3729 +        {
  1.3730 +          data->mNeedsPersisting = true;
  1.3731 +        }
  1.3732 +        if (aData)
  1.3733 +        {
  1.3734 +            *aData = data;
  1.3735 +        }
  1.3736 +        return NS_OK;
  1.3737 +    }
  1.3738 +
  1.3739 +    // Create a unique file name for the uri
  1.3740 +    nsString filename;
  1.3741 +    rv = MakeFilenameFromURI(aURI, filename);
  1.3742 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1.3743 +
  1.3744 +    // Store the file name
  1.3745 +    data = new URIData;
  1.3746 +    NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
  1.3747 +
  1.3748 +    data->mNeedsPersisting = aNeedsPersisting;
  1.3749 +    data->mNeedsFixup = true;
  1.3750 +    data->mFilename = filename;
  1.3751 +    data->mSaved = false;
  1.3752 +    data->mIsSubFrame = false;
  1.3753 +    data->mDataPath = mCurrentDataPath;
  1.3754 +    data->mDataPathIsRelative = mCurrentDataPathIsRelative;
  1.3755 +    data->mRelativePathToData = mCurrentRelativePathToData;
  1.3756 +    data->mCharset = mCurrentCharset;
  1.3757 +
  1.3758 +    if (aNeedsPersisting)
  1.3759 +        mCurrentThingsToPersist++;
  1.3760 +
  1.3761 +    mURIMap.Put(spec, data);
  1.3762 +    if (aData)
  1.3763 +    {
  1.3764 +        *aData = data;
  1.3765 +    }
  1.3766 +
  1.3767 +    return NS_OK;
  1.3768 +}
  1.3769 +
  1.3770 +// Ordered so that typical documents work fastest.
  1.3771 +//                                    strlen("blockquote")==10
  1.3772 +static const char kSpecialXHTMLTags[][11] = {
  1.3773 +    "body",
  1.3774 +    "head",
  1.3775 +    "img",
  1.3776 +    "script",
  1.3777 +    "a",
  1.3778 +    "area",
  1.3779 +    "link",
  1.3780 +    "input",
  1.3781 +    "frame",
  1.3782 +    "iframe",
  1.3783 +    "object",
  1.3784 +    "applet",
  1.3785 +    "form",
  1.3786 +    "blockquote",
  1.3787 +    "q",
  1.3788 +    "del",
  1.3789 +    "ins"
  1.3790 +};
  1.3791 +
  1.3792 +static bool IsSpecialXHTMLTag(nsIDOMNode *aNode)
  1.3793 +{
  1.3794 +    nsAutoString tmp;
  1.3795 +    aNode->GetNamespaceURI(tmp);
  1.3796 +    if (!tmp.EqualsLiteral("http://www.w3.org/1999/xhtml"))
  1.3797 +        return false;
  1.3798 +
  1.3799 +    aNode->GetLocalName(tmp);
  1.3800 +    for (uint32_t i = 0; i < ArrayLength(kSpecialXHTMLTags); i++) {
  1.3801 +        if (tmp.EqualsASCII(kSpecialXHTMLTags[i]))
  1.3802 +        {
  1.3803 +            // XXX This element MAY have URI attributes, but
  1.3804 +            //     we are not actually checking if they are present.
  1.3805 +            //     That would slow us down further, and I am not so sure
  1.3806 +            //     how important that would be.
  1.3807 +            return true;
  1.3808 +        }
  1.3809 +    }
  1.3810 +
  1.3811 +    return false;
  1.3812 +}
  1.3813 +
  1.3814 +static bool HasSpecialXHTMLTags(nsIDOMNode *aParent)
  1.3815 +{
  1.3816 +    if (IsSpecialXHTMLTag(aParent))
  1.3817 +        return true;
  1.3818 +
  1.3819 +    nsCOMPtr<nsIDOMNodeList> list;
  1.3820 +    aParent->GetChildNodes(getter_AddRefs(list));
  1.3821 +    if (list)
  1.3822 +    {
  1.3823 +        uint32_t count;
  1.3824 +        list->GetLength(&count);
  1.3825 +        uint32_t i;
  1.3826 +        for (i = 0; i < count; i++) {
  1.3827 +            nsCOMPtr<nsIDOMNode> node;
  1.3828 +            list->Item(i, getter_AddRefs(node));
  1.3829 +            if (!node)
  1.3830 +                break;
  1.3831 +            uint16_t nodeType;
  1.3832 +            node->GetNodeType(&nodeType);
  1.3833 +            if (nodeType == nsIDOMNode::ELEMENT_NODE) {
  1.3834 +                return HasSpecialXHTMLTags(node);
  1.3835 +            }
  1.3836 +        }
  1.3837 +    }
  1.3838 +
  1.3839 +    return false;
  1.3840 +}
  1.3841 +
  1.3842 +static bool NeedXHTMLBaseTag(nsIDOMDocument *aDocument)
  1.3843 +{
  1.3844 +    nsCOMPtr<nsIDOMElement> docElement;
  1.3845 +    aDocument->GetDocumentElement(getter_AddRefs(docElement));
  1.3846 +
  1.3847 +    nsCOMPtr<nsIDOMNode> node(do_QueryInterface(docElement));
  1.3848 +    if (node)
  1.3849 +    {
  1.3850 +        return HasSpecialXHTMLTags(node);
  1.3851 +    }
  1.3852 +
  1.3853 +    return false;
  1.3854 +}
  1.3855 +
  1.3856 +// Set document base. This could create an invalid XML document (still well-formed).
  1.3857 +nsresult
  1.3858 +nsWebBrowserPersist::SetDocumentBase(
  1.3859 +    nsIDOMDocument *aDocument, nsIURI *aBaseURI)
  1.3860 +{
  1.3861 +    if (mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS)
  1.3862 +    {
  1.3863 +        return NS_OK;
  1.3864 +    }
  1.3865 +
  1.3866 +    NS_ENSURE_ARG_POINTER(aBaseURI);
  1.3867 +
  1.3868 +    nsCOMPtr<nsIDOMXMLDocument> xmlDoc;
  1.3869 +    nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
  1.3870 +    if (!htmlDoc)
  1.3871 +    {
  1.3872 +        xmlDoc = do_QueryInterface(aDocument);
  1.3873 +        if (!xmlDoc)
  1.3874 +        {
  1.3875 +            return NS_ERROR_FAILURE;
  1.3876 +        }
  1.3877 +    }
  1.3878 +
  1.3879 +    NS_NAMED_LITERAL_STRING(kXHTMLNS, "http://www.w3.org/1999/xhtml");
  1.3880 +    NS_NAMED_LITERAL_STRING(kHead, "head");
  1.3881 +
  1.3882 +    // Find the head element
  1.3883 +    nsCOMPtr<nsIDOMElement> headElement;
  1.3884 +    nsCOMPtr<nsIDOMNodeList> headList;
  1.3885 +    if (xmlDoc)
  1.3886 +    {
  1.3887 +        // First see if there is XHTML content that needs base
  1.3888 +        // tags.
  1.3889 +        if (!NeedXHTMLBaseTag(aDocument))
  1.3890 +            return NS_OK;
  1.3891 +
  1.3892 +        aDocument->GetElementsByTagNameNS(
  1.3893 +            kXHTMLNS,
  1.3894 +            kHead, getter_AddRefs(headList));
  1.3895 +    }
  1.3896 +    else
  1.3897 +    {
  1.3898 +        aDocument->GetElementsByTagName(
  1.3899 +            kHead, getter_AddRefs(headList));
  1.3900 +    }
  1.3901 +    if (headList)
  1.3902 +    {
  1.3903 +        nsCOMPtr<nsIDOMNode> headNode;
  1.3904 +        headList->Item(0, getter_AddRefs(headNode));
  1.3905 +        headElement = do_QueryInterface(headNode);
  1.3906 +    }
  1.3907 +    if (!headElement)
  1.3908 +    {
  1.3909 +        // Create head and insert as first element
  1.3910 +        nsCOMPtr<nsIDOMNode> firstChildNode;
  1.3911 +        nsCOMPtr<nsIDOMNode> newNode;
  1.3912 +        if (xmlDoc)
  1.3913 +        {
  1.3914 +            aDocument->CreateElementNS(
  1.3915 +                kXHTMLNS,
  1.3916 +                kHead, getter_AddRefs(headElement));
  1.3917 +        }
  1.3918 +        else
  1.3919 +        {
  1.3920 +            aDocument->CreateElement(
  1.3921 +                kHead, getter_AddRefs(headElement));
  1.3922 +        }
  1.3923 +        nsCOMPtr<nsIDOMElement> documentElement;
  1.3924 +        aDocument->GetDocumentElement(getter_AddRefs(documentElement));
  1.3925 +        if (documentElement)
  1.3926 +        {
  1.3927 +            documentElement->GetFirstChild(getter_AddRefs(firstChildNode));
  1.3928 +            documentElement->InsertBefore(headElement, firstChildNode, getter_AddRefs(newNode));
  1.3929 +        }
  1.3930 +    }
  1.3931 +    if (!headElement)
  1.3932 +    {
  1.3933 +        return NS_ERROR_FAILURE;
  1.3934 +    }
  1.3935 +
  1.3936 +    // Find or create the BASE element
  1.3937 +    NS_NAMED_LITERAL_STRING(kBase, "base");
  1.3938 +    nsCOMPtr<nsIDOMElement> baseElement;
  1.3939 +    nsCOMPtr<nsIDOMHTMLCollection> baseList;
  1.3940 +    if (xmlDoc)
  1.3941 +    {
  1.3942 +        headElement->GetElementsByTagNameNS(
  1.3943 +            kXHTMLNS,
  1.3944 +            kBase, getter_AddRefs(baseList));
  1.3945 +    }
  1.3946 +    else
  1.3947 +    {
  1.3948 +        headElement->GetElementsByTagName(
  1.3949 +            kBase, getter_AddRefs(baseList));
  1.3950 +    }
  1.3951 +    if (baseList)
  1.3952 +    {
  1.3953 +        nsCOMPtr<nsIDOMNode> baseNode;
  1.3954 +        baseList->Item(0, getter_AddRefs(baseNode));
  1.3955 +        baseElement = do_QueryInterface(baseNode);
  1.3956 +    }
  1.3957 +
  1.3958 +    // Add the BASE element
  1.3959 +    if (!baseElement)
  1.3960 +    {
  1.3961 +      nsCOMPtr<nsIDOMNode> newNode;
  1.3962 +      if (xmlDoc)
  1.3963 +      {
  1.3964 +          aDocument->CreateElementNS(
  1.3965 +              kXHTMLNS,
  1.3966 +              kBase, getter_AddRefs(baseElement));
  1.3967 +      }
  1.3968 +      else
  1.3969 +      {
  1.3970 +          aDocument->CreateElement(
  1.3971 +              kBase, getter_AddRefs(baseElement));
  1.3972 +      }
  1.3973 +      headElement->AppendChild(baseElement, getter_AddRefs(newNode));
  1.3974 +    }
  1.3975 +    if (!baseElement)
  1.3976 +    {
  1.3977 +        return NS_ERROR_FAILURE;
  1.3978 +    }
  1.3979 +    nsAutoCString uriSpec;
  1.3980 +    aBaseURI->GetSpec(uriSpec);
  1.3981 +    NS_ConvertUTF8toUTF16 href(uriSpec);
  1.3982 +    baseElement->SetAttribute(NS_LITERAL_STRING("href"), href);
  1.3983 +
  1.3984 +    return NS_OK;
  1.3985 +}
  1.3986 +
  1.3987 +// Decide if we need to apply conversion to the passed channel.
  1.3988 +void nsWebBrowserPersist::SetApplyConversionIfNeeded(nsIChannel *aChannel)
  1.3989 +{
  1.3990 +    nsresult rv = NS_OK;
  1.3991 +    nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aChannel, &rv);
  1.3992 +    if (NS_FAILED(rv))
  1.3993 +        return;
  1.3994 +
  1.3995 +    // Set the default conversion preference:
  1.3996 +    encChannel->SetApplyConversion(false);
  1.3997 +
  1.3998 +    nsCOMPtr<nsIURI> thisURI;
  1.3999 +    aChannel->GetURI(getter_AddRefs(thisURI));
  1.4000 +    nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(thisURI));
  1.4001 +    if (!sourceURL)
  1.4002 +        return;
  1.4003 +    nsAutoCString extension;
  1.4004 +    sourceURL->GetFileExtension(extension);
  1.4005 +
  1.4006 +    nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
  1.4007 +    encChannel->GetContentEncodings(getter_AddRefs(encEnum));
  1.4008 +    if (!encEnum)
  1.4009 +        return;
  1.4010 +    nsCOMPtr<nsIExternalHelperAppService> helperAppService =
  1.4011 +        do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
  1.4012 +    if (NS_FAILED(rv))
  1.4013 +        return;
  1.4014 +    bool hasMore;
  1.4015 +    rv = encEnum->HasMore(&hasMore);
  1.4016 +    if (NS_SUCCEEDED(rv) && hasMore)
  1.4017 +    {
  1.4018 +        nsAutoCString encType;
  1.4019 +        rv = encEnum->GetNext(encType);
  1.4020 +        if (NS_SUCCEEDED(rv))
  1.4021 +        {
  1.4022 +            bool applyConversion = false;
  1.4023 +            rv = helperAppService->ApplyDecodingForExtension(extension, encType,
  1.4024 +                                                             &applyConversion);
  1.4025 +            if (NS_SUCCEEDED(rv))
  1.4026 +                encChannel->SetApplyConversion(applyConversion);
  1.4027 +        }
  1.4028 +    }
  1.4029 +}
  1.4030 +
  1.4031 +///////////////////////////////////////////////////////////////////////////////
  1.4032 +
  1.4033 +
  1.4034 +nsEncoderNodeFixup::nsEncoderNodeFixup() : mWebBrowserPersist(nullptr)
  1.4035 +{
  1.4036 +}
  1.4037 +
  1.4038 +
  1.4039 +nsEncoderNodeFixup::~nsEncoderNodeFixup()
  1.4040 +{
  1.4041 +}
  1.4042 +
  1.4043 +
  1.4044 +NS_IMPL_ADDREF(nsEncoderNodeFixup)
  1.4045 +NS_IMPL_RELEASE(nsEncoderNodeFixup)
  1.4046 +
  1.4047 +
  1.4048 +NS_INTERFACE_MAP_BEGIN(nsEncoderNodeFixup)
  1.4049 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentEncoderNodeFixup)
  1.4050 +    NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoderNodeFixup)
  1.4051 +NS_INTERFACE_MAP_END
  1.4052 +
  1.4053 +
  1.4054 +NS_IMETHODIMP nsEncoderNodeFixup::FixupNode(
  1.4055 +    nsIDOMNode *aNode, bool *aSerializeCloneKids, nsIDOMNode **aOutNode)
  1.4056 +{
  1.4057 +    NS_ENSURE_ARG_POINTER(aNode);
  1.4058 +    NS_ENSURE_ARG_POINTER(aOutNode);
  1.4059 +    NS_ENSURE_TRUE(mWebBrowserPersist, NS_ERROR_FAILURE);
  1.4060 +
  1.4061 +    *aOutNode = nullptr;
  1.4062 +
  1.4063 +    // Test whether we need to fixup the node
  1.4064 +    uint16_t type = 0;
  1.4065 +    aNode->GetNodeType(&type);
  1.4066 +    if (type == nsIDOMNode::ELEMENT_NODE ||
  1.4067 +        type == nsIDOMNode::PROCESSING_INSTRUCTION_NODE)
  1.4068 +    {
  1.4069 +        return mWebBrowserPersist->CloneNodeWithFixedUpAttributes(aNode, aSerializeCloneKids, aOutNode);
  1.4070 +    }
  1.4071 +
  1.4072 +    return NS_OK;
  1.4073 +}

mercurial