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