embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     8 #include "nspr.h"
    10 #include "nsIFileStreams.h"       // New Necko file streams
    11 #include <algorithm>
    13 #include "nsNetUtil.h"
    14 #include "nsComponentManagerUtils.h"
    15 #include "nsIComponentRegistrar.h"
    16 #include "nsIStorageStream.h"
    17 #include "nsISeekableStream.h"
    18 #include "nsIHttpChannel.h"
    19 #include "nsIHttpChannelInternal.h"
    20 #include "nsIEncodedChannel.h"
    21 #include "nsIUploadChannel.h"
    22 #include "nsICachingChannel.h"
    23 #include "nsIFileChannel.h"
    24 #include "nsEscape.h"
    25 #include "nsUnicharUtils.h"
    26 #include "nsIStringEnumerator.h"
    27 #include "nsCRT.h"
    28 #include "nsSupportsArray.h"
    29 #include "nsContentCID.h"
    30 #include "nsStreamUtils.h"
    32 #include "nsCExternalHandlerService.h"
    34 #include "nsIURL.h"
    35 #include "nsIFileURL.h"
    36 #include "nsIDocument.h"
    37 #include "nsIDOMDocument.h"
    38 #include "nsIDOMXMLDocument.h"
    39 #include "nsIDOMTreeWalker.h"
    40 #include "nsIDOMNode.h"
    41 #include "nsIDOMComment.h"
    42 #include "nsIDOMMozNamedAttrMap.h"
    43 #include "nsIDOMAttr.h"
    44 #include "nsIDOMNodeList.h"
    45 #include "nsIWebProgressListener.h"
    46 #include "nsIAuthPrompt.h"
    47 #include "nsIPrompt.h"
    48 #include "nsISHEntry.h"
    49 #include "nsIWebPageDescriptor.h"
    50 #include "nsIFormControl.h"
    51 #include "nsContentUtils.h"
    53 #include "nsIDOMNodeFilter.h"
    54 #include "nsIDOMProcessingInstruction.h"
    55 #include "nsIDOMHTMLAnchorElement.h"
    56 #include "nsIDOMHTMLAreaElement.h"
    57 #include "nsIDOMHTMLCollection.h"
    58 #include "nsIDOMHTMLImageElement.h"
    59 #include "nsIDOMHTMLScriptElement.h"
    60 #include "nsIDOMHTMLLinkElement.h"
    61 #include "nsIDOMHTMLBaseElement.h"
    62 #include "nsIDOMHTMLFrameElement.h"
    63 #include "nsIDOMHTMLIFrameElement.h"
    64 #include "nsIDOMHTMLInputElement.h"
    65 #include "nsIDOMHTMLEmbedElement.h"
    66 #include "nsIDOMHTMLObjectElement.h"
    67 #include "nsIDOMHTMLAppletElement.h"
    68 #include "nsIDOMHTMLOptionElement.h"
    69 #include "nsIDOMHTMLTextAreaElement.h"
    70 #include "nsIDOMHTMLDocument.h"
    71 #include "nsIDOMHTMLSourceElement.h"
    72 #include "nsIDOMHTMLMediaElement.h"
    74 #include "nsIImageLoadingContent.h"
    76 #include "ftpCore.h"
    77 #include "nsITransport.h"
    78 #include "nsISocketTransport.h"
    79 #include "nsIStringBundle.h"
    80 #include "nsIProtocolHandler.h"
    82 #include "nsWebBrowserPersist.h"
    84 #include "nsIContent.h"
    85 #include "nsIMIMEInfo.h"
    86 #include "mozilla/dom/HTMLInputElement.h"
    87 #include "mozilla/dom/HTMLSharedElement.h"
    88 #include "mozilla/dom/HTMLSharedObjectElement.h"
    90 using namespace mozilla;
    91 using namespace mozilla::dom;
    93 // Buffer file writes in 32kb chunks
    94 #define BUFFERED_OUTPUT_SIZE (1024 * 32)
    96 // Information about a DOM document
    97 struct DocData
    98 {
    99     nsCOMPtr<nsIURI> mBaseURI;
   100     nsCOMPtr<nsIDOMDocument> mDocument;
   101     nsCOMPtr<nsIURI> mFile;
   102     nsCOMPtr<nsIURI> mDataPath;
   103     bool mDataPathIsRelative;
   104     nsCString mRelativePathToData;
   105     nsCString mCharset;
   106 };
   108 // Information about a URI
   109 struct URIData
   110 {
   111     bool mNeedsPersisting;
   112     bool mSaved;
   113     bool mIsSubFrame;
   114     bool mDataPathIsRelative;
   115     bool mNeedsFixup;
   116     nsString mFilename;
   117     nsString mSubFrameExt;
   118     nsCOMPtr<nsIURI> mFile;
   119     nsCOMPtr<nsIURI> mDataPath;
   120     nsCString mRelativePathToData;
   121     nsCString mCharset;
   122 };
   124 // Information about the output stream
   125 struct OutputData
   126 {
   127     nsCOMPtr<nsIURI> mFile;
   128     nsCOMPtr<nsIURI> mOriginalLocation;
   129     nsCOMPtr<nsIOutputStream> mStream;
   130     int64_t mSelfProgress;
   131     int64_t mSelfProgressMax;
   132     bool mCalcFileExt;
   134     OutputData(nsIURI *aFile, nsIURI *aOriginalLocation, bool aCalcFileExt) :
   135         mFile(aFile),
   136         mOriginalLocation(aOriginalLocation),
   137         mSelfProgress(0),
   138         mSelfProgressMax(10000),
   139         mCalcFileExt(aCalcFileExt)
   140     {
   141     }
   142     ~OutputData()
   143     {
   144         if (mStream)
   145         {
   146             mStream->Close();
   147         }
   148     }
   149 };
   151 struct UploadData
   152 {
   153     nsCOMPtr<nsIURI> mFile;
   154     int64_t mSelfProgress;
   155     int64_t mSelfProgressMax;
   157     UploadData(nsIURI *aFile) :
   158         mFile(aFile),
   159         mSelfProgress(0),
   160         mSelfProgressMax(10000)
   161     {
   162     }
   163 };
   165 struct CleanupData
   166 {
   167     nsCOMPtr<nsIFile> mFile;
   168     // Snapshot of what the file actually is at the time of creation so that if
   169     // it transmutes into something else later on it can be ignored. For example,
   170     // catch files that turn into dirs or vice versa.
   171     bool mIsDirectory;
   172 };
   174 // Maximum file length constant. The max file name length is
   175 // volume / server dependent but it is difficult to obtain
   176 // that information. Instead this constant is a reasonable value that
   177 // modern systems should able to cope with.
   178 const uint32_t kDefaultMaxFilenameLength = 64;
   180 // Default flags for persistence
   181 const uint32_t kDefaultPersistFlags =
   182     nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
   183     nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
   185 // String bundle where error messages come from
   186 const char *kWebBrowserPersistStringBundle =
   187     "chrome://global/locale/nsWebBrowserPersist.properties";
   189 nsWebBrowserPersist::nsWebBrowserPersist() :
   190     mCurrentThingsToPersist(0),
   191     mFirstAndOnlyUse(true),
   192     mCancel(false),
   193     mJustStartedLoading(true),
   194     mCompleted(false),
   195     mStartSaving(false),
   196     mReplaceExisting(true),
   197     mSerializingOutput(false),
   198     mIsPrivate(false),
   199     mPersistFlags(kDefaultPersistFlags),
   200     mPersistResult(NS_OK),
   201     mTotalCurrentProgress(0),
   202     mTotalMaxProgress(0),
   203     mWrapColumn(72),
   204     mEncodingFlags(0)
   205 {
   206 }
   208 nsWebBrowserPersist::~nsWebBrowserPersist()
   209 {
   210     Cleanup();
   211 }
   213 //*****************************************************************************
   214 // nsWebBrowserPersist::nsISupports
   215 //*****************************************************************************
   217 NS_IMPL_ADDREF(nsWebBrowserPersist)
   218 NS_IMPL_RELEASE(nsWebBrowserPersist)
   220 NS_INTERFACE_MAP_BEGIN(nsWebBrowserPersist)
   221     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersist)
   222     NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersist)
   223     NS_INTERFACE_MAP_ENTRY(nsICancelable)
   224     NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   225     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   226     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   227     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   228     NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   229 NS_INTERFACE_MAP_END
   232 //*****************************************************************************
   233 // nsWebBrowserPersist::nsIInterfaceRequestor
   234 //*****************************************************************************
   236 NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
   237 {
   238     NS_ENSURE_ARG_POINTER(aIFace);
   240     *aIFace = nullptr;
   242     nsresult rv = QueryInterface(aIID, aIFace);
   243     if (NS_SUCCEEDED(rv))
   244     {
   245         return rv;
   246     }
   248     if (mProgressListener && (aIID.Equals(NS_GET_IID(nsIAuthPrompt))
   249                              || aIID.Equals(NS_GET_IID(nsIPrompt))))
   250     {
   251         mProgressListener->QueryInterface(aIID, aIFace);
   252         if (*aIFace)
   253             return NS_OK;
   254     }
   256     nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
   257     if (req)
   258     {
   259         return req->GetInterface(aIID, aIFace);
   260     }
   262     return NS_ERROR_NO_INTERFACE;
   263 }
   266 //*****************************************************************************
   267 // nsWebBrowserPersist::nsIWebBrowserPersist
   268 //*****************************************************************************
   270 /* attribute unsigned long persistFlags; */
   271 NS_IMETHODIMP nsWebBrowserPersist::GetPersistFlags(uint32_t *aPersistFlags)
   272 {
   273     NS_ENSURE_ARG_POINTER(aPersistFlags);
   274     *aPersistFlags = mPersistFlags;
   275     return NS_OK;
   276 }
   277 NS_IMETHODIMP nsWebBrowserPersist::SetPersistFlags(uint32_t aPersistFlags)
   278 {
   279     mPersistFlags = aPersistFlags;
   280     mReplaceExisting = (mPersistFlags & PERSIST_FLAGS_REPLACE_EXISTING_FILES) ? true : false;
   281     mSerializingOutput = (mPersistFlags & PERSIST_FLAGS_SERIALIZE_OUTPUT) ? true : false;
   282     return NS_OK;
   283 }
   285 /* readonly attribute unsigned long currentState; */
   286 NS_IMETHODIMP nsWebBrowserPersist::GetCurrentState(uint32_t *aCurrentState)
   287 {
   288     NS_ENSURE_ARG_POINTER(aCurrentState);
   289     if (mCompleted)
   290     {
   291         *aCurrentState = PERSIST_STATE_FINISHED;
   292     }
   293     else if (mFirstAndOnlyUse)
   294     {
   295         *aCurrentState = PERSIST_STATE_SAVING;
   296     }
   297     else
   298     {
   299         *aCurrentState = PERSIST_STATE_READY;
   300     }
   301     return NS_OK;
   302 }
   304 /* readonly attribute unsigned long result; */
   305 NS_IMETHODIMP nsWebBrowserPersist::GetResult(nsresult *aResult)
   306 {
   307     NS_ENSURE_ARG_POINTER(aResult);
   308     *aResult = mPersistResult;
   309     return NS_OK;
   310 }
   312 /* attribute nsIWebBrowserPersistProgress progressListener; */
   313 NS_IMETHODIMP nsWebBrowserPersist::GetProgressListener(
   314     nsIWebProgressListener * *aProgressListener)
   315 {
   316     NS_ENSURE_ARG_POINTER(aProgressListener);
   317     *aProgressListener = mProgressListener;
   318     NS_IF_ADDREF(*aProgressListener);
   319     return NS_OK;
   320 }
   322 NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
   323     nsIWebProgressListener * aProgressListener)
   324 {
   325     mProgressListener = aProgressListener;
   326     mProgressListener2 = do_QueryInterface(aProgressListener);
   327     mEventSink = do_GetInterface(aProgressListener);
   328     return NS_OK;
   329 }
   331 /* void saveURI (in nsIURI aURI, in nsISupports aCacheKey, in nsIURI aReferrer,
   332    in nsIInputStream aPostData, in wstring aExtraHeaders,
   333    in nsISupports aFile, in nsILoadContext aPrivayContext); */
   334 NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
   335     nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, nsILoadContext* aPrivacyContext)
   336 {
   337     return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, aFile,
   338                                aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
   339 }
   341 NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
   342     nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, bool aIsPrivate)
   343 {
   344     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   345     mFirstAndOnlyUse = false; // Stop people from reusing this object!
   347     nsCOMPtr<nsIURI> fileAsURI;
   348     nsresult rv;
   349     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   350     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   352     // SaveURI doesn't like broken uris.
   353     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
   354     rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
   355     return NS_FAILED(rv) ? rv : NS_OK;
   356 }
   358 /* void saveChannel (in nsIChannel aChannel, in nsISupports aFile); */
   359 NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
   360     nsIChannel *aChannel, nsISupports *aFile)
   361 {
   362     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   363     mFirstAndOnlyUse = false; // Stop people from reusing this object!
   365     nsCOMPtr<nsIURI> fileAsURI;
   366     nsresult rv;
   367     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   368     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   370     rv = aChannel->GetURI(getter_AddRefs(mURI));
   371     NS_ENSURE_SUCCESS(rv, rv);
   373     // SaveURI doesn't like broken uris.
   374     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
   375     rv = SaveChannelInternal(aChannel, fileAsURI, false);
   376     return NS_FAILED(rv) ? rv : NS_OK;
   377 }
   380 /* void saveDocument (in nsIDOMDocument aDocument, in nsIURI aFileURI,
   381    in nsIURI aDataPathURI, in string aOutputContentType,
   382    in unsigned long aEncodingFlags, in unsigned long aWrapColumn); */
   383 NS_IMETHODIMP nsWebBrowserPersist::SaveDocument(
   384     nsIDOMDocument *aDocument, nsISupports *aFile, nsISupports *aDataPath,
   385     const char *aOutputContentType, uint32_t aEncodingFlags, uint32_t aWrapColumn)
   386 {
   387     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
   388     mFirstAndOnlyUse = false; // Stop people from reusing this object!
   390     nsCOMPtr<nsIURI> fileAsURI;
   391     nsCOMPtr<nsIURI> datapathAsURI;
   392     nsresult rv;
   394     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
   395     nsCOMPtr<nsILoadContext> privacyContext = doc ? doc->GetLoadContext() : nullptr;
   396     mIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
   398     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
   399     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   400     if (aDataPath)
   401     {
   402         rv = GetValidURIFromObject(aDataPath, getter_AddRefs(datapathAsURI));
   403         NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
   404     }
   406     mWrapColumn = aWrapColumn;
   408     // Produce nsIDocumentEncoder encoding flags
   409     mEncodingFlags = 0;
   410     if (aEncodingFlags & ENCODE_FLAGS_SELECTION_ONLY)
   411         mEncodingFlags |= nsIDocumentEncoder::OutputSelectionOnly;
   412     if (aEncodingFlags & ENCODE_FLAGS_FORMATTED)
   413         mEncodingFlags |= nsIDocumentEncoder::OutputFormatted;
   414     if (aEncodingFlags & ENCODE_FLAGS_RAW)
   415         mEncodingFlags |= nsIDocumentEncoder::OutputRaw;
   416     if (aEncodingFlags & ENCODE_FLAGS_BODY_ONLY)
   417         mEncodingFlags |= nsIDocumentEncoder::OutputBodyOnly;
   418     if (aEncodingFlags & ENCODE_FLAGS_PREFORMATTED)
   419         mEncodingFlags |= nsIDocumentEncoder::OutputPreformatted;
   420     if (aEncodingFlags & ENCODE_FLAGS_WRAP)
   421         mEncodingFlags |= nsIDocumentEncoder::OutputWrap;
   422     if (aEncodingFlags & ENCODE_FLAGS_FORMAT_FLOWED)
   423         mEncodingFlags |= nsIDocumentEncoder::OutputFormatFlowed;
   424     if (aEncodingFlags & ENCODE_FLAGS_ABSOLUTE_LINKS)
   425         mEncodingFlags |= nsIDocumentEncoder::OutputAbsoluteLinks;
   426     if (aEncodingFlags & ENCODE_FLAGS_ENCODE_BASIC_ENTITIES)
   427         mEncodingFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities;
   428     if (aEncodingFlags & ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES)
   429         mEncodingFlags |= nsIDocumentEncoder::OutputEncodeLatin1Entities;
   430     if (aEncodingFlags & ENCODE_FLAGS_ENCODE_HTML_ENTITIES)
   431         mEncodingFlags |= nsIDocumentEncoder::OutputEncodeHTMLEntities;
   432     if (aEncodingFlags & ENCODE_FLAGS_ENCODE_W3C_ENTITIES)
   433         mEncodingFlags |= nsIDocumentEncoder::OutputEncodeW3CEntities;
   434     if (aEncodingFlags & ENCODE_FLAGS_CR_LINEBREAKS)
   435         mEncodingFlags |= nsIDocumentEncoder::OutputCRLineBreak;
   436     if (aEncodingFlags & ENCODE_FLAGS_LF_LINEBREAKS)
   437         mEncodingFlags |= nsIDocumentEncoder::OutputLFLineBreak;
   438     if (aEncodingFlags & ENCODE_FLAGS_NOSCRIPT_CONTENT)
   439         mEncodingFlags |= nsIDocumentEncoder::OutputNoScriptContent;
   440     if (aEncodingFlags & ENCODE_FLAGS_NOFRAMES_CONTENT)
   441         mEncodingFlags |= nsIDocumentEncoder::OutputNoFramesContent;
   443     if (aOutputContentType)
   444     {
   445         mContentType.AssignASCII(aOutputContentType);
   446     }
   448     rv = SaveDocumentInternal(aDocument, fileAsURI, datapathAsURI);
   450     // Now save the URIs that have been gathered
   452     if (NS_SUCCEEDED(rv) && datapathAsURI)
   453     {
   454         rv = SaveGatheredURIs(fileAsURI);
   455     }
   456     else if (mProgressListener)
   457     {
   458         // tell the listener we're done
   459         mProgressListener->OnStateChange(nullptr, nullptr,
   460                                          nsIWebProgressListener::STATE_START |
   461                                          nsIWebProgressListener::STATE_IS_NETWORK,
   462                                          NS_OK);
   463         mProgressListener->OnStateChange(nullptr, nullptr,
   464                                          nsIWebProgressListener::STATE_STOP |
   465                                          nsIWebProgressListener::STATE_IS_NETWORK,
   466                                          rv);
   467     }
   469     return rv;
   470 }
   472 /* void cancel(nsresult aReason); */
   473 NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
   474 {
   475     mCancel = true;
   476     EndDownload(aReason);
   477     return NS_OK;
   478 }
   481 /* void cancelSave(); */
   482 NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
   483 {
   484     return Cancel(NS_BINDING_ABORTED);
   485 }
   488 nsresult
   489 nsWebBrowserPersist::StartUpload(nsIStorageStream *storStream,
   490     nsIURI *aDestinationURI, const nsACString &aContentType)
   491 {
   492      // setup the upload channel if the destination is not local
   493     nsCOMPtr<nsIInputStream> inputstream;
   494     nsresult rv = storStream->NewInputStream(0, getter_AddRefs(inputstream));
   495     NS_ENSURE_TRUE(inputstream, NS_ERROR_FAILURE);
   496     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   497     return StartUpload(inputstream, aDestinationURI, aContentType);
   498 }
   500 nsresult
   501 nsWebBrowserPersist::StartUpload(nsIInputStream *aInputStream,
   502     nsIURI *aDestinationURI, const nsACString &aContentType)
   503 {
   504     nsCOMPtr<nsIChannel> destChannel;
   505     CreateChannelFromURI(aDestinationURI, getter_AddRefs(destChannel));
   506     nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(destChannel));
   507     NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
   509     // Set the upload stream
   510     // NOTE: ALL data must be available in "inputstream"
   511     nsresult rv = uploadChannel->SetUploadStream(aInputStream, aContentType, -1);
   512     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   513     rv = destChannel->AsyncOpen(this, nullptr);
   514     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   516     // add this to the upload list
   517     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
   518     mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
   520     return NS_OK;
   521 }
   523 nsresult
   524 nsWebBrowserPersist::SaveGatheredURIs(nsIURI *aFileAsURI)
   525 {
   526     nsresult rv = NS_OK;
   528     // Count how many URIs in the URI map require persisting
   529     uint32_t urisToPersist = 0;
   530     if (mURIMap.Count() > 0)
   531     {
   532         mURIMap.EnumerateRead(EnumCountURIsToPersist, &urisToPersist);
   533     }
   535     if (urisToPersist > 0)
   536     {
   537         // Persist each file in the uri map. The document(s)
   538         // will be saved after the last one of these is saved.
   539         mURIMap.EnumerateRead(EnumPersistURIs, this);
   540     }
   542     // if we don't have anything in mOutputMap (added from above enumeration)
   543     // then we build the doc list (SaveDocuments)
   544     if (mOutputMap.Count() == 0)
   545     {
   546         // There are no URIs to save, so just save the document(s)
   548         // State start notification
   549         uint32_t addToStateFlags = 0;
   550         if (mProgressListener)
   551         {
   552             if (mJustStartedLoading)
   553             {
   554                 addToStateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   555             }
   556             mProgressListener->OnStateChange(nullptr, nullptr,
   557                 nsIWebProgressListener::STATE_START | addToStateFlags, NS_OK);
   558         }
   560         rv = SaveDocuments();
   561         if (NS_FAILED(rv))
   562             EndDownload(rv);
   563         else if (aFileAsURI)
   564         {
   565             // local files won't trigger OnStopRequest so we call EndDownload here
   566             bool isFile = false;
   567             aFileAsURI->SchemeIs("file", &isFile);
   568             if (isFile)
   569                 EndDownload(NS_OK);
   570         }
   572         // State stop notification
   573         if (mProgressListener)
   574         {
   575             mProgressListener->OnStateChange(nullptr, nullptr,
   576                 nsIWebProgressListener::STATE_STOP | addToStateFlags, rv);
   577         }
   578     }
   580     return rv;
   581 }
   583 // this method returns true if there is another file to persist and false if not
   584 bool
   585 nsWebBrowserPersist::SerializeNextFile()
   586 {
   587     if (!mSerializingOutput)
   588     {
   589         return false;
   590     }
   592     nsresult rv = SaveGatheredURIs(nullptr);
   593     if (NS_FAILED(rv))
   594     {
   595         return false;
   596     }
   598     return (mURIMap.Count()
   599         || mUploadList.Count()
   600         || mDocList.Length()
   601         || mOutputMap.Count());
   602 }
   605 //*****************************************************************************
   606 // nsWebBrowserPersist::nsIRequestObserver
   607 //*****************************************************************************
   609 NS_IMETHODIMP nsWebBrowserPersist::OnStartRequest(
   610     nsIRequest* request, nsISupports *ctxt)
   611 {
   612     if (mProgressListener)
   613     {
   614         uint32_t stateFlags = nsIWebProgressListener::STATE_START |
   615                               nsIWebProgressListener::STATE_IS_REQUEST;
   616         if (mJustStartedLoading)
   617         {
   618             stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   619         }
   620         mProgressListener->OnStateChange(nullptr, request, stateFlags, NS_OK);
   621     }
   623     mJustStartedLoading = false;
   625     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   626     NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   628     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   629     OutputData *data = mOutputMap.Get(keyPtr);
   631     // NOTE: This code uses the channel as a hash key so it will not
   632     //       recognize redirected channels because the key is not the same.
   633     //       When that happens we remove and add the data entry to use the
   634     //       new channel as the hash key.
   635     if (!data)
   636     {
   637         UploadData *upData = mUploadList.Get(keyPtr);
   638         if (!upData)
   639         {
   640             // Redirect? Try and fixup the output table
   641             nsresult rv = FixRedirectedChannelEntry(channel);
   642             NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   644             // Should be able to find the data after fixup unless redirects
   645             // are disabled.
   646             data = mOutputMap.Get(keyPtr);
   647             if (!data)
   648             {
   649                 return NS_ERROR_FAILURE;
   650             }
   651         }
   652     }
   654     if (data && data->mFile)
   655     {
   656         // If PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION is set in mPersistFlags,
   657         // try to determine whether this channel needs to apply Content-Encoding
   658         // conversions.
   659         NS_ASSERTION(!((mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION) &&
   660                       (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)),
   661                      "Conflict in persist flags: both AUTODETECT and NO_CONVERSION set");
   662         if (mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION)
   663             SetApplyConversionIfNeeded(channel);
   665         if (data->mCalcFileExt && !(mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES))
   666         {
   667             // this is the first point at which the server can tell us the mimetype
   668             CalculateAndAppendFileExt(data->mFile, channel, data->mOriginalLocation);
   670             // now make filename conformant and unique
   671             CalculateUniqueFilename(data->mFile);
   672         }
   674         // compare uris and bail before we add to output map if they are equal
   675         bool isEqual = false;
   676         if (NS_SUCCEEDED(data->mFile->Equals(data->mOriginalLocation, &isEqual))
   677             && isEqual)
   678         {
   679             // remove from output map
   680             mOutputMap.Remove(keyPtr);
   682             // cancel; we don't need to know any more
   683             // stop request will get called
   684             request->Cancel(NS_BINDING_ABORTED);
   685         }
   686     }
   688     return NS_OK;
   689 }
   691 NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest(
   692     nsIRequest* request, nsISupports *ctxt, nsresult status)
   693 {
   694     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   695     OutputData *data = mOutputMap.Get(keyPtr);
   696     if (data)
   697     {
   698         if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(status))
   699             SendErrorStatusChange(true, status, request, data->mFile);
   701         // This will automatically close the output stream
   702         mOutputMap.Remove(keyPtr);
   703     }
   704     else
   705     {
   706         // if we didn't find the data in mOutputMap, try mUploadList
   707         UploadData *upData = mUploadList.Get(keyPtr);
   708         if (upData)
   709         {
   710             mUploadList.Remove(keyPtr);
   711         }
   712     }
   714     // ensure we call SaveDocuments if we:
   715     // 1) aren't canceling
   716     // 2) we haven't triggered the save (which we only want to trigger once)
   717     // 3) we aren't serializing (which will call it inside SerializeNextFile)
   718     if (mOutputMap.Count() == 0 && !mCancel && !mStartSaving && !mSerializingOutput)
   719     {
   720         nsresult rv = SaveDocuments();
   721         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   722     }
   724     bool completed = false;
   725     if (mOutputMap.Count() == 0 && mUploadList.Count() == 0 && !mCancel)
   726     {
   727         // if no documents left in mDocList, --> done
   728         // if we have no files left to serialize and no error result, --> done
   729         if (mDocList.Length() == 0
   730             || (!SerializeNextFile() && NS_SUCCEEDED(mPersistResult)))
   731         {
   732             completed = true;
   733         }
   734     }
   736     if (completed)
   737     {
   738         // we're all done, do our cleanup
   739         EndDownload(status);
   740     }
   742     if (mProgressListener)
   743     {
   744         uint32_t stateFlags = nsIWebProgressListener::STATE_STOP |
   745                               nsIWebProgressListener::STATE_IS_REQUEST;
   746         if (completed)
   747         {
   748             stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
   749         }
   750         mProgressListener->OnStateChange(nullptr, request, stateFlags, status);
   751     }
   752     if (completed)
   753     {
   754         mProgressListener = nullptr;
   755         mProgressListener2 = nullptr;
   756         mEventSink = nullptr;
   757     }
   759     return NS_OK;
   760 }
   762 //*****************************************************************************
   763 // nsWebBrowserPersist::nsIStreamListener
   764 //*****************************************************************************
   766 NS_IMETHODIMP
   767 nsWebBrowserPersist::OnDataAvailable(
   768     nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream,
   769     uint64_t aOffset, uint32_t aLength)
   770 {
   771     bool cancel = mCancel;
   772     if (!cancel)
   773     {
   774         nsresult rv = NS_OK;
   775         uint32_t bytesRemaining = aLength;
   777         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   778         NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   780         nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   781         OutputData *data = mOutputMap.Get(keyPtr);
   782         if (!data) {
   783             // might be uploadData; consume necko's buffer and bail...
   784             uint32_t n;
   785             return aIStream->ReadSegments(NS_DiscardSegment, nullptr, aLength, &n);
   786         }
   788         bool readError = true;
   790         // Make the output stream
   791         if (!data->mStream)
   792         {
   793             rv = MakeOutputStream(data->mFile, getter_AddRefs(data->mStream));
   794             if (NS_FAILED(rv))
   795             {
   796                 readError = false;
   797                 cancel = true;
   798             }
   799         }
   801         // Read data from the input and write to the output
   802         char buffer[8192];
   803         uint32_t bytesRead;
   804         while (!cancel && bytesRemaining)
   805         {
   806             readError = true;
   807             rv = aIStream->Read(buffer,
   808                                 std::min(uint32_t(sizeof(buffer)), bytesRemaining),
   809                                 &bytesRead);
   810             if (NS_SUCCEEDED(rv))
   811             {
   812                 readError = false;
   813                 // Write out the data until something goes wrong, or, it is
   814                 // all written.  We loop because for some errors (e.g., disk
   815                 // full), we get NS_OK with some bytes written, then an error.
   816                 // So, we want to write again in that case to get the actual
   817                 // error code.
   818                 const char *bufPtr = buffer; // Where to write from.
   819                 while (NS_SUCCEEDED(rv) && bytesRead)
   820                 {
   821                     uint32_t bytesWritten = 0;
   822                     rv = data->mStream->Write(bufPtr, bytesRead, &bytesWritten);
   823                     if (NS_SUCCEEDED(rv))
   824                     {
   825                         bytesRead -= bytesWritten;
   826                         bufPtr += bytesWritten;
   827                         bytesRemaining -= bytesWritten;
   828                         // Force an error if (for some reason) we get NS_OK but
   829                         // no bytes written.
   830                         if (!bytesWritten)
   831                         {
   832                             rv = NS_ERROR_FAILURE;
   833                             cancel = true;
   834                         }
   835                     }
   836                     else
   837                     {
   838                         // Disaster - can't write out the bytes - disk full / permission?
   839                         cancel = true;
   840                     }
   841                 }
   842             }
   843             else
   844             {
   845                 // Disaster - can't read the bytes - broken link / file error?
   846                 cancel = true;
   847             }
   848         }
   850         int64_t channelContentLength = -1;
   851         if (!cancel &&
   852             NS_SUCCEEDED(channel->GetContentLength(&channelContentLength)))
   853         {
   854             // if we get -1 at this point, we didn't get content-length header
   855             // assume that we got all of the data and push what we have;
   856             // that's the best we can do now
   857             if ((-1 == channelContentLength) ||
   858                 ((channelContentLength - (aOffset + aLength)) == 0))
   859             {
   860                 NS_WARN_IF_FALSE(channelContentLength != -1,
   861                     "nsWebBrowserPersist::OnDataAvailable() no content length "
   862                     "header, pushing what we have");
   863                 // we're done with this pass; see if we need to do upload
   864                 nsAutoCString contentType;
   865                 channel->GetContentType(contentType);
   866                 // if we don't have the right type of output stream then it's a local file
   867                 nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(data->mStream));
   868                 if (storStream)
   869                 {
   870                     data->mStream->Close();
   871                     data->mStream = nullptr; // null out stream so we don't close it later
   872                     rv = StartUpload(storStream, data->mFile, contentType);
   873                     if (NS_FAILED(rv))
   874                     {
   875                         readError = false;
   876                         cancel = true;
   877                     }
   878                 }
   879             }
   880         }
   882         // Notify listener if an error occurred.
   883         if (cancel)
   884         {
   885             SendErrorStatusChange(readError, rv,
   886                 readError ? request : nullptr, data->mFile);
   887         }
   888     }
   890     // Cancel reading?
   891     if (cancel)
   892     {
   893         EndDownload(NS_BINDING_ABORTED);
   894     }
   896     return NS_OK;
   897 }
   900 //*****************************************************************************
   901 // nsWebBrowserPersist::nsIProgressEventSink
   902 //*****************************************************************************
   904 /* void onProgress (in nsIRequest request, in nsISupports ctxt,
   905     in unsigned long long aProgress, in unsigned long long aProgressMax); */
   906 NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
   907     nsIRequest *request, nsISupports *ctxt, uint64_t aProgress,
   908     uint64_t aProgressMax)
   909 {
   910     if (!mProgressListener)
   911     {
   912         return NS_OK;
   913     }
   915     // Store the progress of this request
   916     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
   917     OutputData *data = mOutputMap.Get(keyPtr);
   918     if (data)
   919     {
   920         data->mSelfProgress = int64_t(aProgress);
   921         data->mSelfProgressMax = int64_t(aProgressMax);
   922     }
   923     else
   924     {
   925         UploadData *upData = mUploadList.Get(keyPtr);
   926         if (upData)
   927         {
   928             upData->mSelfProgress = int64_t(aProgress);
   929             upData->mSelfProgressMax = int64_t(aProgressMax);
   930         }
   931     }
   933     // Notify listener of total progress
   934     CalcTotalProgress();
   935     if (mProgressListener2)
   936     {
   937       mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
   938             aProgressMax, mTotalCurrentProgress, mTotalMaxProgress);
   939     }
   940     else
   941     {
   942       // have to truncate 64-bit to 32bit
   943       mProgressListener->OnProgressChange(nullptr, request, uint64_t(aProgress),
   944               uint64_t(aProgressMax), mTotalCurrentProgress, mTotalMaxProgress);
   945     }
   947     // If our progress listener implements nsIProgressEventSink,
   948     // forward the notification
   949     if (mEventSink)
   950     {
   951         mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
   952     }
   954     return NS_OK;
   955 }
   957 /* void onStatus (in nsIRequest request, in nsISupports ctxt,
   958     in nsresult status, in wstring statusArg); */
   959 NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
   960     nsIRequest *request, nsISupports *ctxt, nsresult status,
   961     const char16_t *statusArg)
   962 {
   963     if (mProgressListener)
   964     {
   965         // We need to filter out non-error error codes.
   966         // Is the only NS_SUCCEEDED value NS_OK?
   967         switch ( status )
   968         {
   969         case NS_NET_STATUS_RESOLVING_HOST:
   970         case NS_NET_STATUS_RESOLVED_HOST:
   971         case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
   972         case NS_NET_STATUS_END_FTP_TRANSACTION:
   973         case NS_NET_STATUS_CONNECTING_TO:
   974         case NS_NET_STATUS_CONNECTED_TO:
   975         case NS_NET_STATUS_SENDING_TO:
   976         case NS_NET_STATUS_RECEIVING_FROM:
   977         case NS_NET_STATUS_WAITING_FOR:
   978         case NS_NET_STATUS_READING:
   979         case NS_NET_STATUS_WRITING:
   980             break;
   982         default:
   983             // Pass other notifications (for legitimate errors) along.
   984             mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
   985             break;
   986         }
   988     }
   990     // If our progress listener implements nsIProgressEventSink,
   991     // forward the notification
   992     if (mEventSink)
   993     {
   994         mEventSink->OnStatus(request, ctxt, status, statusArg);
   995     }
   997     return NS_OK;
   998 }
  1001 //*****************************************************************************
  1002 // nsWebBrowserPersist private methods
  1003 //*****************************************************************************
  1005 // Convert error info into proper message text and send OnStatusChange notification
  1006 // to the web progress listener.
  1007 nsresult nsWebBrowserPersist::SendErrorStatusChange(
  1008     bool aIsReadError, nsresult aResult, nsIRequest *aRequest, nsIURI *aURI)
  1010     NS_ENSURE_ARG_POINTER(aURI);
  1012     if (!mProgressListener)
  1014         // Do nothing
  1015         return NS_OK;
  1018     // Get the file path or spec from the supplied URI
  1019     nsCOMPtr<nsIFile> file;
  1020     GetLocalFileFromURI(aURI, getter_AddRefs(file));
  1021     nsAutoString path;
  1022     if (file)
  1024         file->GetPath(path);
  1026     else
  1028         nsAutoCString fileurl;
  1029         aURI->GetSpec(fileurl);
  1030         AppendUTF8toUTF16(fileurl, path);
  1033     nsAutoString msgId;
  1034     switch(aResult)
  1036     case NS_ERROR_FILE_NAME_TOO_LONG:
  1037         // File name too long.
  1038         msgId.AssignLiteral("fileNameTooLongError");
  1039         break;
  1040     case NS_ERROR_FILE_ALREADY_EXISTS:
  1041         // File exists with same name as directory.
  1042         msgId.AssignLiteral("fileAlreadyExistsError");
  1043         break;
  1044     case NS_ERROR_FILE_DISK_FULL:
  1045     case NS_ERROR_FILE_NO_DEVICE_SPACE:
  1046         // Out of space on target volume.
  1047         msgId.AssignLiteral("diskFull");
  1048         break;
  1050     case NS_ERROR_FILE_READ_ONLY:
  1051         // Attempt to write to read/only file.
  1052         msgId.AssignLiteral("readOnly");
  1053         break;
  1055     case NS_ERROR_FILE_ACCESS_DENIED:
  1056         // Attempt to write without sufficient permissions.
  1057         msgId.AssignLiteral("accessError");
  1058         break;
  1060     default:
  1061         // Generic read/write error message.
  1062         if (aIsReadError)
  1063             msgId.AssignLiteral("readError");
  1064         else
  1065             msgId.AssignLiteral("writeError");
  1066         break;
  1068     // Get properties file bundle and extract status string.
  1069     nsresult rv;
  1070     nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
  1071     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
  1073     nsCOMPtr<nsIStringBundle> bundle;
  1074     rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
  1075     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
  1077     nsXPIDLString msgText;
  1078     const char16_t *strings[1];
  1079     strings[0] = path.get();
  1080     rv = bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText));
  1081     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1083     mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
  1085     return NS_OK;
  1088 nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
  1090     NS_ENSURE_ARG_POINTER(aObject);
  1091     NS_ENSURE_ARG_POINTER(aURI);
  1093     nsCOMPtr<nsIFile> objAsFile = do_QueryInterface(aObject);
  1094     if (objAsFile)
  1096         return NS_NewFileURI(aURI, objAsFile);
  1098     nsCOMPtr<nsIURI> objAsURI = do_QueryInterface(aObject);
  1099     if (objAsURI)
  1101         *aURI = objAsURI;
  1102         NS_ADDREF(*aURI);
  1103         return NS_OK;
  1106     return NS_ERROR_FAILURE;
  1109 nsresult nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const
  1111     nsresult rv;
  1113     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  1114     if (NS_FAILED(rv))
  1115         return rv;
  1117     nsCOMPtr<nsIFile> file;
  1118     rv = fileURL->GetFile(getter_AddRefs(file));
  1119     if (NS_FAILED(rv)) {
  1120         return rv;
  1123     file.forget(aLocalFile);
  1124     return NS_OK;
  1127 nsresult nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath) const
  1129     NS_ENSURE_ARG_POINTER(aURI);
  1131     nsAutoCString newPath;
  1132     nsresult rv = aURI->GetPath(newPath);
  1133     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1135     // Append a forward slash if necessary
  1136     int32_t len = newPath.Length();
  1137     if (len > 0 && newPath.CharAt(len - 1) != '/')
  1139         newPath.Append('/');
  1142     // Store the path back on the URI
  1143     AppendUTF16toUTF8(aPath, newPath);
  1144     aURI->SetPath(newPath);
  1146     return NS_OK;
  1149 nsresult nsWebBrowserPersist::SaveURIInternal(
  1150     nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
  1151     nsIInputStream *aPostData, const char *aExtraHeaders,
  1152     nsIURI *aFile, bool aCalcFileExt, bool aIsPrivate)
  1154     NS_ENSURE_ARG_POINTER(aURI);
  1155     NS_ENSURE_ARG_POINTER(aFile);
  1157     nsresult rv = NS_OK;
  1159     mURI = aURI;
  1161     nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
  1162     if (mPersistFlags & PERSIST_FLAGS_BYPASS_CACHE)
  1164         loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
  1166     else if (mPersistFlags & PERSIST_FLAGS_FROM_CACHE)
  1168         loadFlags |= nsIRequest::LOAD_FROM_CACHE;
  1171     // Extract the cache key
  1172     nsCOMPtr<nsISupports> cacheKey;
  1173     if (aCacheKey)
  1175         // Test if the cache key is actually a web page descriptor (docshell)
  1176         // or session history entry.
  1177         nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(aCacheKey);
  1178         if (!shEntry)
  1180             nsCOMPtr<nsIWebPageDescriptor> webPageDescriptor =
  1181                 do_QueryInterface(aCacheKey);
  1182             if (webPageDescriptor)
  1184                 nsCOMPtr<nsISupports> currentDescriptor;
  1185                 webPageDescriptor->GetCurrentDescriptor(getter_AddRefs(currentDescriptor));
  1186                 shEntry = do_QueryInterface(currentDescriptor);
  1190         if (shEntry)
  1192             shEntry->GetCacheKey(getter_AddRefs(cacheKey));
  1194         else
  1196             // Assume a plain cache key
  1197             cacheKey = aCacheKey;
  1201     // Open a channel to the URI
  1202     nsCOMPtr<nsIChannel> inputChannel;
  1203     rv = NS_NewChannel(getter_AddRefs(inputChannel), aURI,
  1204             nullptr, nullptr, static_cast<nsIInterfaceRequestor*>(this),
  1205             loadFlags);
  1207     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
  1208     if (pbChannel)
  1210         pbChannel->SetPrivate(aIsPrivate);
  1213     if (NS_FAILED(rv) || inputChannel == nullptr)
  1215         EndDownload(NS_ERROR_FAILURE);
  1216         return NS_ERROR_FAILURE;
  1219     // Disable content conversion
  1220     if (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)
  1222         nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
  1223         if (encodedChannel)
  1225             encodedChannel->SetApplyConversion(false);
  1229     if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
  1231         nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
  1232                 do_QueryInterface(inputChannel);
  1233         if (httpChannelInternal)
  1234             httpChannelInternal->SetForceAllowThirdPartyCookie(true);
  1237     // Set the referrer, post data and headers if any
  1238     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
  1239     if (httpChannel)
  1241         // Referrer
  1242         if (aReferrer)
  1244             httpChannel->SetReferrer(aReferrer);
  1247         // Post data
  1248         if (aPostData)
  1250             nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
  1251             if (stream)
  1253                 // Rewind the postdata stream
  1254                 stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  1255                 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
  1256                 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
  1257                 // Attach the postdata to the http channel
  1258                 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
  1262         // Cache key
  1263         nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
  1264         if (cacheChannel && cacheKey)
  1266             cacheChannel->SetCacheKey(cacheKey);
  1269         // Headers
  1270         if (aExtraHeaders)
  1272             nsAutoCString oneHeader;
  1273             nsAutoCString headerName;
  1274             nsAutoCString headerValue;
  1275             int32_t crlf = 0;
  1276             int32_t colon = 0;
  1277             const char *kWhitespace = "\b\t\r\n ";
  1278             nsAutoCString extraHeaders(aExtraHeaders);
  1279             while (true)
  1281                 crlf = extraHeaders.Find("\r\n", true);
  1282                 if (crlf == -1)
  1283                     break;
  1284                 extraHeaders.Mid(oneHeader, 0, crlf);
  1285                 extraHeaders.Cut(0, crlf + 2);
  1286                 colon = oneHeader.Find(":");
  1287                 if (colon == -1)
  1288                     break; // Should have a colon
  1289                 oneHeader.Left(headerName, colon);
  1290                 colon++;
  1291                 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
  1292                 headerName.Trim(kWhitespace);
  1293                 headerValue.Trim(kWhitespace);
  1294                 // Add the header (merging if required)
  1295                 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
  1296                 if (NS_FAILED(rv))
  1298                     EndDownload(NS_ERROR_FAILURE);
  1299                     return NS_ERROR_FAILURE;
  1304     return SaveChannelInternal(inputChannel, aFile, aCalcFileExt);
  1307 nsresult nsWebBrowserPersist::SaveChannelInternal(
  1308     nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
  1310     NS_ENSURE_ARG_POINTER(aChannel);
  1311     NS_ENSURE_ARG_POINTER(aFile);
  1313     // The default behaviour of SaveChannelInternal is to download the source
  1314     // into a storage stream and upload that to the target. MakeOutputStream
  1315     // special-cases a file target and creates a file output stream directly.
  1316     // We want to special-case a file source and create a file input stream,
  1317     // but we don't need to do this in the case of a file target.
  1318     nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
  1319     nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
  1320     if (fc && !fu) {
  1321         nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
  1322         nsresult rv = aChannel->Open(getter_AddRefs(fileInputStream));
  1323         NS_ENSURE_SUCCESS(rv, rv);
  1324         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
  1325                                        fileInputStream, BUFFERED_OUTPUT_SIZE);
  1326         NS_ENSURE_SUCCESS(rv, rv);
  1327         nsAutoCString contentType;
  1328         aChannel->GetContentType(contentType);
  1329         return StartUpload(bufferedInputStream, aFile, contentType);
  1332     // Read from the input channel
  1333     nsresult rv = aChannel->AsyncOpen(this, nullptr);
  1334     if (rv == NS_ERROR_NO_CONTENT)
  1336         // Assume this is a protocol such as mailto: which does not feed out
  1337         // data and just ignore it.
  1338         return NS_SUCCESS_DONT_FIXUP;
  1341     if (NS_FAILED(rv))
  1343         // Opening failed, but do we care?
  1344         if (mPersistFlags & PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS)
  1346             SendErrorStatusChange(true, rv, aChannel, aFile);
  1347             EndDownload(NS_ERROR_FAILURE);
  1348             return NS_ERROR_FAILURE;
  1350         return NS_SUCCESS_DONT_FIXUP;
  1353     // Add the output transport to the output map with the channel as the key
  1354     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aChannel);
  1355     mOutputMap.Put(keyPtr, new OutputData(aFile, mURI, aCalcFileExt));
  1357     return NS_OK;
  1360 nsresult
  1361 nsWebBrowserPersist::GetExtensionForContentType(const char16_t *aContentType, char16_t **aExt)
  1363     NS_ENSURE_ARG_POINTER(aContentType);
  1364     NS_ENSURE_ARG_POINTER(aExt);
  1366     *aExt = nullptr;
  1368     nsresult rv;
  1369     if (!mMIMEService)
  1371         mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
  1372         NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
  1375     nsCOMPtr<nsIMIMEInfo> mimeInfo;
  1376     nsAutoCString contentType;
  1377     contentType.AssignWithConversion(aContentType);
  1378     nsAutoCString ext;
  1379     rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
  1380     if (NS_SUCCEEDED(rv))
  1382         *aExt = UTF8ToNewUnicode(ext);
  1383         NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
  1384         return NS_OK;
  1387     return NS_ERROR_FAILURE;
  1390 nsresult
  1391 nsWebBrowserPersist::GetDocumentExtension(nsIDOMDocument *aDocument, char16_t **aExt)
  1393     NS_ENSURE_ARG_POINTER(aDocument);
  1394     NS_ENSURE_ARG_POINTER(aExt);
  1396     nsXPIDLString contentType;
  1397     nsresult rv = GetDocEncoderContentType(aDocument, nullptr, getter_Copies(contentType));
  1398     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1399     return GetExtensionForContentType(contentType.get(), aExt);
  1402 nsresult
  1403 nsWebBrowserPersist::GetDocEncoderContentType(nsIDOMDocument *aDocument, const char16_t *aContentType, char16_t **aRealContentType)
  1405     NS_ENSURE_ARG_POINTER(aDocument);
  1406     NS_ENSURE_ARG_POINTER(aRealContentType);
  1408     *aRealContentType = nullptr;
  1410     nsAutoString defaultContentType(NS_LITERAL_STRING("text/html"));
  1412     // Get the desired content type for the document, either by using the one
  1413     // supplied or from the document itself.
  1415     nsAutoString contentType;
  1416     if (aContentType)
  1418         contentType.Assign(aContentType);
  1420     else
  1422         // Get the content type from the document
  1423         nsAutoString type;
  1424         if (NS_SUCCEEDED(aDocument->GetContentType(type)) && !type.IsEmpty())
  1425             contentType.Assign(type);
  1428     // Check that an encoder actually exists for the desired output type. The
  1429     // following content types will usually yield an encoder.
  1430     //
  1431     //   text/xml
  1432     //   application/xml
  1433     //   application/xhtml+xml
  1434     //   image/svg+xml
  1435     //   text/html
  1436     //   text/plain
  1438     if (!contentType.IsEmpty() &&
  1439         !contentType.Equals(defaultContentType, nsCaseInsensitiveStringComparator()))
  1441         // Check if there is an encoder for the desired content type
  1442         nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  1443         AppendUTF16toUTF8(contentType, contractID);
  1445         nsCOMPtr<nsIComponentRegistrar> registrar;
  1446         NS_GetComponentRegistrar(getter_AddRefs(registrar));
  1447         if (registrar)
  1449             bool result;
  1450             nsresult rv = registrar->IsContractIDRegistered(contractID.get(), &result);
  1451             if (NS_SUCCEEDED(rv) && result)
  1453                 *aRealContentType = ToNewUnicode(contentType);
  1458     // Use the default if no encoder exists for the desired one
  1459     if (!*aRealContentType)
  1461         *aRealContentType = ToNewUnicode(defaultContentType);
  1464     NS_ENSURE_TRUE(*aRealContentType, NS_ERROR_OUT_OF_MEMORY);
  1466     return NS_OK;
  1469 nsresult nsWebBrowserPersist::SaveDocumentInternal(
  1470     nsIDOMDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
  1472     NS_ENSURE_ARG_POINTER(aDocument);
  1473     NS_ENSURE_ARG_POINTER(aFile);
  1475     // See if we can get the local file representation of this URI
  1476     nsCOMPtr<nsIFile> localFile;
  1477     nsresult rv = GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
  1479     nsCOMPtr<nsIFile> localDataPath;
  1480     if (NS_SUCCEEDED(rv) && aDataPath)
  1482         // See if we can get the local file representation of this URI
  1483         rv = GetLocalFileFromURI(aDataPath, getter_AddRefs(localDataPath));
  1484         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1487     nsCOMPtr<nsIDOMNode> docAsNode = do_QueryInterface(aDocument);
  1489     // Persist the main document
  1490     nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDocument));
  1491     if (!doc) {
  1492         return NS_ERROR_UNEXPECTED;
  1494     mURI = doc->GetDocumentURI();
  1496     nsCOMPtr<nsIURI> oldBaseURI = mCurrentBaseURI;
  1497     nsAutoCString oldCharset(mCurrentCharset);
  1499     // Store the base URI and the charset
  1500     mCurrentBaseURI = doc->GetBaseURI();
  1501     mCurrentCharset = doc->GetDocumentCharacterSet();
  1503     // Does the caller want to fixup the referenced URIs and save those too?
  1504     if (aDataPath)
  1506         // Basic steps are these.
  1507         //
  1508         // 1. Iterate through the document (and subdocuments) building a list
  1509         //    of unique URIs.
  1510         // 2. For each URI create an OutputData entry and open a channel to save
  1511         //    it. As each URI is saved, discover the mime type and fix up the
  1512         //    local filename with the correct extension.
  1513         // 3. Store the document in a list and wait for URI persistence to finish
  1514         // 4. After URI persistence completes save the list of documents,
  1515         //    fixing it up as it goes out to file.
  1517         nsCOMPtr<nsIURI> oldDataPath = mCurrentDataPath;
  1518         bool oldDataPathIsRelative = mCurrentDataPathIsRelative;
  1519         nsCString oldCurrentRelativePathToData = mCurrentRelativePathToData;
  1520         uint32_t oldThingsToPersist = mCurrentThingsToPersist;
  1522         mCurrentDataPathIsRelative = false;
  1523         mCurrentDataPath = aDataPath;
  1524         mCurrentRelativePathToData = "";
  1525         mCurrentThingsToPersist = 0;
  1527         // Determine if the specified data path is relative to the
  1528         // specified file, (e.g. c:\docs\htmldata is relative to
  1529         // c:\docs\myfile.htm, but not to d:\foo\data.
  1531         // Starting with the data dir work back through its parents
  1532         // checking if one of them matches the base directory.
  1534         if (localDataPath && localFile)
  1536             nsCOMPtr<nsIFile> baseDir;
  1537             localFile->GetParent(getter_AddRefs(baseDir));
  1539             nsAutoCString relativePathToData;
  1540             nsCOMPtr<nsIFile> dataDirParent;
  1541             dataDirParent = localDataPath;
  1542             while (dataDirParent)
  1544                 bool sameDir = false;
  1545                 dataDirParent->Equals(baseDir, &sameDir);
  1546                 if (sameDir)
  1548                     mCurrentRelativePathToData = relativePathToData;
  1549                     mCurrentDataPathIsRelative = true;
  1550                     break;
  1553                 nsAutoString dirName;
  1554                 dataDirParent->GetLeafName(dirName);
  1556                 nsAutoCString newRelativePathToData;
  1557                 newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
  1558                                       + NS_LITERAL_CSTRING("/")
  1559                                       + relativePathToData;
  1560                 relativePathToData = newRelativePathToData;
  1562                 nsCOMPtr<nsIFile> newDataDirParent;
  1563                 rv = dataDirParent->GetParent(getter_AddRefs(newDataDirParent));
  1564                 dataDirParent = newDataDirParent;
  1567         else
  1569             // generate a relative path if possible
  1570             nsCOMPtr<nsIURL> pathToBaseURL(do_QueryInterface(aFile));
  1571             if (pathToBaseURL)
  1573                 nsAutoCString relativePath;  // nsACString
  1574                 if (NS_SUCCEEDED(pathToBaseURL->GetRelativeSpec(aDataPath, relativePath)))
  1576                     mCurrentDataPathIsRelative = true;
  1577                     mCurrentRelativePathToData = relativePath;
  1582         // Store the document in a list so when URI persistence is done and the
  1583         // filenames of saved URIs are known, the documents can be fixed up and
  1584         // saved
  1586         DocData *docData = new DocData;
  1587         docData->mBaseURI = mCurrentBaseURI;
  1588         docData->mCharset = mCurrentCharset;
  1589         docData->mDocument = aDocument;
  1590         docData->mFile = aFile;
  1591         docData->mRelativePathToData = mCurrentRelativePathToData;
  1592         docData->mDataPath = mCurrentDataPath;
  1593         docData->mDataPathIsRelative = mCurrentDataPathIsRelative;
  1594         mDocList.AppendElement(docData);
  1596         // Walk the DOM gathering a list of externally referenced URIs in the uri map
  1597         nsCOMPtr<nsIDOMTreeWalker> walker;
  1598         rv = aDocument->CreateTreeWalker(docAsNode,
  1599             nsIDOMNodeFilter::SHOW_ELEMENT |
  1600                 nsIDOMNodeFilter::SHOW_DOCUMENT |
  1601                 nsIDOMNodeFilter::SHOW_PROCESSING_INSTRUCTION,
  1602             nullptr, 1, getter_AddRefs(walker));
  1603         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1605         nsCOMPtr<nsIDOMNode> currentNode;
  1606         walker->GetCurrentNode(getter_AddRefs(currentNode));
  1607         while (currentNode)
  1609             OnWalkDOMNode(currentNode);
  1610             walker->NextNode(getter_AddRefs(currentNode));
  1613         // If there are things to persist, create a directory to hold them
  1614         if (mCurrentThingsToPersist > 0)
  1616             if (localDataPath)
  1618                 bool exists = false;
  1619                 bool haveDir = false;
  1621                 localDataPath->Exists(&exists);
  1622                 if (exists)
  1624                     localDataPath->IsDirectory(&haveDir);
  1626                 if (!haveDir)
  1628                     rv = localDataPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
  1629                     if (NS_SUCCEEDED(rv))
  1630                         haveDir = true;
  1631                     else
  1632                         SendErrorStatusChange(false, rv, nullptr, aFile);
  1634                 if (!haveDir)
  1636                     EndDownload(NS_ERROR_FAILURE);
  1637                     mCurrentBaseURI = oldBaseURI;
  1638                     mCurrentCharset = oldCharset;
  1639                     return NS_ERROR_FAILURE;
  1641                 if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
  1643                     // Add to list of things to delete later if all goes wrong
  1644                     CleanupData *cleanupData = new CleanupData;
  1645                     NS_ENSURE_TRUE(cleanupData, NS_ERROR_OUT_OF_MEMORY);
  1646                     cleanupData->mFile = localDataPath;
  1647                     cleanupData->mIsDirectory = true;
  1648                     mCleanupList.AppendElement(cleanupData);
  1653         mCurrentThingsToPersist = oldThingsToPersist;
  1654         mCurrentDataPath = oldDataPath;
  1655         mCurrentDataPathIsRelative = oldDataPathIsRelative;
  1656         mCurrentRelativePathToData = oldCurrentRelativePathToData;
  1658     else
  1660         // Set the document base to ensure relative links still work
  1661         SetDocumentBase(aDocument, mCurrentBaseURI);
  1663         // Get the content type to save with
  1664         nsXPIDLString realContentType;
  1665         GetDocEncoderContentType(aDocument,
  1666             !mContentType.IsEmpty() ? mContentType.get() : nullptr,
  1667             getter_Copies(realContentType));
  1669         nsAutoCString contentType; contentType.AssignWithConversion(realContentType);
  1670         nsAutoCString charType; // Empty
  1672         // Save the document
  1673         rv = SaveDocumentWithFixup(
  1674             aDocument,
  1675             nullptr,  // no dom fixup
  1676             aFile,
  1677             mReplaceExisting,
  1678             contentType,
  1679             charType,
  1680             mEncodingFlags);
  1681         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1684     mCurrentBaseURI = oldBaseURI;
  1685     mCurrentCharset = oldCharset;
  1687     return NS_OK;
  1690 nsresult nsWebBrowserPersist::SaveDocuments()
  1692     nsresult rv = NS_OK;
  1694     mStartSaving = true;
  1696     // Iterate through all queued documents, saving them to file and fixing
  1697     // them up on the way.
  1699     uint32_t i;
  1700     for (i = 0; i < mDocList.Length(); i++)
  1702         DocData *docData = mDocList.ElementAt(i);
  1703         if (!docData)
  1705             rv = NS_ERROR_FAILURE;
  1706             break;
  1709         mCurrentBaseURI = docData->mBaseURI;
  1710         mCurrentCharset = docData->mCharset;
  1712         // Save the document, fixing it up with the new URIs as we do
  1714         nsEncoderNodeFixup *nodeFixup;
  1715         nodeFixup = new nsEncoderNodeFixup;
  1716         if (nodeFixup)
  1717             nodeFixup->mWebBrowserPersist = this;
  1719         // Get the content type
  1720         nsXPIDLString realContentType;
  1721         GetDocEncoderContentType(docData->mDocument,
  1722             !mContentType.IsEmpty() ? mContentType.get() : nullptr,
  1723             getter_Copies(realContentType));
  1725         nsAutoCString contentType; contentType.AssignWithConversion(realContentType.get());
  1726         nsAutoCString charType; // Empty
  1728         // Save the document, fixing up the links as it goes out
  1729         rv = SaveDocumentWithFixup(
  1730             docData->mDocument,
  1731             nodeFixup,
  1732             docData->mFile,
  1733             mReplaceExisting,
  1734             contentType,
  1735             charType,
  1736             mEncodingFlags);
  1738         if (NS_FAILED(rv))
  1739             break;
  1741         // if we're serializing, bail after first iteration of loop
  1742         if (mSerializingOutput)
  1743             break;
  1746     // delete, cleanup regardless of errors (bug 132417)
  1747     for (i = 0; i < mDocList.Length(); i++)
  1749         DocData *docData = mDocList.ElementAt(i);
  1750         delete docData;
  1751         if (mSerializingOutput)
  1753             mDocList.RemoveElementAt(i);
  1754             break;
  1758     if (!mSerializingOutput)
  1760         mDocList.Clear();
  1763     return rv;
  1766 void nsWebBrowserPersist::Cleanup()
  1768     mURIMap.Clear();
  1769     mOutputMap.EnumerateRead(EnumCleanupOutputMap, this);
  1770     mOutputMap.Clear();
  1771     mUploadList.EnumerateRead(EnumCleanupUploadList, this);
  1772     mUploadList.Clear();
  1773     uint32_t i;
  1774     for (i = 0; i < mDocList.Length(); i++)
  1776         DocData *docData = mDocList.ElementAt(i);
  1777         delete docData;
  1779     mDocList.Clear();
  1780     for (i = 0; i < mCleanupList.Length(); i++)
  1782         CleanupData *cleanupData = mCleanupList.ElementAt(i);
  1783         delete cleanupData;
  1785     mCleanupList.Clear();
  1786     mFilenameList.Clear();
  1789 void nsWebBrowserPersist::CleanupLocalFiles()
  1791     // Two passes, the first pass cleans up files, the second pass tests
  1792     // for and then deletes empty directories. Directories that are not
  1793     // empty after the first pass must contain files from something else
  1794     // and are not deleted.
  1795     int pass;
  1796     for (pass = 0; pass < 2; pass++)
  1798         uint32_t i;
  1799         for (i = 0; i < mCleanupList.Length(); i++)
  1801             CleanupData *cleanupData = mCleanupList.ElementAt(i);
  1802             nsCOMPtr<nsIFile> file = cleanupData->mFile;
  1804             // Test if the dir / file exists (something in an earlier loop
  1805             // may have already removed it)
  1806             bool exists = false;
  1807             file->Exists(&exists);
  1808             if (!exists)
  1809                 continue;
  1811             // Test if the file has changed in between creation and deletion
  1812             // in some way that means it should be ignored
  1813             bool isDirectory = false;
  1814             file->IsDirectory(&isDirectory);
  1815             if (isDirectory != cleanupData->mIsDirectory)
  1816                 continue; // A file has become a dir or vice versa !
  1818             if (pass == 0 && !isDirectory)
  1820                 file->Remove(false);
  1822             else if (pass == 1 && isDirectory) // Directory
  1824                 // Directories are more complicated. Enumerate through
  1825                 // children looking for files. Any files created by the
  1826                 // persist object would have been deleted by the first
  1827                 // pass so if there are any there at this stage, the dir
  1828                 // cannot be deleted because it has someone else's files
  1829                 // in it. Empty child dirs are deleted but they must be
  1830                 // recursed through to ensure they are actually empty.
  1832                 bool isEmptyDirectory = true;
  1833                 nsCOMArray<nsISimpleEnumerator> dirStack;
  1834                 int32_t stackSize = 0;
  1836                 // Push the top level enum onto the stack
  1837                 nsCOMPtr<nsISimpleEnumerator> pos;
  1838                 if (NS_SUCCEEDED(file->GetDirectoryEntries(getter_AddRefs(pos))))
  1839                     dirStack.AppendObject(pos);
  1841                 while (isEmptyDirectory && (stackSize = dirStack.Count()))
  1843                     // Pop the last element
  1844                     nsCOMPtr<nsISimpleEnumerator> curPos;
  1845                     curPos = dirStack[stackSize-1];
  1846                     dirStack.RemoveObjectAt(stackSize - 1);
  1848                     // Test if the enumerator has any more files in it
  1849                     bool hasMoreElements = false;
  1850                     curPos->HasMoreElements(&hasMoreElements);
  1851                     if (!hasMoreElements)
  1853                         continue;
  1856                     // Child files automatically make this code drop out,
  1857                     // while child dirs keep the loop going.
  1858                     nsCOMPtr<nsISupports> child;
  1859                     curPos->GetNext(getter_AddRefs(child));
  1860                     NS_ASSERTION(child, "No child element, but hasMoreElements says otherwise");
  1861                     if (!child)
  1862                         continue;
  1863                     nsCOMPtr<nsIFile> childAsFile = do_QueryInterface(child);
  1864                     NS_ASSERTION(childAsFile, "This should be a file but isn't");
  1866                     bool childIsSymlink = false;
  1867                     childAsFile->IsSymlink(&childIsSymlink);
  1868                     bool childIsDir = false;
  1869                     childAsFile->IsDirectory(&childIsDir);
  1870                     if (!childIsDir || childIsSymlink)
  1872                         // Some kind of file or symlink which means dir
  1873                         // is not empty so just drop out.
  1874                         isEmptyDirectory = false;
  1875                         break;
  1877                     // Push parent enumerator followed by child enumerator
  1878                     nsCOMPtr<nsISimpleEnumerator> childPos;
  1879                     childAsFile->GetDirectoryEntries(getter_AddRefs(childPos));
  1880                     dirStack.AppendObject(curPos);
  1881                     if (childPos)
  1882                         dirStack.AppendObject(childPos);
  1885                 dirStack.Clear();
  1887                 // If after all that walking the dir is deemed empty, delete it
  1888                 if (isEmptyDirectory)
  1890                     file->Remove(true);
  1897 nsresult
  1898 nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
  1900     nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  1901     NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
  1903     bool nameHasChanged = false;
  1904     nsresult rv;
  1906     // Get the old filename
  1907     nsAutoCString filename;
  1908     rv = url->GetFileName(filename);
  1909     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1910     nsAutoCString directory;
  1911     rv = url->GetDirectory(directory);
  1912     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  1914     // Split the filename into a base and an extension.
  1915     // e.g. "foo.html" becomes "foo" & ".html"
  1916     //
  1917     // The nsIURL methods GetFileBaseName & GetFileExtension don't
  1918     // preserve the dot whereas this code does to save some effort
  1919     // later when everything is put back together.
  1920     int32_t lastDot = filename.RFind(".");
  1921     nsAutoCString base;
  1922     nsAutoCString ext;
  1923     if (lastDot >= 0)
  1925         filename.Mid(base, 0, lastDot);
  1926         filename.Mid(ext, lastDot, filename.Length() - lastDot); // includes dot
  1928     else
  1930         // filename contains no dot
  1931         base = filename;
  1934     // Test if the filename is longer than allowed by the OS
  1935     int32_t needToChop = filename.Length() - kDefaultMaxFilenameLength;
  1936     if (needToChop > 0)
  1938         // Truncate the base first and then the ext if necessary
  1939         if (base.Length() > (uint32_t) needToChop)
  1941             base.Truncate(base.Length() - needToChop);
  1943         else
  1945             needToChop -= base.Length() - 1;
  1946             base.Truncate(1);
  1947             if (ext.Length() > (uint32_t) needToChop)
  1949                 ext.Truncate(ext.Length() - needToChop);
  1951             else
  1953                 ext.Truncate(0);
  1955             // If kDefaultMaxFilenameLength were 1 we'd be in trouble here,
  1956             // but that won't happen because it will be set to a sensible
  1957             // value.
  1960         filename.Assign(base);
  1961         filename.Append(ext);
  1962         nameHasChanged = true;
  1965     // Ensure the filename is unique
  1966     // Create a filename if it's empty, or if the filename / datapath is
  1967     // already taken by another URI and create an alternate name.
  1969     if (base.IsEmpty() || !mFilenameList.IsEmpty())
  1971         nsAutoCString tmpPath;
  1972         nsAutoCString tmpBase;
  1973         uint32_t duplicateCounter = 1;
  1974         while (1)
  1976             // Make a file name,
  1977             // Foo become foo_001, foo_002, etc.
  1978             // Empty files become _001, _002 etc.
  1980             if (base.IsEmpty() || duplicateCounter > 1)
  1982                 char * tmp = PR_smprintf("_%03d", duplicateCounter);
  1983                 NS_ENSURE_TRUE(tmp, NS_ERROR_OUT_OF_MEMORY);
  1984                 if (filename.Length() < kDefaultMaxFilenameLength - 4)
  1986                     tmpBase = base;
  1988                 else
  1990                     base.Mid(tmpBase, 0, base.Length() - 4);
  1992                 tmpBase.Append(tmp);
  1993                 PR_smprintf_free(tmp);
  1995             else
  1997                 tmpBase = base;
  2000             tmpPath.Assign(directory);
  2001             tmpPath.Append(tmpBase);
  2002             tmpPath.Append(ext);
  2004             // Test if the name is a duplicate
  2005             if (!mFilenameList.Contains(tmpPath))
  2007                 if (!base.Equals(tmpBase))
  2009                     filename.Assign(tmpBase);
  2010                     filename.Append(ext);
  2011                     nameHasChanged = true;
  2013                 break;
  2015             duplicateCounter++;
  2019     // Add name to list of those already used
  2020     nsAutoCString newFilepath(directory);
  2021     newFilepath.Append(filename);
  2022     mFilenameList.AppendElement(newFilepath);
  2024     // Update the uri accordingly if the filename actually changed
  2025     if (nameHasChanged)
  2027         // Final sanity test
  2028         if (filename.Length() > kDefaultMaxFilenameLength)
  2030             NS_WARNING("Filename wasn't truncated less than the max file length - how can that be?");
  2031             return NS_ERROR_FAILURE;
  2034         nsCOMPtr<nsIFile> localFile;
  2035         GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  2037         if (localFile)
  2039             nsAutoString filenameAsUnichar;
  2040             filenameAsUnichar.AssignWithConversion(filename.get());
  2041             localFile->SetLeafName(filenameAsUnichar);
  2043             // Resync the URI with the file after the extension has been appended
  2044             nsresult rv;
  2045             nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  2046             NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  2047             fileURL->SetFile(localFile);  // this should recalculate uri
  2049         else
  2051             url->SetFileName(filename);
  2055     return NS_OK;
  2059 nsresult
  2060 nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
  2062     // Try to get filename from the URI.
  2063     nsAutoString fileName;
  2065     // Get a suggested file name from the URL but strip it of characters
  2066     // likely to cause the name to be illegal.
  2068     nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  2069     if (url)
  2071         nsAutoCString nameFromURL;
  2072         url->GetFileName(nameFromURL);
  2073         if (mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES)
  2075             fileName.AssignWithConversion(NS_UnescapeURL(nameFromURL).get());
  2076             goto end;
  2078         if (!nameFromURL.IsEmpty())
  2080             // Unescape the file name (GetFileName escapes it)
  2081             NS_UnescapeURL(nameFromURL);
  2082             uint32_t nameLength = 0;
  2083             const char *p = nameFromURL.get();
  2084             for (;*p && *p != ';' && *p != '?' && *p != '#' && *p != '.'
  2085                  ;p++)
  2087                 if (nsCRT::IsAsciiAlpha(*p) || nsCRT::IsAsciiDigit(*p)
  2088                     || *p == '.' || *p == '-' ||  *p == '_' || (*p == ' '))
  2090                     fileName.Append(char16_t(*p));
  2091                     if (++nameLength == kDefaultMaxFilenameLength)
  2093                         // Note:
  2094                         // There is no point going any further since it will be
  2095                         // truncated in CalculateUniqueFilename anyway.
  2096                         // More importantly, certain implementations of
  2097                         // nsIFile (e.g. the Mac impl) might truncate
  2098                         // names in undesirable ways, such as truncating from
  2099                         // the middle, inserting ellipsis and so on.
  2100                         break;
  2107     // Empty filenames can confuse the local file object later
  2108     // when it attempts to set the leaf name in CalculateUniqueFilename
  2109     // for duplicates and ends up replacing the parent dir. To avoid
  2110     // the problem, all filenames are made at least one character long.
  2111     if (fileName.IsEmpty())
  2113         fileName.Append(char16_t('a')); // 'a' is for arbitrary
  2116 end:
  2117     aFilename = fileName;
  2118     return NS_OK;
  2122 nsresult
  2123 nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
  2125     nsresult rv;
  2127     if (!mMIMEService)
  2129         mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
  2130         NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
  2133     nsAutoCString contentType;
  2135     // Get the content type from the channel
  2136     aChannel->GetContentType(contentType);
  2138     // Get the content type from the MIME service
  2139     if (contentType.IsEmpty())
  2141         nsCOMPtr<nsIURI> uri;
  2142         aChannel->GetOriginalURI(getter_AddRefs(uri));
  2143         mMIMEService->GetTypeFromURI(uri, contentType);
  2146     // Append the extension onto the file
  2147     if (!contentType.IsEmpty())
  2149         nsCOMPtr<nsIMIMEInfo> mimeInfo;
  2150         mMIMEService->GetFromTypeAndExtension(
  2151             contentType, EmptyCString(), getter_AddRefs(mimeInfo));
  2153         nsCOMPtr<nsIFile> localFile;
  2154         GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  2156         if (mimeInfo)
  2158             nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
  2159             NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
  2161             nsAutoCString newFileName;
  2162             url->GetFileName(newFileName);
  2164             // Test if the current extension is current for the mime type
  2165             bool hasExtension = false;
  2166             int32_t ext = newFileName.RFind(".");
  2167             if (ext != -1)
  2169                 mimeInfo->ExtensionExists(Substring(newFileName, ext + 1), &hasExtension);
  2172             // Append the mime file extension
  2173             nsAutoCString fileExt;
  2174             if (!hasExtension)
  2176                 // Test if previous extension is acceptable
  2177                 nsCOMPtr<nsIURL> oldurl(do_QueryInterface(aOriginalURIWithExtension));
  2178                 NS_ENSURE_TRUE(oldurl, NS_ERROR_FAILURE);
  2179                 oldurl->GetFileExtension(fileExt);
  2180                 bool useOldExt = false;
  2181                 if (!fileExt.IsEmpty())
  2183                     mimeInfo->ExtensionExists(fileExt, &useOldExt);
  2186                 // can't use old extension so use primary extension
  2187                 if (!useOldExt)
  2189                     mimeInfo->GetPrimaryExtension(fileExt);
  2192                 if (!fileExt.IsEmpty())
  2194                     uint32_t newLength = newFileName.Length() + fileExt.Length() + 1;
  2195                     if (newLength > kDefaultMaxFilenameLength)
  2197                         if (fileExt.Length() > kDefaultMaxFilenameLength/2)
  2198                             fileExt.Truncate(kDefaultMaxFilenameLength/2);
  2200                         uint32_t diff = kDefaultMaxFilenameLength - 1 -
  2201                                         fileExt.Length();
  2202                         if (newFileName.Length() > diff)
  2203                             newFileName.Truncate(diff);
  2205                     newFileName.Append(".");
  2206                     newFileName.Append(fileExt);
  2209                 if (localFile)
  2211                     localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
  2213                     // Resync the URI with the file after the extension has been appended
  2214                     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
  2215                     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  2216                     fileURL->SetFile(localFile);  // this should recalculate uri
  2218                 else
  2220                     url->SetFileName(newFileName);
  2227     return NS_OK;
  2230 nsresult
  2231 nsWebBrowserPersist::MakeOutputStream(
  2232     nsIURI *aURI, nsIOutputStream **aOutputStream)
  2234     nsresult rv;
  2236     nsCOMPtr<nsIFile> localFile;
  2237     GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
  2238     if (localFile)
  2239         rv = MakeOutputStreamFromFile(localFile, aOutputStream);
  2240     else
  2241         rv = MakeOutputStreamFromURI(aURI, aOutputStream);
  2243     return rv;
  2246 nsresult
  2247 nsWebBrowserPersist::MakeOutputStreamFromFile(
  2248     nsIFile *aFile, nsIOutputStream **aOutputStream)
  2250     nsresult rv = NS_OK;
  2252     nsCOMPtr<nsIFileOutputStream> fileOutputStream =
  2253         do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
  2254     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  2256     // XXX brade:  get the right flags here!
  2257     int32_t ioFlags = -1;
  2258     if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE)
  2259       ioFlags = PR_APPEND | PR_CREATE_FILE | PR_WRONLY;
  2260     rv = fileOutputStream->Init(aFile, ioFlags, -1, 0);
  2261     NS_ENSURE_SUCCESS(rv, rv);
  2263     *aOutputStream = NS_BufferOutputStream(fileOutputStream,
  2264                                            BUFFERED_OUTPUT_SIZE).take();
  2266     if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
  2268         // Add to cleanup list in event of failure
  2269         CleanupData *cleanupData = new CleanupData;
  2270         if (!cleanupData) {
  2271           NS_RELEASE(*aOutputStream);
  2272           return NS_ERROR_OUT_OF_MEMORY;
  2274         cleanupData->mFile = aFile;
  2275         cleanupData->mIsDirectory = false;
  2276         mCleanupList.AppendElement(cleanupData);
  2279     return NS_OK;
  2282 nsresult
  2283 nsWebBrowserPersist::MakeOutputStreamFromURI(
  2284     nsIURI *aURI, nsIOutputStream  **aOutputStream)
  2286     uint32_t segsize = 8192;
  2287     uint32_t maxsize = uint32_t(-1);
  2288     nsCOMPtr<nsIStorageStream> storStream;
  2289     nsresult rv = NS_NewStorageStream(segsize, maxsize, getter_AddRefs(storStream));
  2290     NS_ENSURE_SUCCESS(rv, rv);
  2292     NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
  2293     return NS_OK;
  2296 void
  2297 nsWebBrowserPersist::EndDownload(nsresult aResult)
  2299     // Store the error code in the result if it is an error
  2300     if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(aResult))
  2302         mPersistResult = aResult;
  2305     // Do file cleanup if required
  2306     if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
  2308         CleanupLocalFiles();
  2311     // Cleanup the channels
  2312     mCompleted = true;
  2313     Cleanup();
  2316 struct MOZ_STACK_CLASS FixRedirectData
  2318     nsCOMPtr<nsIChannel> mNewChannel;
  2319     nsCOMPtr<nsIURI> mOriginalURI;
  2320     nsCOMPtr<nsISupports> mMatchingKey;
  2321 };
  2323 nsresult
  2324 nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
  2326     NS_ENSURE_ARG_POINTER(aNewChannel);
  2327     nsCOMPtr<nsIURI> originalURI;
  2329     // Enumerate through existing open channels looking for one with
  2330     // a URI matching the one specified.
  2332     FixRedirectData data;
  2333     data.mNewChannel = aNewChannel;
  2334     data.mNewChannel->GetOriginalURI(getter_AddRefs(data.mOriginalURI));
  2335     mOutputMap.EnumerateRead(EnumFixRedirect, &data);
  2337     // If a match is found, remove the data entry with the old channel key
  2338     // and re-add it with the new channel key.
  2340     if (data.mMatchingKey)
  2342         nsAutoPtr<OutputData> outputData;
  2343         mOutputMap.RemoveAndForget(data.mMatchingKey, outputData);
  2344         NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
  2346         // Store data again with new channel unless told to ignore redirects
  2347         if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA))
  2349             nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
  2350             mOutputMap.Put(keyPtr, outputData.forget());
  2354     return NS_OK;
  2357 PLDHashOperator
  2358 nsWebBrowserPersist::EnumFixRedirect(nsISupports *aKey, OutputData *aData, void* aClosure)
  2360     FixRedirectData *data = static_cast<FixRedirectData*>(aClosure);
  2362     nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(aKey);
  2363     nsCOMPtr<nsIURI> thisURI;
  2365     thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
  2367     // Compare this channel's URI to the one passed in.
  2368     bool matchingURI = false;
  2369     thisURI->Equals(data->mOriginalURI, &matchingURI);
  2370     if (matchingURI)
  2372         data->mMatchingKey = aKey;
  2373         return PL_DHASH_STOP;
  2376     return PL_DHASH_NEXT;
  2379 void
  2380 nsWebBrowserPersist::CalcTotalProgress()
  2382     mTotalCurrentProgress = 0;
  2383     mTotalMaxProgress = 0;
  2385     if (mOutputMap.Count() > 0)
  2387         // Total up the progress of each output stream
  2388         mOutputMap.EnumerateRead(EnumCalcProgress, this);
  2391     if (mUploadList.Count() > 0)
  2393         // Total up the progress of each upload
  2394         mUploadList.EnumerateRead(EnumCalcUploadProgress, this);
  2397     // XXX this code seems pretty bogus and pointless
  2398     if (mTotalCurrentProgress == 0 && mTotalMaxProgress == 0)
  2400         // No output streams so we must be complete
  2401         mTotalCurrentProgress = 10000;
  2402         mTotalMaxProgress = 10000;
  2406 PLDHashOperator
  2407 nsWebBrowserPersist::EnumCalcProgress(nsISupports *aKey, OutputData *aData, void* aClosure)
  2409     nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  2411     // only count toward total progress if destination file is local
  2412     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aData->mFile);
  2413     if (fileURL)
  2415         pthis->mTotalCurrentProgress += aData->mSelfProgress;
  2416         pthis->mTotalMaxProgress += aData->mSelfProgressMax;
  2418     return PL_DHASH_NEXT;
  2421 PLDHashOperator
  2422 nsWebBrowserPersist::EnumCalcUploadProgress(nsISupports *aKey, UploadData *aData, void* aClosure)
  2424     if (aData && aClosure)
  2426         nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  2427         pthis->mTotalCurrentProgress += aData->mSelfProgress;
  2428         pthis->mTotalMaxProgress += aData->mSelfProgressMax;
  2430     return PL_DHASH_NEXT;
  2433 PLDHashOperator
  2434 nsWebBrowserPersist::EnumCountURIsToPersist(const nsACString &aKey, URIData *aData, void* aClosure)
  2436     uint32_t *count = static_cast<uint32_t*>(aClosure);
  2437     if (aData->mNeedsPersisting && !aData->mSaved)
  2439         (*count)++;
  2441     return PL_DHASH_NEXT;
  2444 PLDHashOperator
  2445 nsWebBrowserPersist::EnumPersistURIs(const nsACString &aKey, URIData *aData, void* aClosure)
  2447     if (!aData->mNeedsPersisting || aData->mSaved)
  2449         return PL_DHASH_NEXT;
  2452     nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
  2453     nsresult rv;
  2455     // Create a URI from the key
  2456     nsAutoCString key = nsAutoCString(aKey);
  2457     nsCOMPtr<nsIURI> uri;
  2458     rv = NS_NewURI(getter_AddRefs(uri),
  2459                    nsDependentCString(key.get(), key.Length()),
  2460                    aData->mCharset.get());
  2461     NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  2463     // Make a URI to save the data to
  2464     nsCOMPtr<nsIURI> fileAsURI;
  2465     rv = aData->mDataPath->Clone(getter_AddRefs(fileAsURI));
  2466     NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  2467     rv = pthis->AppendPathToURI(fileAsURI, aData->mFilename);
  2468     NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  2470     rv = pthis->SaveURIInternal(uri, nullptr, nullptr, nullptr, nullptr, fileAsURI, true,
  2471                                 pthis->mIsPrivate);
  2472     // if SaveURIInternal fails, then it will have called EndDownload,
  2473     // which means that |aData| is no longer valid memory.  we MUST bail.
  2474     NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
  2476     if (rv == NS_OK)
  2478         // Store the actual object because once it's persisted this
  2479         // will be fixed up with the right file extension.
  2481         aData->mFile = fileAsURI;
  2482         aData->mSaved = true;
  2484     else
  2486         aData->mNeedsFixup = false;
  2489     if (pthis->mSerializingOutput)
  2490         return PL_DHASH_STOP;
  2492     return PL_DHASH_NEXT;
  2495 PLDHashOperator
  2496 nsWebBrowserPersist::EnumCleanupOutputMap(nsISupports *aKey, OutputData *aData, void* aClosure)
  2498     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
  2499     if (channel)
  2501         channel->Cancel(NS_BINDING_ABORTED);
  2503     return PL_DHASH_NEXT;
  2506 PLDHashOperator
  2507 nsWebBrowserPersist::EnumCleanupUploadList(nsISupports *aKey, UploadData *aData, void* aClosure)
  2509     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
  2510     if (channel)
  2512         channel->Cancel(NS_BINDING_ABORTED);
  2514     return PL_DHASH_NEXT;
  2517 nsresult nsWebBrowserPersist::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, const nsAString &aHref)
  2519     NS_ENSURE_ARG_POINTER(aPI);
  2520     nsresult rv = NS_OK;
  2522     nsAutoString data;
  2523     rv = aPI->GetData(data);
  2524     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  2526     nsAutoString href;
  2527     nsContentUtils::GetPseudoAttributeValue(data,
  2528                                             nsGkAtoms::href,
  2529                                             href);
  2531     // Construct and set a new data value for the xml-stylesheet
  2532     if (!aHref.IsEmpty() && !href.IsEmpty())
  2534         nsAutoString alternate;
  2535         nsAutoString charset;
  2536         nsAutoString title;
  2537         nsAutoString type;
  2538         nsAutoString media;
  2540         nsContentUtils::GetPseudoAttributeValue(data,
  2541                                                 nsGkAtoms::alternate,
  2542                                                 alternate);
  2543         nsContentUtils::GetPseudoAttributeValue(data,
  2544                                                 nsGkAtoms::charset,
  2545                                                 charset);
  2546         nsContentUtils::GetPseudoAttributeValue(data,
  2547                                                 nsGkAtoms::title,
  2548                                                 title);
  2549         nsContentUtils::GetPseudoAttributeValue(data,
  2550                                                 nsGkAtoms::type,
  2551                                                 type);
  2552         nsContentUtils::GetPseudoAttributeValue(data,
  2553                                                 nsGkAtoms::media,
  2554                                                 media);
  2556         NS_NAMED_LITERAL_STRING(kCloseAttr, "\" ");
  2557         nsAutoString newData;
  2558         newData += NS_LITERAL_STRING("href=\"") + aHref + kCloseAttr;
  2559         if (!title.IsEmpty())
  2561             newData += NS_LITERAL_STRING("title=\"") + title + kCloseAttr;
  2563         if (!media.IsEmpty())
  2565             newData += NS_LITERAL_STRING("media=\"") + media + kCloseAttr;
  2567         if (!type.IsEmpty())
  2569             newData += NS_LITERAL_STRING("type=\"") + type + kCloseAttr;
  2571         if (!charset.IsEmpty())
  2573             newData += NS_LITERAL_STRING("charset=\"") + charset + kCloseAttr;
  2575         if (!alternate.IsEmpty())
  2577             newData += NS_LITERAL_STRING("alternate=\"") + alternate + kCloseAttr;
  2579         newData.Truncate(newData.Length() - 1);  // Remove the extra space on the end.
  2580         aPI->SetData(newData);
  2583     return rv;
  2586 nsresult nsWebBrowserPersist::GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
  2588     NS_ENSURE_ARG_POINTER(aPI);
  2590     nsresult rv = NS_OK;
  2591     nsAutoString data;
  2592     rv = aPI->GetData(data);
  2593     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  2595     nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
  2597     return NS_OK;
  2600 nsresult nsWebBrowserPersist::OnWalkDOMNode(nsIDOMNode *aNode)
  2602     // Fixup xml-stylesheet processing instructions
  2603     nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNode);
  2604     if (nodeAsPI)
  2606         nsAutoString target;
  2607         nodeAsPI->GetTarget(target);
  2608         if (target.EqualsLiteral("xml-stylesheet"))
  2610             nsAutoString href;
  2611             GetXMLStyleSheetLink(nodeAsPI, href);
  2612             if (!href.IsEmpty())
  2614                 StoreURI(NS_ConvertUTF16toUTF8(href).get());
  2617         return NS_OK;
  2620     nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  2621     if (!content)
  2623         return NS_OK;
  2626     // Test the node to see if it's an image, frame, iframe, css, js
  2627     nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNode);
  2628     if (nodeAsImage)
  2630         StoreURIAttribute(aNode, "src");
  2631         return NS_OK;
  2634     if (content->IsSVG(nsGkAtoms::img))
  2636         StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
  2637         return NS_OK;
  2640     nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNode);
  2641     if (nodeAsMedia)
  2643         StoreURIAttribute(aNode, "src");
  2644         return NS_OK;
  2646     nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNode);
  2647     if (nodeAsSource)
  2649         StoreURIAttribute(aNode, "src");
  2650         return NS_OK;
  2653     if (content->IsHTML(nsGkAtoms::body)) {
  2654         StoreURIAttribute(aNode, "background");
  2655         return NS_OK;
  2658     if (content->IsHTML(nsGkAtoms::table)) {
  2659         StoreURIAttribute(aNode, "background");
  2660         return NS_OK;
  2663     if (content->IsHTML(nsGkAtoms::tr)) {
  2664         StoreURIAttribute(aNode, "background");
  2665         return NS_OK;
  2668     if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
  2669         StoreURIAttribute(aNode, "background");
  2670         return NS_OK;
  2673     nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
  2674     if (nodeAsScript)
  2676         StoreURIAttribute(aNode, "src");
  2677         return NS_OK;
  2680     if (content->IsSVG(nsGkAtoms::script))
  2682         StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
  2683         return NS_OK;
  2686     nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
  2687     if (nodeAsEmbed)
  2689         StoreURIAttribute(aNode, "src");
  2690         return NS_OK;
  2693     nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
  2694     if (nodeAsObject)
  2696         StoreURIAttribute(aNode, "data");
  2697         return NS_OK;
  2700     nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNode);
  2701     if (nodeAsApplet)
  2703         // For an applet, relative URIs are resolved relative to the
  2704         // codebase (which is resolved relative to the base URI).
  2705         nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
  2706         nsAutoString codebase;
  2707         nodeAsApplet->GetCodeBase(codebase);
  2708         if (!codebase.IsEmpty()) {
  2709             nsCOMPtr<nsIURI> baseURI;
  2710             NS_NewURI(getter_AddRefs(baseURI), codebase,
  2711                       mCurrentCharset.get(), mCurrentBaseURI);
  2712             if (baseURI) {
  2713                 mCurrentBaseURI = baseURI;
  2717         URIData *archiveURIData = nullptr;
  2718         StoreURIAttribute(aNode, "archive", true, &archiveURIData);
  2719         // We only store 'code' locally if there is no 'archive',
  2720         // otherwise we assume the archive file(s) contains it (bug 430283).
  2721         if (!archiveURIData)
  2722             StoreURIAttribute(aNode, "code");
  2724         // restore the base URI we really want to have
  2725         mCurrentBaseURI = oldBase;
  2726         return NS_OK;
  2729     nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNode);
  2730     if (nodeAsLink)
  2732         // Test if the link has a rel value indicating it to be a stylesheet
  2733         nsAutoString linkRel;
  2734         if (NS_SUCCEEDED(nodeAsLink->GetRel(linkRel)) && !linkRel.IsEmpty())
  2736             nsReadingIterator<char16_t> start;
  2737             nsReadingIterator<char16_t> end;
  2738             nsReadingIterator<char16_t> current;
  2740             linkRel.BeginReading(start);
  2741             linkRel.EndReading(end);
  2743             // Walk through space delimited string looking for "stylesheet"
  2744             for (current = start; current != end; ++current)
  2746                 // Ignore whitespace
  2747                 if (nsCRT::IsAsciiSpace(*current))
  2748                     continue;
  2750                 // Grab the next space delimited word
  2751                 nsReadingIterator<char16_t> startWord = current;
  2752                 do {
  2753                     ++current;
  2754                 } while (current != end && !nsCRT::IsAsciiSpace(*current));
  2756                 // Store the link for fix up if it says "stylesheet"
  2757                 if (Substring(startWord, current)
  2758                         .LowerCaseEqualsLiteral("stylesheet"))
  2760                     StoreURIAttribute(aNode, "href");
  2761                     return NS_OK;
  2763                 if (current == end)
  2764                     break;
  2767         return NS_OK;
  2770     nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
  2771     if (nodeAsFrame)
  2773         URIData *data = nullptr;
  2774         StoreURIAttribute(aNode, "src", false, &data);
  2775         if (data)
  2777             data->mIsSubFrame = true;
  2778             // Save the frame content
  2779             nsCOMPtr<nsIDOMDocument> content;
  2780             nodeAsFrame->GetContentDocument(getter_AddRefs(content));
  2781             if (content)
  2783                 SaveSubframeContent(content, data);
  2786         return NS_OK;
  2789     nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
  2790     if (nodeAsIFrame && !(mPersistFlags & PERSIST_FLAGS_IGNORE_IFRAMES))
  2792         URIData *data = nullptr;
  2793         StoreURIAttribute(aNode, "src", false, &data);
  2794         if (data)
  2796             data->mIsSubFrame = true;
  2797             // Save the frame content
  2798             nsCOMPtr<nsIDOMDocument> content;
  2799             nodeAsIFrame->GetContentDocument(getter_AddRefs(content));
  2800             if (content)
  2802                 SaveSubframeContent(content, data);
  2805         return NS_OK;
  2808     nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
  2809     if (nodeAsInput)
  2811         StoreURIAttribute(aNode, "src");
  2812         return NS_OK;
  2815     return NS_OK;
  2818 nsresult
  2819 nsWebBrowserPersist::GetNodeToFixup(nsIDOMNode *aNodeIn, nsIDOMNode **aNodeOut)
  2821     if (!(mPersistFlags & PERSIST_FLAGS_FIXUP_ORIGINAL_DOM))
  2823         nsresult rv = aNodeIn->CloneNode(false, 1, aNodeOut);
  2824         NS_ENSURE_SUCCESS(rv, rv);
  2826     else
  2828         NS_ADDREF(*aNodeOut = aNodeIn);
  2830     nsCOMPtr<nsIDOMHTMLElement> element(do_QueryInterface(*aNodeOut));
  2831     if (element) {
  2832         // Make sure this is not XHTML
  2833         nsAutoString namespaceURI;
  2834         element->GetNamespaceURI(namespaceURI);
  2835         if (namespaceURI.IsEmpty()) {
  2836             // This is a tag-soup node.  It may have a _base_href attribute
  2837             // stuck on it by the parser, but since we're fixing up all URIs
  2838             // relative to the overall document base that will screw us up.
  2839             // Just remove the _base_href.
  2840             element->RemoveAttribute(NS_LITERAL_STRING("_base_href"));
  2843     return NS_OK;
  2846 nsresult
  2847 nsWebBrowserPersist::CloneNodeWithFixedUpAttributes(
  2848     nsIDOMNode *aNodeIn, bool *aSerializeCloneKids, nsIDOMNode **aNodeOut)
  2850     nsresult rv;
  2851     *aNodeOut = nullptr;
  2852     *aSerializeCloneKids = false;
  2854     // Fixup xml-stylesheet processing instructions
  2855     nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNodeIn);
  2856     if (nodeAsPI)
  2858         nsAutoString target;
  2859         nodeAsPI->GetTarget(target);
  2860         if (target.EqualsLiteral("xml-stylesheet"))
  2862             rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2863             if (NS_SUCCEEDED(rv) && *aNodeOut)
  2865                 nsCOMPtr<nsIDOMProcessingInstruction> outNode = do_QueryInterface(*aNodeOut);
  2866                 nsAutoString href;
  2867                 GetXMLStyleSheetLink(nodeAsPI, href);
  2868                 if (!href.IsEmpty())
  2870                     FixupURI(href);
  2871                     FixupXMLStyleSheetLink(outNode, href);
  2877     // BASE elements are replaced by a comment so relative links are not hosed.
  2879     if (!(mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS))
  2881         nsCOMPtr<nsIDOMHTMLBaseElement> nodeAsBase = do_QueryInterface(aNodeIn);
  2882         if (nodeAsBase)
  2884             nsCOMPtr<nsIDOMDocument> ownerDocument;
  2885             HTMLSharedElement* base = static_cast<HTMLSharedElement*>(nodeAsBase.get());
  2886             base->GetOwnerDocument(getter_AddRefs(ownerDocument));
  2887             if (ownerDocument)
  2889                 nsAutoString href;
  2890                 base->GetHref(href); // Doesn't matter if this fails
  2891                 nsCOMPtr<nsIDOMComment> comment;
  2892                 nsAutoString commentText; commentText.AssignLiteral(" base ");
  2893                 if (!href.IsEmpty())
  2895                     commentText += NS_LITERAL_STRING("href=\"") + href + NS_LITERAL_STRING("\" ");
  2897                 rv = ownerDocument->CreateComment(commentText, getter_AddRefs(comment));
  2898                 if (comment)
  2900                     return CallQueryInterface(comment, aNodeOut);
  2906     nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
  2907     if (!content)
  2909         return NS_OK;
  2912     // Fix up href and file links in the elements
  2914     nsCOMPtr<nsIDOMHTMLAnchorElement> nodeAsAnchor = do_QueryInterface(aNodeIn);
  2915     if (nodeAsAnchor)
  2917         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2918         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2920             FixupAnchor(*aNodeOut);
  2922         return rv;
  2925     nsCOMPtr<nsIDOMHTMLAreaElement> nodeAsArea = do_QueryInterface(aNodeIn);
  2926     if (nodeAsArea)
  2928         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2929         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2931             FixupAnchor(*aNodeOut);
  2933         return rv;
  2936     if (content->IsHTML(nsGkAtoms::body)) {
  2937         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2938         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2940             FixupNodeAttribute(*aNodeOut, "background");
  2942         return rv;
  2945     if (content->IsHTML(nsGkAtoms::table)) {
  2946         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2947         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2949             FixupNodeAttribute(*aNodeOut, "background");
  2951         return rv;
  2954     if (content->IsHTML(nsGkAtoms::tr)) {
  2955         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2956         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2958             FixupNodeAttribute(*aNodeOut, "background");
  2960         return rv;
  2963     if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
  2964         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2965         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2967             FixupNodeAttribute(*aNodeOut, "background");
  2969         return rv;
  2972     nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNodeIn);
  2973     if (nodeAsImage)
  2975         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2976         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2978             // Disable image loads
  2979             nsCOMPtr<nsIImageLoadingContent> imgCon =
  2980                 do_QueryInterface(*aNodeOut);
  2981             if (imgCon)
  2982                 imgCon->SetLoadingEnabled(false);
  2984             FixupAnchor(*aNodeOut);
  2985             FixupNodeAttribute(*aNodeOut, "src");
  2987         return rv;
  2990     nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNodeIn);
  2991     if (nodeAsMedia)
  2993         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  2994         if (NS_SUCCEEDED(rv) && *aNodeOut)
  2996             FixupNodeAttribute(*aNodeOut, "src");
  2999         return rv;
  3002     nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNodeIn);
  3003     if (nodeAsSource)
  3005         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3006         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3008             FixupNodeAttribute(*aNodeOut, "src");
  3011         return rv;
  3014     if (content->IsSVG(nsGkAtoms::img))
  3016         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3017         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3019             // Disable image loads
  3020             nsCOMPtr<nsIImageLoadingContent> imgCon =
  3021                 do_QueryInterface(*aNodeOut);
  3022             if (imgCon)
  3023                 imgCon->SetLoadingEnabled(false);
  3025             // FixupAnchor(*aNodeOut);  // XXXjwatt: is this line needed?
  3026             FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
  3028         return rv;
  3031     nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNodeIn);
  3032     if (nodeAsScript)
  3034         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3035         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3037             FixupNodeAttribute(*aNodeOut, "src");
  3039         return rv;
  3042     if (content->IsSVG(nsGkAtoms::script))
  3044         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3045         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3047             FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
  3049         return rv;
  3052     nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNodeIn);
  3053     if (nodeAsEmbed)
  3055         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3056         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3058             FixupNodeAttribute(*aNodeOut, "src");
  3060         return rv;
  3063     nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNodeIn);
  3064     if (nodeAsObject)
  3066         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3067         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3069             FixupNodeAttribute(*aNodeOut, "data");
  3071         return rv;
  3074     nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNodeIn);
  3075     if (nodeAsApplet)
  3077         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3078         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3080             nsCOMPtr<nsIDOMHTMLAppletElement> newApplet =
  3081                 do_QueryInterface(*aNodeOut);
  3082             // For an applet, relative URIs are resolved relative to the
  3083             // codebase (which is resolved relative to the base URI).
  3084             nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
  3085             nsAutoString codebase;
  3086             nodeAsApplet->GetCodeBase(codebase);
  3087             if (!codebase.IsEmpty()) {
  3088                 nsCOMPtr<nsIURI> baseURI;
  3089                 NS_NewURI(getter_AddRefs(baseURI), codebase,
  3090                           mCurrentCharset.get(), mCurrentBaseURI);
  3091                 if (baseURI) {
  3092                     mCurrentBaseURI = baseURI;
  3095             // Unset the codebase too, since we'll correctly relativize the
  3096             // code and archive paths.
  3097             static_cast<HTMLSharedObjectElement*>(newApplet.get())->
  3098               RemoveAttribute(NS_LITERAL_STRING("codebase"));
  3099             FixupNodeAttribute(*aNodeOut, "code");
  3100             FixupNodeAttribute(*aNodeOut, "archive");
  3101             // restore the base URI we really want to have
  3102             mCurrentBaseURI = oldBase;
  3104         return rv;
  3107     nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNodeIn);
  3108     if (nodeAsLink)
  3110         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3111         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3113             // First see if the link represents linked content
  3114             rv = FixupNodeAttribute(*aNodeOut, "href");
  3115             if (NS_FAILED(rv))
  3117                 // Perhaps this link is actually an anchor to related content
  3118                 FixupAnchor(*aNodeOut);
  3120             // TODO if "type" attribute == "text/css"
  3121             //        fixup stylesheet
  3123         return rv;
  3126     nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNodeIn);
  3127     if (nodeAsFrame)
  3129         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3130         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3132             FixupNodeAttribute(*aNodeOut, "src");
  3134         return rv;
  3137     nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNodeIn);
  3138     if (nodeAsIFrame)
  3140         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3141         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3143             FixupNodeAttribute(*aNodeOut, "src");
  3145         return rv;
  3148     nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNodeIn);
  3149     if (nodeAsInput)
  3151         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3152         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3154             // Disable image loads
  3155             nsCOMPtr<nsIImageLoadingContent> imgCon =
  3156                 do_QueryInterface(*aNodeOut);
  3157             if (imgCon)
  3158                 imgCon->SetLoadingEnabled(false);
  3160             FixupNodeAttribute(*aNodeOut, "src");
  3162             nsAutoString valueStr;
  3163             NS_NAMED_LITERAL_STRING(valueAttr, "value");
  3164             // Update element node attributes with user-entered form state
  3165             nsCOMPtr<nsIContent> content = do_QueryInterface(*aNodeOut);
  3166             nsRefPtr<HTMLInputElement> outElt =
  3167               HTMLInputElement::FromContentOrNull(content);
  3168             nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
  3169             switch (formControl->GetType()) {
  3170                 case NS_FORM_INPUT_EMAIL:
  3171                 case NS_FORM_INPUT_SEARCH:
  3172                 case NS_FORM_INPUT_TEXT:
  3173                 case NS_FORM_INPUT_TEL:
  3174                 case NS_FORM_INPUT_URL:
  3175                 case NS_FORM_INPUT_NUMBER:
  3176                 case NS_FORM_INPUT_RANGE:
  3177                 case NS_FORM_INPUT_DATE:
  3178                 case NS_FORM_INPUT_TIME:
  3179                 case NS_FORM_INPUT_COLOR:
  3180                     nodeAsInput->GetValue(valueStr);
  3181                     // Avoid superfluous value="" serialization
  3182                     if (valueStr.IsEmpty())
  3183                       outElt->RemoveAttribute(valueAttr);
  3184                     else
  3185                       outElt->SetAttribute(valueAttr, valueStr);
  3186                     break;
  3187                 case NS_FORM_INPUT_CHECKBOX:
  3188                 case NS_FORM_INPUT_RADIO:
  3189                     bool checked;
  3190                     nodeAsInput->GetChecked(&checked);
  3191                     outElt->SetDefaultChecked(checked);
  3192                     break;
  3193                 default:
  3194                     break;
  3197         return rv;
  3200     nsCOMPtr<nsIDOMHTMLTextAreaElement> nodeAsTextArea = do_QueryInterface(aNodeIn);
  3201     if (nodeAsTextArea)
  3203         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3204         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3206             // Tell the document encoder to serialize the text child we create below
  3207             *aSerializeCloneKids = true;
  3209             nsAutoString valueStr;
  3210             nodeAsTextArea->GetValue(valueStr);
  3212             (*aNodeOut)->SetTextContent(valueStr);
  3214         return rv;
  3217     nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
  3218     if (nodeAsOption)
  3220         rv = GetNodeToFixup(aNodeIn, aNodeOut);
  3221         if (NS_SUCCEEDED(rv) && *aNodeOut)
  3223             nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
  3224             bool selected;
  3225             nodeAsOption->GetSelected(&selected);
  3226             outElt->SetDefaultSelected(selected);
  3228         return rv;
  3231     return NS_OK;
  3234 nsresult
  3235 nsWebBrowserPersist::StoreURI(
  3236     const char *aURI, bool aNeedsPersisting, URIData **aData)
  3238     NS_ENSURE_ARG_POINTER(aURI);
  3240     nsCOMPtr<nsIURI> uri;
  3241     nsresult rv = NS_NewURI(getter_AddRefs(uri),
  3242                             nsDependentCString(aURI),
  3243                             mCurrentCharset.get(),
  3244                             mCurrentBaseURI);
  3245     NS_ENSURE_SUCCESS(rv, rv);
  3247     return StoreURI(uri, aNeedsPersisting, aData);
  3250 nsresult
  3251 nsWebBrowserPersist::StoreURI(
  3252     nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
  3254     NS_ENSURE_ARG_POINTER(aURI);
  3255     if (aData)
  3257         *aData = nullptr;
  3260     // Test if this URI should be persisted. By default
  3261     // we should assume the URI  is persistable.
  3262     bool doNotPersistURI;
  3263     nsresult rv = NS_URIChainHasFlags(aURI,
  3264                                       nsIProtocolHandler::URI_NON_PERSISTABLE,
  3265                                       &doNotPersistURI);
  3266     if (NS_FAILED(rv))
  3268         doNotPersistURI = false;
  3271     if (doNotPersistURI)
  3273         return NS_OK;
  3276     URIData *data = nullptr;
  3277     MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
  3278     if (aData)
  3280         *aData = data;
  3283     return NS_OK;
  3286 nsresult
  3287 nsWebBrowserPersist::StoreURIAttributeNS(
  3288     nsIDOMNode *aNode, const char *aNamespaceURI, const char *aAttribute,
  3289     bool aNeedsPersisting, URIData **aData)
  3291     NS_ENSURE_ARG_POINTER(aNode);
  3292     NS_ENSURE_ARG_POINTER(aNamespaceURI);
  3293     NS_ENSURE_ARG_POINTER(aAttribute);
  3295     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  3296     MOZ_ASSERT(element);
  3298     // Find the named URI attribute on the (element) node and store
  3299     // a reference to the URI that maps onto a local file name
  3301     nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  3302     nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  3303     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3305     NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
  3306     NS_ConvertASCIItoUTF16 attribute(aAttribute);
  3307     nsCOMPtr<nsIDOMAttr> attr;
  3308     rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
  3309     if (attr)
  3311         nsAutoString oldValue;
  3312         attr->GetValue(oldValue);
  3313         if (!oldValue.IsEmpty())
  3315             NS_ConvertUTF16toUTF8 oldCValue(oldValue);
  3316             return StoreURI(oldCValue.get(), aNeedsPersisting, aData);
  3320     return NS_OK;
  3323 nsresult
  3324 nsWebBrowserPersist::FixupURI(nsAString &aURI)
  3326     // get the current location of the file (absolutized)
  3327     nsCOMPtr<nsIURI> uri;
  3328     nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
  3329                             mCurrentCharset.get(), mCurrentBaseURI);
  3330     NS_ENSURE_SUCCESS(rv, rv);
  3331     nsAutoCString spec;
  3332     rv = uri->GetSpec(spec);
  3333     NS_ENSURE_SUCCESS(rv, rv);
  3335     // Search for the URI in the map and replace it with the local file
  3336     if (!mURIMap.Contains(spec))
  3338         return NS_ERROR_FAILURE;
  3340     URIData *data = mURIMap.Get(spec);
  3341     if (!data->mNeedsFixup)
  3343         return NS_OK;
  3345     nsCOMPtr<nsIURI> fileAsURI;
  3346     if (data->mFile)
  3348         rv = data->mFile->Clone(getter_AddRefs(fileAsURI));
  3349         NS_ENSURE_SUCCESS(rv, rv);
  3351     else
  3353         rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
  3354         NS_ENSURE_SUCCESS(rv, rv);
  3355         rv = AppendPathToURI(fileAsURI, data->mFilename);
  3356         NS_ENSURE_SUCCESS(rv, rv);
  3358     nsAutoString newValue;
  3360     // remove username/password if present
  3361     fileAsURI->SetUserPass(EmptyCString());
  3363     // reset node attribute
  3364     // Use relative or absolute links
  3365     if (data->mDataPathIsRelative)
  3367         nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
  3368         if (!url)
  3369           return NS_ERROR_FAILURE;
  3371         nsAutoCString filename;
  3372         url->GetFileName(filename);
  3374         nsAutoCString rawPathURL(data->mRelativePathToData);
  3375         rawPathURL.Append(filename);
  3377         nsAutoCString buf;
  3378         AppendUTF8toUTF16(NS_EscapeURL(rawPathURL, esc_FilePath, buf),
  3379                           newValue);
  3381     else
  3383         nsAutoCString fileurl;
  3384         fileAsURI->GetSpec(fileurl);
  3385         AppendUTF8toUTF16(fileurl, newValue);
  3387     if (data->mIsSubFrame)
  3389         newValue.Append(data->mSubFrameExt);
  3392     aURI = newValue;
  3393     return NS_OK;
  3396 nsresult
  3397 nsWebBrowserPersist::FixupNodeAttributeNS(nsIDOMNode *aNode,
  3398                                           const char *aNamespaceURI,
  3399                                           const char *aAttribute)
  3401     NS_ENSURE_ARG_POINTER(aNode);
  3402     NS_ENSURE_ARG_POINTER(aNamespaceURI);
  3403     NS_ENSURE_ARG_POINTER(aAttribute);
  3405     // Find the named URI attribute on the (element) node and change it to reference
  3406     // a local file.
  3408     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  3409     MOZ_ASSERT(element);
  3411     nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  3412     nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  3413     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3415     NS_ConvertASCIItoUTF16 attribute(aAttribute);
  3416     NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
  3417     nsCOMPtr<nsIDOMAttr> attr;
  3418     rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
  3419     if (attr) {
  3420         nsString uri;
  3421         attr->GetValue(uri);
  3422         rv = FixupURI(uri);
  3423         if (NS_SUCCEEDED(rv))
  3425             attr->SetValue(uri);
  3429     return rv;
  3432 nsresult
  3433 nsWebBrowserPersist::FixupAnchor(nsIDOMNode *aNode)
  3435     NS_ENSURE_ARG_POINTER(aNode);
  3437     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  3438     MOZ_ASSERT(element);
  3440     nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
  3441     nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
  3442     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3444     if (mPersistFlags & PERSIST_FLAGS_DONT_FIXUP_LINKS)
  3446         return NS_OK;
  3449     // Make all anchor links absolute so they point off onto the Internet
  3450     nsString attribute(NS_LITERAL_STRING("href"));
  3451     nsCOMPtr<nsIDOMAttr> attr;
  3452     rv = attrMap->GetNamedItem(attribute, getter_AddRefs(attr));
  3453     if (attr)
  3455         nsString oldValue;
  3456         attr->GetValue(oldValue);
  3457         NS_ConvertUTF16toUTF8 oldCValue(oldValue);
  3459         // Skip empty values and self-referencing bookmarks
  3460         if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#')
  3462             return NS_OK;
  3465         // if saving file to same location, we don't need to do any fixup
  3466         bool isEqual = false;
  3467         if (NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual))
  3468             && isEqual)
  3470             return NS_OK;
  3473         nsCOMPtr<nsIURI> relativeURI;
  3474         relativeURI = (mPersistFlags & PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
  3475                       ? mTargetBaseURI : mCurrentBaseURI;
  3476         // Make a new URI to replace the current one
  3477         nsCOMPtr<nsIURI> newURI;
  3478         rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
  3479                        mCurrentCharset.get(), relativeURI);
  3480         if (NS_SUCCEEDED(rv) && newURI)
  3482             newURI->SetUserPass(EmptyCString());
  3483             nsAutoCString uriSpec;
  3484             newURI->GetSpec(uriSpec);
  3485             attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec));
  3489     return NS_OK;
  3492 nsresult
  3493 nsWebBrowserPersist::StoreAndFixupStyleSheet(nsIStyleSheet *aStyleSheet)
  3495     // TODO go through the style sheet fixing up all links
  3496     return NS_OK;
  3499 bool
  3500 nsWebBrowserPersist::DocumentEncoderExists(const char16_t *aContentType)
  3502     // Check if there is an encoder for the desired content type.
  3503     nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  3504     AppendUTF16toUTF8(aContentType, contractID);
  3506     nsCOMPtr<nsIComponentRegistrar> registrar;
  3507     NS_GetComponentRegistrar(getter_AddRefs(registrar));
  3508     if (registrar)
  3510         bool result;
  3511         nsresult rv = registrar->IsContractIDRegistered(contractID.get(),
  3512                                                         &result);
  3513         if (NS_SUCCEEDED(rv) && result)
  3515             return true;
  3518     return false;
  3521 nsresult
  3522 nsWebBrowserPersist::SaveSubframeContent(
  3523     nsIDOMDocument *aFrameContent, URIData *aData)
  3525     NS_ENSURE_ARG_POINTER(aData);
  3527     // Extract the content type for the frame's contents.
  3528     nsCOMPtr<nsIDocument> frameDoc(do_QueryInterface(aFrameContent));
  3529     NS_ENSURE_STATE(frameDoc);
  3531     nsAutoString contentType;
  3532     nsresult rv = frameDoc->GetContentType(contentType);
  3533     NS_ENSURE_SUCCESS(rv, rv);
  3535     nsXPIDLString ext;
  3536     GetExtensionForContentType(contentType.get(), getter_Copies(ext));
  3538     // We must always have an extension so we will try to re-assign
  3539     // the original extension if GetExtensionForContentType fails.
  3540     if (ext.IsEmpty())
  3542         nsCOMPtr<nsIURL> url(do_QueryInterface(frameDoc->GetDocumentURI(),
  3543                                                &rv));
  3544         nsAutoCString extension;
  3545         if (NS_SUCCEEDED(rv))
  3547             url->GetFileExtension(extension);
  3549         else
  3551             extension.AssignLiteral("htm");
  3553         aData->mSubFrameExt.Assign(char16_t('.'));
  3554         AppendUTF8toUTF16(extension, aData->mSubFrameExt);
  3556     else
  3558         aData->mSubFrameExt.Assign(char16_t('.'));
  3559         aData->mSubFrameExt.Append(ext);
  3562     nsString filenameWithExt = aData->mFilename;
  3563     filenameWithExt.Append(aData->mSubFrameExt);
  3565     // Work out the path for the subframe
  3566     nsCOMPtr<nsIURI> frameURI;
  3567     rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
  3568     NS_ENSURE_SUCCESS(rv, rv);
  3569     rv = AppendPathToURI(frameURI, filenameWithExt);
  3570     NS_ENSURE_SUCCESS(rv, rv);
  3572     // Work out the path for the subframe data
  3573     nsCOMPtr<nsIURI> frameDataURI;
  3574     rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
  3575     NS_ENSURE_SUCCESS(rv, rv);
  3576     nsAutoString newFrameDataPath(aData->mFilename);
  3578     // Append _data
  3579     newFrameDataPath.AppendLiteral("_data");
  3580     rv = AppendPathToURI(frameDataURI, newFrameDataPath);
  3581     NS_ENSURE_SUCCESS(rv, rv);
  3583     // Make frame document & data path conformant and unique
  3584     rv = CalculateUniqueFilename(frameURI);
  3585     NS_ENSURE_SUCCESS(rv, rv);
  3586     rv = CalculateUniqueFilename(frameDataURI);
  3587     NS_ENSURE_SUCCESS(rv, rv);
  3589     mCurrentThingsToPersist++;
  3591     // We shouldn't use SaveDocumentInternal for the contents
  3592     // of frames that are not documents, e.g. images.
  3593     if (DocumentEncoderExists(contentType.get()))
  3595         rv = SaveDocumentInternal(aFrameContent, frameURI, frameDataURI);
  3597     else
  3599         rv = StoreURI(frameDoc->GetDocumentURI());
  3601     NS_ENSURE_SUCCESS(rv, rv);
  3603     // Store the updated uri to the frame
  3604     aData->mFile = frameURI;
  3605     aData->mSubFrameExt.Truncate(); // we already put this in frameURI
  3607     return NS_OK;
  3610 nsresult
  3611 nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
  3613     nsresult rv = NS_OK;
  3614     *aChannel = nullptr;
  3616     nsCOMPtr<nsIIOService> ioserv;
  3617     ioserv = do_GetIOService(&rv);
  3618     NS_ENSURE_SUCCESS(rv, rv);
  3620     rv = ioserv->NewChannelFromURI(aURI, aChannel);
  3621     NS_ENSURE_SUCCESS(rv, rv);
  3622     NS_ENSURE_ARG_POINTER(*aChannel);
  3624     rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
  3625     NS_ENSURE_SUCCESS(rv, rv);
  3626     return NS_OK;
  3629 nsresult
  3630 nsWebBrowserPersist::SaveDocumentWithFixup(
  3631     nsIDOMDocument *aDocument, nsIDocumentEncoderNodeFixup *aNodeFixup,
  3632     nsIURI *aFile, bool aReplaceExisting, const nsACString &aFormatType,
  3633     const nsCString &aSaveCharset, uint32_t aFlags)
  3635     NS_ENSURE_ARG_POINTER(aFile);
  3637     nsresult  rv = NS_OK;
  3638     nsCOMPtr<nsIFile> localFile;
  3639     GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
  3640     if (localFile)
  3642         // if we're not replacing an existing file but the file
  3643         // exists, something is wrong
  3644         bool fileExists = false;
  3645         rv = localFile->Exists(&fileExists);
  3646         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3648         if (!aReplaceExisting && fileExists)
  3649             return NS_ERROR_FAILURE;                // where are the file I/O errors?
  3652     nsCOMPtr<nsIOutputStream> outputStream;
  3653     rv = MakeOutputStream(aFile, getter_AddRefs(outputStream));
  3654     if (NS_FAILED(rv))
  3656         SendErrorStatusChange(false, rv, nullptr, aFile);
  3657         return NS_ERROR_FAILURE;
  3659     NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
  3661     // Get a document encoder instance
  3662     nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
  3663     contractID.Append(aFormatType);
  3665     nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(contractID.get(), &rv);
  3666     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3668     NS_ConvertASCIItoUTF16 newContentType(aFormatType);
  3669     rv = encoder->Init(aDocument, newContentType, aFlags);
  3670     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3672     mTargetBaseURI = aFile;
  3674     // Set the node fixup callback
  3675     encoder->SetNodeFixup(aNodeFixup);
  3677     if (mWrapColumn && (aFlags & ENCODE_FLAGS_WRAP))
  3678         encoder->SetWrapColumn(mWrapColumn);
  3680     nsAutoCString charsetStr(aSaveCharset);
  3681     if (charsetStr.IsEmpty())
  3683         nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
  3684         NS_ASSERTION(doc, "Need a document");
  3685         charsetStr = doc->GetDocumentCharacterSet();
  3688     rv = encoder->SetCharset(charsetStr);
  3689     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3691     rv = encoder->EncodeToStream(outputStream);
  3692     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3694     if (!localFile)
  3696         nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(outputStream));
  3697         if (storStream)
  3699             outputStream->Close();
  3700             rv = StartUpload(storStream, aFile, aFormatType);
  3701             NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3705     return rv;
  3709 // we store the current location as the key (absolutized version of domnode's attribute's value)
  3710 nsresult
  3711 nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
  3712     nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
  3714     NS_ENSURE_ARG_POINTER(aURI);
  3716     nsAutoCString spec;
  3717     nsresult rv = aURI->GetSpec(spec);
  3718     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3720     // Create a sensibly named filename for the URI and store in the URI map
  3721     URIData *data;
  3722     if (mURIMap.Contains(spec))
  3724         data = mURIMap.Get(spec);
  3725         if (aNeedsPersisting)
  3727           data->mNeedsPersisting = true;
  3729         if (aData)
  3731             *aData = data;
  3733         return NS_OK;
  3736     // Create a unique file name for the uri
  3737     nsString filename;
  3738     rv = MakeFilenameFromURI(aURI, filename);
  3739     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
  3741     // Store the file name
  3742     data = new URIData;
  3743     NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
  3745     data->mNeedsPersisting = aNeedsPersisting;
  3746     data->mNeedsFixup = true;
  3747     data->mFilename = filename;
  3748     data->mSaved = false;
  3749     data->mIsSubFrame = false;
  3750     data->mDataPath = mCurrentDataPath;
  3751     data->mDataPathIsRelative = mCurrentDataPathIsRelative;
  3752     data->mRelativePathToData = mCurrentRelativePathToData;
  3753     data->mCharset = mCurrentCharset;
  3755     if (aNeedsPersisting)
  3756         mCurrentThingsToPersist++;
  3758     mURIMap.Put(spec, data);
  3759     if (aData)
  3761         *aData = data;
  3764     return NS_OK;
  3767 // Ordered so that typical documents work fastest.
  3768 //                                    strlen("blockquote")==10
  3769 static const char kSpecialXHTMLTags[][11] = {
  3770     "body",
  3771     "head",
  3772     "img",
  3773     "script",
  3774     "a",
  3775     "area",
  3776     "link",
  3777     "input",
  3778     "frame",
  3779     "iframe",
  3780     "object",
  3781     "applet",
  3782     "form",
  3783     "blockquote",
  3784     "q",
  3785     "del",
  3786     "ins"
  3787 };
  3789 static bool IsSpecialXHTMLTag(nsIDOMNode *aNode)
  3791     nsAutoString tmp;
  3792     aNode->GetNamespaceURI(tmp);
  3793     if (!tmp.EqualsLiteral("http://www.w3.org/1999/xhtml"))
  3794         return false;
  3796     aNode->GetLocalName(tmp);
  3797     for (uint32_t i = 0; i < ArrayLength(kSpecialXHTMLTags); i++) {
  3798         if (tmp.EqualsASCII(kSpecialXHTMLTags[i]))
  3800             // XXX This element MAY have URI attributes, but
  3801             //     we are not actually checking if they are present.
  3802             //     That would slow us down further, and I am not so sure
  3803             //     how important that would be.
  3804             return true;
  3808     return false;
  3811 static bool HasSpecialXHTMLTags(nsIDOMNode *aParent)
  3813     if (IsSpecialXHTMLTag(aParent))
  3814         return true;
  3816     nsCOMPtr<nsIDOMNodeList> list;
  3817     aParent->GetChildNodes(getter_AddRefs(list));
  3818     if (list)
  3820         uint32_t count;
  3821         list->GetLength(&count);
  3822         uint32_t i;
  3823         for (i = 0; i < count; i++) {
  3824             nsCOMPtr<nsIDOMNode> node;
  3825             list->Item(i, getter_AddRefs(node));
  3826             if (!node)
  3827                 break;
  3828             uint16_t nodeType;
  3829             node->GetNodeType(&nodeType);
  3830             if (nodeType == nsIDOMNode::ELEMENT_NODE) {
  3831                 return HasSpecialXHTMLTags(node);
  3836     return false;
  3839 static bool NeedXHTMLBaseTag(nsIDOMDocument *aDocument)
  3841     nsCOMPtr<nsIDOMElement> docElement;
  3842     aDocument->GetDocumentElement(getter_AddRefs(docElement));
  3844     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(docElement));
  3845     if (node)
  3847         return HasSpecialXHTMLTags(node);
  3850     return false;
  3853 // Set document base. This could create an invalid XML document (still well-formed).
  3854 nsresult
  3855 nsWebBrowserPersist::SetDocumentBase(
  3856     nsIDOMDocument *aDocument, nsIURI *aBaseURI)
  3858     if (mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS)
  3860         return NS_OK;
  3863     NS_ENSURE_ARG_POINTER(aBaseURI);
  3865     nsCOMPtr<nsIDOMXMLDocument> xmlDoc;
  3866     nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
  3867     if (!htmlDoc)
  3869         xmlDoc = do_QueryInterface(aDocument);
  3870         if (!xmlDoc)
  3872             return NS_ERROR_FAILURE;
  3876     NS_NAMED_LITERAL_STRING(kXHTMLNS, "http://www.w3.org/1999/xhtml");
  3877     NS_NAMED_LITERAL_STRING(kHead, "head");
  3879     // Find the head element
  3880     nsCOMPtr<nsIDOMElement> headElement;
  3881     nsCOMPtr<nsIDOMNodeList> headList;
  3882     if (xmlDoc)
  3884         // First see if there is XHTML content that needs base
  3885         // tags.
  3886         if (!NeedXHTMLBaseTag(aDocument))
  3887             return NS_OK;
  3889         aDocument->GetElementsByTagNameNS(
  3890             kXHTMLNS,
  3891             kHead, getter_AddRefs(headList));
  3893     else
  3895         aDocument->GetElementsByTagName(
  3896             kHead, getter_AddRefs(headList));
  3898     if (headList)
  3900         nsCOMPtr<nsIDOMNode> headNode;
  3901         headList->Item(0, getter_AddRefs(headNode));
  3902         headElement = do_QueryInterface(headNode);
  3904     if (!headElement)
  3906         // Create head and insert as first element
  3907         nsCOMPtr<nsIDOMNode> firstChildNode;
  3908         nsCOMPtr<nsIDOMNode> newNode;
  3909         if (xmlDoc)
  3911             aDocument->CreateElementNS(
  3912                 kXHTMLNS,
  3913                 kHead, getter_AddRefs(headElement));
  3915         else
  3917             aDocument->CreateElement(
  3918                 kHead, getter_AddRefs(headElement));
  3920         nsCOMPtr<nsIDOMElement> documentElement;
  3921         aDocument->GetDocumentElement(getter_AddRefs(documentElement));
  3922         if (documentElement)
  3924             documentElement->GetFirstChild(getter_AddRefs(firstChildNode));
  3925             documentElement->InsertBefore(headElement, firstChildNode, getter_AddRefs(newNode));
  3928     if (!headElement)
  3930         return NS_ERROR_FAILURE;
  3933     // Find or create the BASE element
  3934     NS_NAMED_LITERAL_STRING(kBase, "base");
  3935     nsCOMPtr<nsIDOMElement> baseElement;
  3936     nsCOMPtr<nsIDOMHTMLCollection> baseList;
  3937     if (xmlDoc)
  3939         headElement->GetElementsByTagNameNS(
  3940             kXHTMLNS,
  3941             kBase, getter_AddRefs(baseList));
  3943     else
  3945         headElement->GetElementsByTagName(
  3946             kBase, getter_AddRefs(baseList));
  3948     if (baseList)
  3950         nsCOMPtr<nsIDOMNode> baseNode;
  3951         baseList->Item(0, getter_AddRefs(baseNode));
  3952         baseElement = do_QueryInterface(baseNode);
  3955     // Add the BASE element
  3956     if (!baseElement)
  3958       nsCOMPtr<nsIDOMNode> newNode;
  3959       if (xmlDoc)
  3961           aDocument->CreateElementNS(
  3962               kXHTMLNS,
  3963               kBase, getter_AddRefs(baseElement));
  3965       else
  3967           aDocument->CreateElement(
  3968               kBase, getter_AddRefs(baseElement));
  3970       headElement->AppendChild(baseElement, getter_AddRefs(newNode));
  3972     if (!baseElement)
  3974         return NS_ERROR_FAILURE;
  3976     nsAutoCString uriSpec;
  3977     aBaseURI->GetSpec(uriSpec);
  3978     NS_ConvertUTF8toUTF16 href(uriSpec);
  3979     baseElement->SetAttribute(NS_LITERAL_STRING("href"), href);
  3981     return NS_OK;
  3984 // Decide if we need to apply conversion to the passed channel.
  3985 void nsWebBrowserPersist::SetApplyConversionIfNeeded(nsIChannel *aChannel)
  3987     nsresult rv = NS_OK;
  3988     nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aChannel, &rv);
  3989     if (NS_FAILED(rv))
  3990         return;
  3992     // Set the default conversion preference:
  3993     encChannel->SetApplyConversion(false);
  3995     nsCOMPtr<nsIURI> thisURI;
  3996     aChannel->GetURI(getter_AddRefs(thisURI));
  3997     nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(thisURI));
  3998     if (!sourceURL)
  3999         return;
  4000     nsAutoCString extension;
  4001     sourceURL->GetFileExtension(extension);
  4003     nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
  4004     encChannel->GetContentEncodings(getter_AddRefs(encEnum));
  4005     if (!encEnum)
  4006         return;
  4007     nsCOMPtr<nsIExternalHelperAppService> helperAppService =
  4008         do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
  4009     if (NS_FAILED(rv))
  4010         return;
  4011     bool hasMore;
  4012     rv = encEnum->HasMore(&hasMore);
  4013     if (NS_SUCCEEDED(rv) && hasMore)
  4015         nsAutoCString encType;
  4016         rv = encEnum->GetNext(encType);
  4017         if (NS_SUCCEEDED(rv))
  4019             bool applyConversion = false;
  4020             rv = helperAppService->ApplyDecodingForExtension(extension, encType,
  4021                                                              &applyConversion);
  4022             if (NS_SUCCEEDED(rv))
  4023                 encChannel->SetApplyConversion(applyConversion);
  4028 ///////////////////////////////////////////////////////////////////////////////
  4031 nsEncoderNodeFixup::nsEncoderNodeFixup() : mWebBrowserPersist(nullptr)
  4036 nsEncoderNodeFixup::~nsEncoderNodeFixup()
  4041 NS_IMPL_ADDREF(nsEncoderNodeFixup)
  4042 NS_IMPL_RELEASE(nsEncoderNodeFixup)
  4045 NS_INTERFACE_MAP_BEGIN(nsEncoderNodeFixup)
  4046     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentEncoderNodeFixup)
  4047     NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoderNodeFixup)
  4048 NS_INTERFACE_MAP_END
  4051 NS_IMETHODIMP nsEncoderNodeFixup::FixupNode(
  4052     nsIDOMNode *aNode, bool *aSerializeCloneKids, nsIDOMNode **aOutNode)
  4054     NS_ENSURE_ARG_POINTER(aNode);
  4055     NS_ENSURE_ARG_POINTER(aOutNode);
  4056     NS_ENSURE_TRUE(mWebBrowserPersist, NS_ERROR_FAILURE);
  4058     *aOutNode = nullptr;
  4060     // Test whether we need to fixup the node
  4061     uint16_t type = 0;
  4062     aNode->GetNodeType(&type);
  4063     if (type == nsIDOMNode::ELEMENT_NODE ||
  4064         type == nsIDOMNode::PROCESSING_INSTRUCTION_NODE)
  4066         return mWebBrowserPersist->CloneNodeWithFixedUpAttributes(aNode, aSerializeCloneKids, aOutNode);
  4069     return NS_OK;

mercurial