embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/ArrayUtils.h"
michael@0 7
michael@0 8 #include "nspr.h"
michael@0 9
michael@0 10 #include "nsIFileStreams.h" // New Necko file streams
michael@0 11 #include <algorithm>
michael@0 12
michael@0 13 #include "nsNetUtil.h"
michael@0 14 #include "nsComponentManagerUtils.h"
michael@0 15 #include "nsIComponentRegistrar.h"
michael@0 16 #include "nsIStorageStream.h"
michael@0 17 #include "nsISeekableStream.h"
michael@0 18 #include "nsIHttpChannel.h"
michael@0 19 #include "nsIHttpChannelInternal.h"
michael@0 20 #include "nsIEncodedChannel.h"
michael@0 21 #include "nsIUploadChannel.h"
michael@0 22 #include "nsICachingChannel.h"
michael@0 23 #include "nsIFileChannel.h"
michael@0 24 #include "nsEscape.h"
michael@0 25 #include "nsUnicharUtils.h"
michael@0 26 #include "nsIStringEnumerator.h"
michael@0 27 #include "nsCRT.h"
michael@0 28 #include "nsSupportsArray.h"
michael@0 29 #include "nsContentCID.h"
michael@0 30 #include "nsStreamUtils.h"
michael@0 31
michael@0 32 #include "nsCExternalHandlerService.h"
michael@0 33
michael@0 34 #include "nsIURL.h"
michael@0 35 #include "nsIFileURL.h"
michael@0 36 #include "nsIDocument.h"
michael@0 37 #include "nsIDOMDocument.h"
michael@0 38 #include "nsIDOMXMLDocument.h"
michael@0 39 #include "nsIDOMTreeWalker.h"
michael@0 40 #include "nsIDOMNode.h"
michael@0 41 #include "nsIDOMComment.h"
michael@0 42 #include "nsIDOMMozNamedAttrMap.h"
michael@0 43 #include "nsIDOMAttr.h"
michael@0 44 #include "nsIDOMNodeList.h"
michael@0 45 #include "nsIWebProgressListener.h"
michael@0 46 #include "nsIAuthPrompt.h"
michael@0 47 #include "nsIPrompt.h"
michael@0 48 #include "nsISHEntry.h"
michael@0 49 #include "nsIWebPageDescriptor.h"
michael@0 50 #include "nsIFormControl.h"
michael@0 51 #include "nsContentUtils.h"
michael@0 52
michael@0 53 #include "nsIDOMNodeFilter.h"
michael@0 54 #include "nsIDOMProcessingInstruction.h"
michael@0 55 #include "nsIDOMHTMLAnchorElement.h"
michael@0 56 #include "nsIDOMHTMLAreaElement.h"
michael@0 57 #include "nsIDOMHTMLCollection.h"
michael@0 58 #include "nsIDOMHTMLImageElement.h"
michael@0 59 #include "nsIDOMHTMLScriptElement.h"
michael@0 60 #include "nsIDOMHTMLLinkElement.h"
michael@0 61 #include "nsIDOMHTMLBaseElement.h"
michael@0 62 #include "nsIDOMHTMLFrameElement.h"
michael@0 63 #include "nsIDOMHTMLIFrameElement.h"
michael@0 64 #include "nsIDOMHTMLInputElement.h"
michael@0 65 #include "nsIDOMHTMLEmbedElement.h"
michael@0 66 #include "nsIDOMHTMLObjectElement.h"
michael@0 67 #include "nsIDOMHTMLAppletElement.h"
michael@0 68 #include "nsIDOMHTMLOptionElement.h"
michael@0 69 #include "nsIDOMHTMLTextAreaElement.h"
michael@0 70 #include "nsIDOMHTMLDocument.h"
michael@0 71 #include "nsIDOMHTMLSourceElement.h"
michael@0 72 #include "nsIDOMHTMLMediaElement.h"
michael@0 73
michael@0 74 #include "nsIImageLoadingContent.h"
michael@0 75
michael@0 76 #include "ftpCore.h"
michael@0 77 #include "nsITransport.h"
michael@0 78 #include "nsISocketTransport.h"
michael@0 79 #include "nsIStringBundle.h"
michael@0 80 #include "nsIProtocolHandler.h"
michael@0 81
michael@0 82 #include "nsWebBrowserPersist.h"
michael@0 83
michael@0 84 #include "nsIContent.h"
michael@0 85 #include "nsIMIMEInfo.h"
michael@0 86 #include "mozilla/dom/HTMLInputElement.h"
michael@0 87 #include "mozilla/dom/HTMLSharedElement.h"
michael@0 88 #include "mozilla/dom/HTMLSharedObjectElement.h"
michael@0 89
michael@0 90 using namespace mozilla;
michael@0 91 using namespace mozilla::dom;
michael@0 92
michael@0 93 // Buffer file writes in 32kb chunks
michael@0 94 #define BUFFERED_OUTPUT_SIZE (1024 * 32)
michael@0 95
michael@0 96 // Information about a DOM document
michael@0 97 struct DocData
michael@0 98 {
michael@0 99 nsCOMPtr<nsIURI> mBaseURI;
michael@0 100 nsCOMPtr<nsIDOMDocument> mDocument;
michael@0 101 nsCOMPtr<nsIURI> mFile;
michael@0 102 nsCOMPtr<nsIURI> mDataPath;
michael@0 103 bool mDataPathIsRelative;
michael@0 104 nsCString mRelativePathToData;
michael@0 105 nsCString mCharset;
michael@0 106 };
michael@0 107
michael@0 108 // Information about a URI
michael@0 109 struct URIData
michael@0 110 {
michael@0 111 bool mNeedsPersisting;
michael@0 112 bool mSaved;
michael@0 113 bool mIsSubFrame;
michael@0 114 bool mDataPathIsRelative;
michael@0 115 bool mNeedsFixup;
michael@0 116 nsString mFilename;
michael@0 117 nsString mSubFrameExt;
michael@0 118 nsCOMPtr<nsIURI> mFile;
michael@0 119 nsCOMPtr<nsIURI> mDataPath;
michael@0 120 nsCString mRelativePathToData;
michael@0 121 nsCString mCharset;
michael@0 122 };
michael@0 123
michael@0 124 // Information about the output stream
michael@0 125 struct OutputData
michael@0 126 {
michael@0 127 nsCOMPtr<nsIURI> mFile;
michael@0 128 nsCOMPtr<nsIURI> mOriginalLocation;
michael@0 129 nsCOMPtr<nsIOutputStream> mStream;
michael@0 130 int64_t mSelfProgress;
michael@0 131 int64_t mSelfProgressMax;
michael@0 132 bool mCalcFileExt;
michael@0 133
michael@0 134 OutputData(nsIURI *aFile, nsIURI *aOriginalLocation, bool aCalcFileExt) :
michael@0 135 mFile(aFile),
michael@0 136 mOriginalLocation(aOriginalLocation),
michael@0 137 mSelfProgress(0),
michael@0 138 mSelfProgressMax(10000),
michael@0 139 mCalcFileExt(aCalcFileExt)
michael@0 140 {
michael@0 141 }
michael@0 142 ~OutputData()
michael@0 143 {
michael@0 144 if (mStream)
michael@0 145 {
michael@0 146 mStream->Close();
michael@0 147 }
michael@0 148 }
michael@0 149 };
michael@0 150
michael@0 151 struct UploadData
michael@0 152 {
michael@0 153 nsCOMPtr<nsIURI> mFile;
michael@0 154 int64_t mSelfProgress;
michael@0 155 int64_t mSelfProgressMax;
michael@0 156
michael@0 157 UploadData(nsIURI *aFile) :
michael@0 158 mFile(aFile),
michael@0 159 mSelfProgress(0),
michael@0 160 mSelfProgressMax(10000)
michael@0 161 {
michael@0 162 }
michael@0 163 };
michael@0 164
michael@0 165 struct CleanupData
michael@0 166 {
michael@0 167 nsCOMPtr<nsIFile> mFile;
michael@0 168 // Snapshot of what the file actually is at the time of creation so that if
michael@0 169 // it transmutes into something else later on it can be ignored. For example,
michael@0 170 // catch files that turn into dirs or vice versa.
michael@0 171 bool mIsDirectory;
michael@0 172 };
michael@0 173
michael@0 174 // Maximum file length constant. The max file name length is
michael@0 175 // volume / server dependent but it is difficult to obtain
michael@0 176 // that information. Instead this constant is a reasonable value that
michael@0 177 // modern systems should able to cope with.
michael@0 178 const uint32_t kDefaultMaxFilenameLength = 64;
michael@0 179
michael@0 180 // Default flags for persistence
michael@0 181 const uint32_t kDefaultPersistFlags =
michael@0 182 nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
michael@0 183 nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
michael@0 184
michael@0 185 // String bundle where error messages come from
michael@0 186 const char *kWebBrowserPersistStringBundle =
michael@0 187 "chrome://global/locale/nsWebBrowserPersist.properties";
michael@0 188
michael@0 189 nsWebBrowserPersist::nsWebBrowserPersist() :
michael@0 190 mCurrentThingsToPersist(0),
michael@0 191 mFirstAndOnlyUse(true),
michael@0 192 mCancel(false),
michael@0 193 mJustStartedLoading(true),
michael@0 194 mCompleted(false),
michael@0 195 mStartSaving(false),
michael@0 196 mReplaceExisting(true),
michael@0 197 mSerializingOutput(false),
michael@0 198 mIsPrivate(false),
michael@0 199 mPersistFlags(kDefaultPersistFlags),
michael@0 200 mPersistResult(NS_OK),
michael@0 201 mTotalCurrentProgress(0),
michael@0 202 mTotalMaxProgress(0),
michael@0 203 mWrapColumn(72),
michael@0 204 mEncodingFlags(0)
michael@0 205 {
michael@0 206 }
michael@0 207
michael@0 208 nsWebBrowserPersist::~nsWebBrowserPersist()
michael@0 209 {
michael@0 210 Cleanup();
michael@0 211 }
michael@0 212
michael@0 213 //*****************************************************************************
michael@0 214 // nsWebBrowserPersist::nsISupports
michael@0 215 //*****************************************************************************
michael@0 216
michael@0 217 NS_IMPL_ADDREF(nsWebBrowserPersist)
michael@0 218 NS_IMPL_RELEASE(nsWebBrowserPersist)
michael@0 219
michael@0 220 NS_INTERFACE_MAP_BEGIN(nsWebBrowserPersist)
michael@0 221 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersist)
michael@0 222 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersist)
michael@0 223 NS_INTERFACE_MAP_ENTRY(nsICancelable)
michael@0 224 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
michael@0 225 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 226 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
michael@0 227 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
michael@0 228 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
michael@0 229 NS_INTERFACE_MAP_END
michael@0 230
michael@0 231
michael@0 232 //*****************************************************************************
michael@0 233 // nsWebBrowserPersist::nsIInterfaceRequestor
michael@0 234 //*****************************************************************************
michael@0 235
michael@0 236 NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
michael@0 237 {
michael@0 238 NS_ENSURE_ARG_POINTER(aIFace);
michael@0 239
michael@0 240 *aIFace = nullptr;
michael@0 241
michael@0 242 nsresult rv = QueryInterface(aIID, aIFace);
michael@0 243 if (NS_SUCCEEDED(rv))
michael@0 244 {
michael@0 245 return rv;
michael@0 246 }
michael@0 247
michael@0 248 if (mProgressListener && (aIID.Equals(NS_GET_IID(nsIAuthPrompt))
michael@0 249 || aIID.Equals(NS_GET_IID(nsIPrompt))))
michael@0 250 {
michael@0 251 mProgressListener->QueryInterface(aIID, aIFace);
michael@0 252 if (*aIFace)
michael@0 253 return NS_OK;
michael@0 254 }
michael@0 255
michael@0 256 nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
michael@0 257 if (req)
michael@0 258 {
michael@0 259 return req->GetInterface(aIID, aIFace);
michael@0 260 }
michael@0 261
michael@0 262 return NS_ERROR_NO_INTERFACE;
michael@0 263 }
michael@0 264
michael@0 265
michael@0 266 //*****************************************************************************
michael@0 267 // nsWebBrowserPersist::nsIWebBrowserPersist
michael@0 268 //*****************************************************************************
michael@0 269
michael@0 270 /* attribute unsigned long persistFlags; */
michael@0 271 NS_IMETHODIMP nsWebBrowserPersist::GetPersistFlags(uint32_t *aPersistFlags)
michael@0 272 {
michael@0 273 NS_ENSURE_ARG_POINTER(aPersistFlags);
michael@0 274 *aPersistFlags = mPersistFlags;
michael@0 275 return NS_OK;
michael@0 276 }
michael@0 277 NS_IMETHODIMP nsWebBrowserPersist::SetPersistFlags(uint32_t aPersistFlags)
michael@0 278 {
michael@0 279 mPersistFlags = aPersistFlags;
michael@0 280 mReplaceExisting = (mPersistFlags & PERSIST_FLAGS_REPLACE_EXISTING_FILES) ? true : false;
michael@0 281 mSerializingOutput = (mPersistFlags & PERSIST_FLAGS_SERIALIZE_OUTPUT) ? true : false;
michael@0 282 return NS_OK;
michael@0 283 }
michael@0 284
michael@0 285 /* readonly attribute unsigned long currentState; */
michael@0 286 NS_IMETHODIMP nsWebBrowserPersist::GetCurrentState(uint32_t *aCurrentState)
michael@0 287 {
michael@0 288 NS_ENSURE_ARG_POINTER(aCurrentState);
michael@0 289 if (mCompleted)
michael@0 290 {
michael@0 291 *aCurrentState = PERSIST_STATE_FINISHED;
michael@0 292 }
michael@0 293 else if (mFirstAndOnlyUse)
michael@0 294 {
michael@0 295 *aCurrentState = PERSIST_STATE_SAVING;
michael@0 296 }
michael@0 297 else
michael@0 298 {
michael@0 299 *aCurrentState = PERSIST_STATE_READY;
michael@0 300 }
michael@0 301 return NS_OK;
michael@0 302 }
michael@0 303
michael@0 304 /* readonly attribute unsigned long result; */
michael@0 305 NS_IMETHODIMP nsWebBrowserPersist::GetResult(nsresult *aResult)
michael@0 306 {
michael@0 307 NS_ENSURE_ARG_POINTER(aResult);
michael@0 308 *aResult = mPersistResult;
michael@0 309 return NS_OK;
michael@0 310 }
michael@0 311
michael@0 312 /* attribute nsIWebBrowserPersistProgress progressListener; */
michael@0 313 NS_IMETHODIMP nsWebBrowserPersist::GetProgressListener(
michael@0 314 nsIWebProgressListener * *aProgressListener)
michael@0 315 {
michael@0 316 NS_ENSURE_ARG_POINTER(aProgressListener);
michael@0 317 *aProgressListener = mProgressListener;
michael@0 318 NS_IF_ADDREF(*aProgressListener);
michael@0 319 return NS_OK;
michael@0 320 }
michael@0 321
michael@0 322 NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
michael@0 323 nsIWebProgressListener * aProgressListener)
michael@0 324 {
michael@0 325 mProgressListener = aProgressListener;
michael@0 326 mProgressListener2 = do_QueryInterface(aProgressListener);
michael@0 327 mEventSink = do_GetInterface(aProgressListener);
michael@0 328 return NS_OK;
michael@0 329 }
michael@0 330
michael@0 331 /* void saveURI (in nsIURI aURI, in nsISupports aCacheKey, in nsIURI aReferrer,
michael@0 332 in nsIInputStream aPostData, in wstring aExtraHeaders,
michael@0 333 in nsISupports aFile, in nsILoadContext aPrivayContext); */
michael@0 334 NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
michael@0 335 nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, nsILoadContext* aPrivacyContext)
michael@0 336 {
michael@0 337 return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, aFile,
michael@0 338 aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
michael@0 339 }
michael@0 340
michael@0 341 NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
michael@0 342 nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, bool aIsPrivate)
michael@0 343 {
michael@0 344 NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
michael@0 345 mFirstAndOnlyUse = false; // Stop people from reusing this object!
michael@0 346
michael@0 347 nsCOMPtr<nsIURI> fileAsURI;
michael@0 348 nsresult rv;
michael@0 349 rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
michael@0 350 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 351
michael@0 352 // SaveURI doesn't like broken uris.
michael@0 353 mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
michael@0 354 rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
michael@0 355 return NS_FAILED(rv) ? rv : NS_OK;
michael@0 356 }
michael@0 357
michael@0 358 /* void saveChannel (in nsIChannel aChannel, in nsISupports aFile); */
michael@0 359 NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
michael@0 360 nsIChannel *aChannel, nsISupports *aFile)
michael@0 361 {
michael@0 362 NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
michael@0 363 mFirstAndOnlyUse = false; // Stop people from reusing this object!
michael@0 364
michael@0 365 nsCOMPtr<nsIURI> fileAsURI;
michael@0 366 nsresult rv;
michael@0 367 rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
michael@0 368 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 369
michael@0 370 rv = aChannel->GetURI(getter_AddRefs(mURI));
michael@0 371 NS_ENSURE_SUCCESS(rv, rv);
michael@0 372
michael@0 373 // SaveURI doesn't like broken uris.
michael@0 374 mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
michael@0 375 rv = SaveChannelInternal(aChannel, fileAsURI, false);
michael@0 376 return NS_FAILED(rv) ? rv : NS_OK;
michael@0 377 }
michael@0 378
michael@0 379
michael@0 380 /* void saveDocument (in nsIDOMDocument aDocument, in nsIURI aFileURI,
michael@0 381 in nsIURI aDataPathURI, in string aOutputContentType,
michael@0 382 in unsigned long aEncodingFlags, in unsigned long aWrapColumn); */
michael@0 383 NS_IMETHODIMP nsWebBrowserPersist::SaveDocument(
michael@0 384 nsIDOMDocument *aDocument, nsISupports *aFile, nsISupports *aDataPath,
michael@0 385 const char *aOutputContentType, uint32_t aEncodingFlags, uint32_t aWrapColumn)
michael@0 386 {
michael@0 387 NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
michael@0 388 mFirstAndOnlyUse = false; // Stop people from reusing this object!
michael@0 389
michael@0 390 nsCOMPtr<nsIURI> fileAsURI;
michael@0 391 nsCOMPtr<nsIURI> datapathAsURI;
michael@0 392 nsresult rv;
michael@0 393
michael@0 394 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
michael@0 395 nsCOMPtr<nsILoadContext> privacyContext = doc ? doc->GetLoadContext() : nullptr;
michael@0 396 mIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
michael@0 397
michael@0 398 rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
michael@0 399 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 400 if (aDataPath)
michael@0 401 {
michael@0 402 rv = GetValidURIFromObject(aDataPath, getter_AddRefs(datapathAsURI));
michael@0 403 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 404 }
michael@0 405
michael@0 406 mWrapColumn = aWrapColumn;
michael@0 407
michael@0 408 // Produce nsIDocumentEncoder encoding flags
michael@0 409 mEncodingFlags = 0;
michael@0 410 if (aEncodingFlags & ENCODE_FLAGS_SELECTION_ONLY)
michael@0 411 mEncodingFlags |= nsIDocumentEncoder::OutputSelectionOnly;
michael@0 412 if (aEncodingFlags & ENCODE_FLAGS_FORMATTED)
michael@0 413 mEncodingFlags |= nsIDocumentEncoder::OutputFormatted;
michael@0 414 if (aEncodingFlags & ENCODE_FLAGS_RAW)
michael@0 415 mEncodingFlags |= nsIDocumentEncoder::OutputRaw;
michael@0 416 if (aEncodingFlags & ENCODE_FLAGS_BODY_ONLY)
michael@0 417 mEncodingFlags |= nsIDocumentEncoder::OutputBodyOnly;
michael@0 418 if (aEncodingFlags & ENCODE_FLAGS_PREFORMATTED)
michael@0 419 mEncodingFlags |= nsIDocumentEncoder::OutputPreformatted;
michael@0 420 if (aEncodingFlags & ENCODE_FLAGS_WRAP)
michael@0 421 mEncodingFlags |= nsIDocumentEncoder::OutputWrap;
michael@0 422 if (aEncodingFlags & ENCODE_FLAGS_FORMAT_FLOWED)
michael@0 423 mEncodingFlags |= nsIDocumentEncoder::OutputFormatFlowed;
michael@0 424 if (aEncodingFlags & ENCODE_FLAGS_ABSOLUTE_LINKS)
michael@0 425 mEncodingFlags |= nsIDocumentEncoder::OutputAbsoluteLinks;
michael@0 426 if (aEncodingFlags & ENCODE_FLAGS_ENCODE_BASIC_ENTITIES)
michael@0 427 mEncodingFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities;
michael@0 428 if (aEncodingFlags & ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES)
michael@0 429 mEncodingFlags |= nsIDocumentEncoder::OutputEncodeLatin1Entities;
michael@0 430 if (aEncodingFlags & ENCODE_FLAGS_ENCODE_HTML_ENTITIES)
michael@0 431 mEncodingFlags |= nsIDocumentEncoder::OutputEncodeHTMLEntities;
michael@0 432 if (aEncodingFlags & ENCODE_FLAGS_ENCODE_W3C_ENTITIES)
michael@0 433 mEncodingFlags |= nsIDocumentEncoder::OutputEncodeW3CEntities;
michael@0 434 if (aEncodingFlags & ENCODE_FLAGS_CR_LINEBREAKS)
michael@0 435 mEncodingFlags |= nsIDocumentEncoder::OutputCRLineBreak;
michael@0 436 if (aEncodingFlags & ENCODE_FLAGS_LF_LINEBREAKS)
michael@0 437 mEncodingFlags |= nsIDocumentEncoder::OutputLFLineBreak;
michael@0 438 if (aEncodingFlags & ENCODE_FLAGS_NOSCRIPT_CONTENT)
michael@0 439 mEncodingFlags |= nsIDocumentEncoder::OutputNoScriptContent;
michael@0 440 if (aEncodingFlags & ENCODE_FLAGS_NOFRAMES_CONTENT)
michael@0 441 mEncodingFlags |= nsIDocumentEncoder::OutputNoFramesContent;
michael@0 442
michael@0 443 if (aOutputContentType)
michael@0 444 {
michael@0 445 mContentType.AssignASCII(aOutputContentType);
michael@0 446 }
michael@0 447
michael@0 448 rv = SaveDocumentInternal(aDocument, fileAsURI, datapathAsURI);
michael@0 449
michael@0 450 // Now save the URIs that have been gathered
michael@0 451
michael@0 452 if (NS_SUCCEEDED(rv) && datapathAsURI)
michael@0 453 {
michael@0 454 rv = SaveGatheredURIs(fileAsURI);
michael@0 455 }
michael@0 456 else if (mProgressListener)
michael@0 457 {
michael@0 458 // tell the listener we're done
michael@0 459 mProgressListener->OnStateChange(nullptr, nullptr,
michael@0 460 nsIWebProgressListener::STATE_START |
michael@0 461 nsIWebProgressListener::STATE_IS_NETWORK,
michael@0 462 NS_OK);
michael@0 463 mProgressListener->OnStateChange(nullptr, nullptr,
michael@0 464 nsIWebProgressListener::STATE_STOP |
michael@0 465 nsIWebProgressListener::STATE_IS_NETWORK,
michael@0 466 rv);
michael@0 467 }
michael@0 468
michael@0 469 return rv;
michael@0 470 }
michael@0 471
michael@0 472 /* void cancel(nsresult aReason); */
michael@0 473 NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
michael@0 474 {
michael@0 475 mCancel = true;
michael@0 476 EndDownload(aReason);
michael@0 477 return NS_OK;
michael@0 478 }
michael@0 479
michael@0 480
michael@0 481 /* void cancelSave(); */
michael@0 482 NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
michael@0 483 {
michael@0 484 return Cancel(NS_BINDING_ABORTED);
michael@0 485 }
michael@0 486
michael@0 487
michael@0 488 nsresult
michael@0 489 nsWebBrowserPersist::StartUpload(nsIStorageStream *storStream,
michael@0 490 nsIURI *aDestinationURI, const nsACString &aContentType)
michael@0 491 {
michael@0 492 // setup the upload channel if the destination is not local
michael@0 493 nsCOMPtr<nsIInputStream> inputstream;
michael@0 494 nsresult rv = storStream->NewInputStream(0, getter_AddRefs(inputstream));
michael@0 495 NS_ENSURE_TRUE(inputstream, NS_ERROR_FAILURE);
michael@0 496 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 497 return StartUpload(inputstream, aDestinationURI, aContentType);
michael@0 498 }
michael@0 499
michael@0 500 nsresult
michael@0 501 nsWebBrowserPersist::StartUpload(nsIInputStream *aInputStream,
michael@0 502 nsIURI *aDestinationURI, const nsACString &aContentType)
michael@0 503 {
michael@0 504 nsCOMPtr<nsIChannel> destChannel;
michael@0 505 CreateChannelFromURI(aDestinationURI, getter_AddRefs(destChannel));
michael@0 506 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(destChannel));
michael@0 507 NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
michael@0 508
michael@0 509 // Set the upload stream
michael@0 510 // NOTE: ALL data must be available in "inputstream"
michael@0 511 nsresult rv = uploadChannel->SetUploadStream(aInputStream, aContentType, -1);
michael@0 512 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 513 rv = destChannel->AsyncOpen(this, nullptr);
michael@0 514 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 515
michael@0 516 // add this to the upload list
michael@0 517 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
michael@0 518 mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
michael@0 519
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522
michael@0 523 nsresult
michael@0 524 nsWebBrowserPersist::SaveGatheredURIs(nsIURI *aFileAsURI)
michael@0 525 {
michael@0 526 nsresult rv = NS_OK;
michael@0 527
michael@0 528 // Count how many URIs in the URI map require persisting
michael@0 529 uint32_t urisToPersist = 0;
michael@0 530 if (mURIMap.Count() > 0)
michael@0 531 {
michael@0 532 mURIMap.EnumerateRead(EnumCountURIsToPersist, &urisToPersist);
michael@0 533 }
michael@0 534
michael@0 535 if (urisToPersist > 0)
michael@0 536 {
michael@0 537 // Persist each file in the uri map. The document(s)
michael@0 538 // will be saved after the last one of these is saved.
michael@0 539 mURIMap.EnumerateRead(EnumPersistURIs, this);
michael@0 540 }
michael@0 541
michael@0 542 // if we don't have anything in mOutputMap (added from above enumeration)
michael@0 543 // then we build the doc list (SaveDocuments)
michael@0 544 if (mOutputMap.Count() == 0)
michael@0 545 {
michael@0 546 // There are no URIs to save, so just save the document(s)
michael@0 547
michael@0 548 // State start notification
michael@0 549 uint32_t addToStateFlags = 0;
michael@0 550 if (mProgressListener)
michael@0 551 {
michael@0 552 if (mJustStartedLoading)
michael@0 553 {
michael@0 554 addToStateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
michael@0 555 }
michael@0 556 mProgressListener->OnStateChange(nullptr, nullptr,
michael@0 557 nsIWebProgressListener::STATE_START | addToStateFlags, NS_OK);
michael@0 558 }
michael@0 559
michael@0 560 rv = SaveDocuments();
michael@0 561 if (NS_FAILED(rv))
michael@0 562 EndDownload(rv);
michael@0 563 else if (aFileAsURI)
michael@0 564 {
michael@0 565 // local files won't trigger OnStopRequest so we call EndDownload here
michael@0 566 bool isFile = false;
michael@0 567 aFileAsURI->SchemeIs("file", &isFile);
michael@0 568 if (isFile)
michael@0 569 EndDownload(NS_OK);
michael@0 570 }
michael@0 571
michael@0 572 // State stop notification
michael@0 573 if (mProgressListener)
michael@0 574 {
michael@0 575 mProgressListener->OnStateChange(nullptr, nullptr,
michael@0 576 nsIWebProgressListener::STATE_STOP | addToStateFlags, rv);
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580 return rv;
michael@0 581 }
michael@0 582
michael@0 583 // this method returns true if there is another file to persist and false if not
michael@0 584 bool
michael@0 585 nsWebBrowserPersist::SerializeNextFile()
michael@0 586 {
michael@0 587 if (!mSerializingOutput)
michael@0 588 {
michael@0 589 return false;
michael@0 590 }
michael@0 591
michael@0 592 nsresult rv = SaveGatheredURIs(nullptr);
michael@0 593 if (NS_FAILED(rv))
michael@0 594 {
michael@0 595 return false;
michael@0 596 }
michael@0 597
michael@0 598 return (mURIMap.Count()
michael@0 599 || mUploadList.Count()
michael@0 600 || mDocList.Length()
michael@0 601 || mOutputMap.Count());
michael@0 602 }
michael@0 603
michael@0 604
michael@0 605 //*****************************************************************************
michael@0 606 // nsWebBrowserPersist::nsIRequestObserver
michael@0 607 //*****************************************************************************
michael@0 608
michael@0 609 NS_IMETHODIMP nsWebBrowserPersist::OnStartRequest(
michael@0 610 nsIRequest* request, nsISupports *ctxt)
michael@0 611 {
michael@0 612 if (mProgressListener)
michael@0 613 {
michael@0 614 uint32_t stateFlags = nsIWebProgressListener::STATE_START |
michael@0 615 nsIWebProgressListener::STATE_IS_REQUEST;
michael@0 616 if (mJustStartedLoading)
michael@0 617 {
michael@0 618 stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
michael@0 619 }
michael@0 620 mProgressListener->OnStateChange(nullptr, request, stateFlags, NS_OK);
michael@0 621 }
michael@0 622
michael@0 623 mJustStartedLoading = false;
michael@0 624
michael@0 625 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 626 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
michael@0 627
michael@0 628 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
michael@0 629 OutputData *data = mOutputMap.Get(keyPtr);
michael@0 630
michael@0 631 // NOTE: This code uses the channel as a hash key so it will not
michael@0 632 // recognize redirected channels because the key is not the same.
michael@0 633 // When that happens we remove and add the data entry to use the
michael@0 634 // new channel as the hash key.
michael@0 635 if (!data)
michael@0 636 {
michael@0 637 UploadData *upData = mUploadList.Get(keyPtr);
michael@0 638 if (!upData)
michael@0 639 {
michael@0 640 // Redirect? Try and fixup the output table
michael@0 641 nsresult rv = FixRedirectedChannelEntry(channel);
michael@0 642 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 643
michael@0 644 // Should be able to find the data after fixup unless redirects
michael@0 645 // are disabled.
michael@0 646 data = mOutputMap.Get(keyPtr);
michael@0 647 if (!data)
michael@0 648 {
michael@0 649 return NS_ERROR_FAILURE;
michael@0 650 }
michael@0 651 }
michael@0 652 }
michael@0 653
michael@0 654 if (data && data->mFile)
michael@0 655 {
michael@0 656 // If PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION is set in mPersistFlags,
michael@0 657 // try to determine whether this channel needs to apply Content-Encoding
michael@0 658 // conversions.
michael@0 659 NS_ASSERTION(!((mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION) &&
michael@0 660 (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)),
michael@0 661 "Conflict in persist flags: both AUTODETECT and NO_CONVERSION set");
michael@0 662 if (mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION)
michael@0 663 SetApplyConversionIfNeeded(channel);
michael@0 664
michael@0 665 if (data->mCalcFileExt && !(mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES))
michael@0 666 {
michael@0 667 // this is the first point at which the server can tell us the mimetype
michael@0 668 CalculateAndAppendFileExt(data->mFile, channel, data->mOriginalLocation);
michael@0 669
michael@0 670 // now make filename conformant and unique
michael@0 671 CalculateUniqueFilename(data->mFile);
michael@0 672 }
michael@0 673
michael@0 674 // compare uris and bail before we add to output map if they are equal
michael@0 675 bool isEqual = false;
michael@0 676 if (NS_SUCCEEDED(data->mFile->Equals(data->mOriginalLocation, &isEqual))
michael@0 677 && isEqual)
michael@0 678 {
michael@0 679 // remove from output map
michael@0 680 mOutputMap.Remove(keyPtr);
michael@0 681
michael@0 682 // cancel; we don't need to know any more
michael@0 683 // stop request will get called
michael@0 684 request->Cancel(NS_BINDING_ABORTED);
michael@0 685 }
michael@0 686 }
michael@0 687
michael@0 688 return NS_OK;
michael@0 689 }
michael@0 690
michael@0 691 NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest(
michael@0 692 nsIRequest* request, nsISupports *ctxt, nsresult status)
michael@0 693 {
michael@0 694 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
michael@0 695 OutputData *data = mOutputMap.Get(keyPtr);
michael@0 696 if (data)
michael@0 697 {
michael@0 698 if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(status))
michael@0 699 SendErrorStatusChange(true, status, request, data->mFile);
michael@0 700
michael@0 701 // This will automatically close the output stream
michael@0 702 mOutputMap.Remove(keyPtr);
michael@0 703 }
michael@0 704 else
michael@0 705 {
michael@0 706 // if we didn't find the data in mOutputMap, try mUploadList
michael@0 707 UploadData *upData = mUploadList.Get(keyPtr);
michael@0 708 if (upData)
michael@0 709 {
michael@0 710 mUploadList.Remove(keyPtr);
michael@0 711 }
michael@0 712 }
michael@0 713
michael@0 714 // ensure we call SaveDocuments if we:
michael@0 715 // 1) aren't canceling
michael@0 716 // 2) we haven't triggered the save (which we only want to trigger once)
michael@0 717 // 3) we aren't serializing (which will call it inside SerializeNextFile)
michael@0 718 if (mOutputMap.Count() == 0 && !mCancel && !mStartSaving && !mSerializingOutput)
michael@0 719 {
michael@0 720 nsresult rv = SaveDocuments();
michael@0 721 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 722 }
michael@0 723
michael@0 724 bool completed = false;
michael@0 725 if (mOutputMap.Count() == 0 && mUploadList.Count() == 0 && !mCancel)
michael@0 726 {
michael@0 727 // if no documents left in mDocList, --> done
michael@0 728 // if we have no files left to serialize and no error result, --> done
michael@0 729 if (mDocList.Length() == 0
michael@0 730 || (!SerializeNextFile() && NS_SUCCEEDED(mPersistResult)))
michael@0 731 {
michael@0 732 completed = true;
michael@0 733 }
michael@0 734 }
michael@0 735
michael@0 736 if (completed)
michael@0 737 {
michael@0 738 // we're all done, do our cleanup
michael@0 739 EndDownload(status);
michael@0 740 }
michael@0 741
michael@0 742 if (mProgressListener)
michael@0 743 {
michael@0 744 uint32_t stateFlags = nsIWebProgressListener::STATE_STOP |
michael@0 745 nsIWebProgressListener::STATE_IS_REQUEST;
michael@0 746 if (completed)
michael@0 747 {
michael@0 748 stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
michael@0 749 }
michael@0 750 mProgressListener->OnStateChange(nullptr, request, stateFlags, status);
michael@0 751 }
michael@0 752 if (completed)
michael@0 753 {
michael@0 754 mProgressListener = nullptr;
michael@0 755 mProgressListener2 = nullptr;
michael@0 756 mEventSink = nullptr;
michael@0 757 }
michael@0 758
michael@0 759 return NS_OK;
michael@0 760 }
michael@0 761
michael@0 762 //*****************************************************************************
michael@0 763 // nsWebBrowserPersist::nsIStreamListener
michael@0 764 //*****************************************************************************
michael@0 765
michael@0 766 NS_IMETHODIMP
michael@0 767 nsWebBrowserPersist::OnDataAvailable(
michael@0 768 nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream,
michael@0 769 uint64_t aOffset, uint32_t aLength)
michael@0 770 {
michael@0 771 bool cancel = mCancel;
michael@0 772 if (!cancel)
michael@0 773 {
michael@0 774 nsresult rv = NS_OK;
michael@0 775 uint32_t bytesRemaining = aLength;
michael@0 776
michael@0 777 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 778 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
michael@0 779
michael@0 780 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
michael@0 781 OutputData *data = mOutputMap.Get(keyPtr);
michael@0 782 if (!data) {
michael@0 783 // might be uploadData; consume necko's buffer and bail...
michael@0 784 uint32_t n;
michael@0 785 return aIStream->ReadSegments(NS_DiscardSegment, nullptr, aLength, &n);
michael@0 786 }
michael@0 787
michael@0 788 bool readError = true;
michael@0 789
michael@0 790 // Make the output stream
michael@0 791 if (!data->mStream)
michael@0 792 {
michael@0 793 rv = MakeOutputStream(data->mFile, getter_AddRefs(data->mStream));
michael@0 794 if (NS_FAILED(rv))
michael@0 795 {
michael@0 796 readError = false;
michael@0 797 cancel = true;
michael@0 798 }
michael@0 799 }
michael@0 800
michael@0 801 // Read data from the input and write to the output
michael@0 802 char buffer[8192];
michael@0 803 uint32_t bytesRead;
michael@0 804 while (!cancel && bytesRemaining)
michael@0 805 {
michael@0 806 readError = true;
michael@0 807 rv = aIStream->Read(buffer,
michael@0 808 std::min(uint32_t(sizeof(buffer)), bytesRemaining),
michael@0 809 &bytesRead);
michael@0 810 if (NS_SUCCEEDED(rv))
michael@0 811 {
michael@0 812 readError = false;
michael@0 813 // Write out the data until something goes wrong, or, it is
michael@0 814 // all written. We loop because for some errors (e.g., disk
michael@0 815 // full), we get NS_OK with some bytes written, then an error.
michael@0 816 // So, we want to write again in that case to get the actual
michael@0 817 // error code.
michael@0 818 const char *bufPtr = buffer; // Where to write from.
michael@0 819 while (NS_SUCCEEDED(rv) && bytesRead)
michael@0 820 {
michael@0 821 uint32_t bytesWritten = 0;
michael@0 822 rv = data->mStream->Write(bufPtr, bytesRead, &bytesWritten);
michael@0 823 if (NS_SUCCEEDED(rv))
michael@0 824 {
michael@0 825 bytesRead -= bytesWritten;
michael@0 826 bufPtr += bytesWritten;
michael@0 827 bytesRemaining -= bytesWritten;
michael@0 828 // Force an error if (for some reason) we get NS_OK but
michael@0 829 // no bytes written.
michael@0 830 if (!bytesWritten)
michael@0 831 {
michael@0 832 rv = NS_ERROR_FAILURE;
michael@0 833 cancel = true;
michael@0 834 }
michael@0 835 }
michael@0 836 else
michael@0 837 {
michael@0 838 // Disaster - can't write out the bytes - disk full / permission?
michael@0 839 cancel = true;
michael@0 840 }
michael@0 841 }
michael@0 842 }
michael@0 843 else
michael@0 844 {
michael@0 845 // Disaster - can't read the bytes - broken link / file error?
michael@0 846 cancel = true;
michael@0 847 }
michael@0 848 }
michael@0 849
michael@0 850 int64_t channelContentLength = -1;
michael@0 851 if (!cancel &&
michael@0 852 NS_SUCCEEDED(channel->GetContentLength(&channelContentLength)))
michael@0 853 {
michael@0 854 // if we get -1 at this point, we didn't get content-length header
michael@0 855 // assume that we got all of the data and push what we have;
michael@0 856 // that's the best we can do now
michael@0 857 if ((-1 == channelContentLength) ||
michael@0 858 ((channelContentLength - (aOffset + aLength)) == 0))
michael@0 859 {
michael@0 860 NS_WARN_IF_FALSE(channelContentLength != -1,
michael@0 861 "nsWebBrowserPersist::OnDataAvailable() no content length "
michael@0 862 "header, pushing what we have");
michael@0 863 // we're done with this pass; see if we need to do upload
michael@0 864 nsAutoCString contentType;
michael@0 865 channel->GetContentType(contentType);
michael@0 866 // if we don't have the right type of output stream then it's a local file
michael@0 867 nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(data->mStream));
michael@0 868 if (storStream)
michael@0 869 {
michael@0 870 data->mStream->Close();
michael@0 871 data->mStream = nullptr; // null out stream so we don't close it later
michael@0 872 rv = StartUpload(storStream, data->mFile, contentType);
michael@0 873 if (NS_FAILED(rv))
michael@0 874 {
michael@0 875 readError = false;
michael@0 876 cancel = true;
michael@0 877 }
michael@0 878 }
michael@0 879 }
michael@0 880 }
michael@0 881
michael@0 882 // Notify listener if an error occurred.
michael@0 883 if (cancel)
michael@0 884 {
michael@0 885 SendErrorStatusChange(readError, rv,
michael@0 886 readError ? request : nullptr, data->mFile);
michael@0 887 }
michael@0 888 }
michael@0 889
michael@0 890 // Cancel reading?
michael@0 891 if (cancel)
michael@0 892 {
michael@0 893 EndDownload(NS_BINDING_ABORTED);
michael@0 894 }
michael@0 895
michael@0 896 return NS_OK;
michael@0 897 }
michael@0 898
michael@0 899
michael@0 900 //*****************************************************************************
michael@0 901 // nsWebBrowserPersist::nsIProgressEventSink
michael@0 902 //*****************************************************************************
michael@0 903
michael@0 904 /* void onProgress (in nsIRequest request, in nsISupports ctxt,
michael@0 905 in unsigned long long aProgress, in unsigned long long aProgressMax); */
michael@0 906 NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
michael@0 907 nsIRequest *request, nsISupports *ctxt, uint64_t aProgress,
michael@0 908 uint64_t aProgressMax)
michael@0 909 {
michael@0 910 if (!mProgressListener)
michael@0 911 {
michael@0 912 return NS_OK;
michael@0 913 }
michael@0 914
michael@0 915 // Store the progress of this request
michael@0 916 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
michael@0 917 OutputData *data = mOutputMap.Get(keyPtr);
michael@0 918 if (data)
michael@0 919 {
michael@0 920 data->mSelfProgress = int64_t(aProgress);
michael@0 921 data->mSelfProgressMax = int64_t(aProgressMax);
michael@0 922 }
michael@0 923 else
michael@0 924 {
michael@0 925 UploadData *upData = mUploadList.Get(keyPtr);
michael@0 926 if (upData)
michael@0 927 {
michael@0 928 upData->mSelfProgress = int64_t(aProgress);
michael@0 929 upData->mSelfProgressMax = int64_t(aProgressMax);
michael@0 930 }
michael@0 931 }
michael@0 932
michael@0 933 // Notify listener of total progress
michael@0 934 CalcTotalProgress();
michael@0 935 if (mProgressListener2)
michael@0 936 {
michael@0 937 mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
michael@0 938 aProgressMax, mTotalCurrentProgress, mTotalMaxProgress);
michael@0 939 }
michael@0 940 else
michael@0 941 {
michael@0 942 // have to truncate 64-bit to 32bit
michael@0 943 mProgressListener->OnProgressChange(nullptr, request, uint64_t(aProgress),
michael@0 944 uint64_t(aProgressMax), mTotalCurrentProgress, mTotalMaxProgress);
michael@0 945 }
michael@0 946
michael@0 947 // If our progress listener implements nsIProgressEventSink,
michael@0 948 // forward the notification
michael@0 949 if (mEventSink)
michael@0 950 {
michael@0 951 mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
michael@0 952 }
michael@0 953
michael@0 954 return NS_OK;
michael@0 955 }
michael@0 956
michael@0 957 /* void onStatus (in nsIRequest request, in nsISupports ctxt,
michael@0 958 in nsresult status, in wstring statusArg); */
michael@0 959 NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
michael@0 960 nsIRequest *request, nsISupports *ctxt, nsresult status,
michael@0 961 const char16_t *statusArg)
michael@0 962 {
michael@0 963 if (mProgressListener)
michael@0 964 {
michael@0 965 // We need to filter out non-error error codes.
michael@0 966 // Is the only NS_SUCCEEDED value NS_OK?
michael@0 967 switch ( status )
michael@0 968 {
michael@0 969 case NS_NET_STATUS_RESOLVING_HOST:
michael@0 970 case NS_NET_STATUS_RESOLVED_HOST:
michael@0 971 case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
michael@0 972 case NS_NET_STATUS_END_FTP_TRANSACTION:
michael@0 973 case NS_NET_STATUS_CONNECTING_TO:
michael@0 974 case NS_NET_STATUS_CONNECTED_TO:
michael@0 975 case NS_NET_STATUS_SENDING_TO:
michael@0 976 case NS_NET_STATUS_RECEIVING_FROM:
michael@0 977 case NS_NET_STATUS_WAITING_FOR:
michael@0 978 case NS_NET_STATUS_READING:
michael@0 979 case NS_NET_STATUS_WRITING:
michael@0 980 break;
michael@0 981
michael@0 982 default:
michael@0 983 // Pass other notifications (for legitimate errors) along.
michael@0 984 mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
michael@0 985 break;
michael@0 986 }
michael@0 987
michael@0 988 }
michael@0 989
michael@0 990 // If our progress listener implements nsIProgressEventSink,
michael@0 991 // forward the notification
michael@0 992 if (mEventSink)
michael@0 993 {
michael@0 994 mEventSink->OnStatus(request, ctxt, status, statusArg);
michael@0 995 }
michael@0 996
michael@0 997 return NS_OK;
michael@0 998 }
michael@0 999
michael@0 1000
michael@0 1001 //*****************************************************************************
michael@0 1002 // nsWebBrowserPersist private methods
michael@0 1003 //*****************************************************************************
michael@0 1004
michael@0 1005 // Convert error info into proper message text and send OnStatusChange notification
michael@0 1006 // to the web progress listener.
michael@0 1007 nsresult nsWebBrowserPersist::SendErrorStatusChange(
michael@0 1008 bool aIsReadError, nsresult aResult, nsIRequest *aRequest, nsIURI *aURI)
michael@0 1009 {
michael@0 1010 NS_ENSURE_ARG_POINTER(aURI);
michael@0 1011
michael@0 1012 if (!mProgressListener)
michael@0 1013 {
michael@0 1014 // Do nothing
michael@0 1015 return NS_OK;
michael@0 1016 }
michael@0 1017
michael@0 1018 // Get the file path or spec from the supplied URI
michael@0 1019 nsCOMPtr<nsIFile> file;
michael@0 1020 GetLocalFileFromURI(aURI, getter_AddRefs(file));
michael@0 1021 nsAutoString path;
michael@0 1022 if (file)
michael@0 1023 {
michael@0 1024 file->GetPath(path);
michael@0 1025 }
michael@0 1026 else
michael@0 1027 {
michael@0 1028 nsAutoCString fileurl;
michael@0 1029 aURI->GetSpec(fileurl);
michael@0 1030 AppendUTF8toUTF16(fileurl, path);
michael@0 1031 }
michael@0 1032
michael@0 1033 nsAutoString msgId;
michael@0 1034 switch(aResult)
michael@0 1035 {
michael@0 1036 case NS_ERROR_FILE_NAME_TOO_LONG:
michael@0 1037 // File name too long.
michael@0 1038 msgId.AssignLiteral("fileNameTooLongError");
michael@0 1039 break;
michael@0 1040 case NS_ERROR_FILE_ALREADY_EXISTS:
michael@0 1041 // File exists with same name as directory.
michael@0 1042 msgId.AssignLiteral("fileAlreadyExistsError");
michael@0 1043 break;
michael@0 1044 case NS_ERROR_FILE_DISK_FULL:
michael@0 1045 case NS_ERROR_FILE_NO_DEVICE_SPACE:
michael@0 1046 // Out of space on target volume.
michael@0 1047 msgId.AssignLiteral("diskFull");
michael@0 1048 break;
michael@0 1049
michael@0 1050 case NS_ERROR_FILE_READ_ONLY:
michael@0 1051 // Attempt to write to read/only file.
michael@0 1052 msgId.AssignLiteral("readOnly");
michael@0 1053 break;
michael@0 1054
michael@0 1055 case NS_ERROR_FILE_ACCESS_DENIED:
michael@0 1056 // Attempt to write without sufficient permissions.
michael@0 1057 msgId.AssignLiteral("accessError");
michael@0 1058 break;
michael@0 1059
michael@0 1060 default:
michael@0 1061 // Generic read/write error message.
michael@0 1062 if (aIsReadError)
michael@0 1063 msgId.AssignLiteral("readError");
michael@0 1064 else
michael@0 1065 msgId.AssignLiteral("writeError");
michael@0 1066 break;
michael@0 1067 }
michael@0 1068 // Get properties file bundle and extract status string.
michael@0 1069 nsresult rv;
michael@0 1070 nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
michael@0 1071 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
michael@0 1072
michael@0 1073 nsCOMPtr<nsIStringBundle> bundle;
michael@0 1074 rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
michael@0 1075 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
michael@0 1076
michael@0 1077 nsXPIDLString msgText;
michael@0 1078 const char16_t *strings[1];
michael@0 1079 strings[0] = path.get();
michael@0 1080 rv = bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText));
michael@0 1081 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1082
michael@0 1083 mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
michael@0 1084
michael@0 1085 return NS_OK;
michael@0 1086 }
michael@0 1087
michael@0 1088 nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
michael@0 1089 {
michael@0 1090 NS_ENSURE_ARG_POINTER(aObject);
michael@0 1091 NS_ENSURE_ARG_POINTER(aURI);
michael@0 1092
michael@0 1093 nsCOMPtr<nsIFile> objAsFile = do_QueryInterface(aObject);
michael@0 1094 if (objAsFile)
michael@0 1095 {
michael@0 1096 return NS_NewFileURI(aURI, objAsFile);
michael@0 1097 }
michael@0 1098 nsCOMPtr<nsIURI> objAsURI = do_QueryInterface(aObject);
michael@0 1099 if (objAsURI)
michael@0 1100 {
michael@0 1101 *aURI = objAsURI;
michael@0 1102 NS_ADDREF(*aURI);
michael@0 1103 return NS_OK;
michael@0 1104 }
michael@0 1105
michael@0 1106 return NS_ERROR_FAILURE;
michael@0 1107 }
michael@0 1108
michael@0 1109 nsresult nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const
michael@0 1110 {
michael@0 1111 nsresult rv;
michael@0 1112
michael@0 1113 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
michael@0 1114 if (NS_FAILED(rv))
michael@0 1115 return rv;
michael@0 1116
michael@0 1117 nsCOMPtr<nsIFile> file;
michael@0 1118 rv = fileURL->GetFile(getter_AddRefs(file));
michael@0 1119 if (NS_FAILED(rv)) {
michael@0 1120 return rv;
michael@0 1121 }
michael@0 1122
michael@0 1123 file.forget(aLocalFile);
michael@0 1124 return NS_OK;
michael@0 1125 }
michael@0 1126
michael@0 1127 nsresult nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath) const
michael@0 1128 {
michael@0 1129 NS_ENSURE_ARG_POINTER(aURI);
michael@0 1130
michael@0 1131 nsAutoCString newPath;
michael@0 1132 nsresult rv = aURI->GetPath(newPath);
michael@0 1133 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1134
michael@0 1135 // Append a forward slash if necessary
michael@0 1136 int32_t len = newPath.Length();
michael@0 1137 if (len > 0 && newPath.CharAt(len - 1) != '/')
michael@0 1138 {
michael@0 1139 newPath.Append('/');
michael@0 1140 }
michael@0 1141
michael@0 1142 // Store the path back on the URI
michael@0 1143 AppendUTF16toUTF8(aPath, newPath);
michael@0 1144 aURI->SetPath(newPath);
michael@0 1145
michael@0 1146 return NS_OK;
michael@0 1147 }
michael@0 1148
michael@0 1149 nsresult nsWebBrowserPersist::SaveURIInternal(
michael@0 1150 nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
michael@0 1151 nsIInputStream *aPostData, const char *aExtraHeaders,
michael@0 1152 nsIURI *aFile, bool aCalcFileExt, bool aIsPrivate)
michael@0 1153 {
michael@0 1154 NS_ENSURE_ARG_POINTER(aURI);
michael@0 1155 NS_ENSURE_ARG_POINTER(aFile);
michael@0 1156
michael@0 1157 nsresult rv = NS_OK;
michael@0 1158
michael@0 1159 mURI = aURI;
michael@0 1160
michael@0 1161 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
michael@0 1162 if (mPersistFlags & PERSIST_FLAGS_BYPASS_CACHE)
michael@0 1163 {
michael@0 1164 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
michael@0 1165 }
michael@0 1166 else if (mPersistFlags & PERSIST_FLAGS_FROM_CACHE)
michael@0 1167 {
michael@0 1168 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
michael@0 1169 }
michael@0 1170
michael@0 1171 // Extract the cache key
michael@0 1172 nsCOMPtr<nsISupports> cacheKey;
michael@0 1173 if (aCacheKey)
michael@0 1174 {
michael@0 1175 // Test if the cache key is actually a web page descriptor (docshell)
michael@0 1176 // or session history entry.
michael@0 1177 nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(aCacheKey);
michael@0 1178 if (!shEntry)
michael@0 1179 {
michael@0 1180 nsCOMPtr<nsIWebPageDescriptor> webPageDescriptor =
michael@0 1181 do_QueryInterface(aCacheKey);
michael@0 1182 if (webPageDescriptor)
michael@0 1183 {
michael@0 1184 nsCOMPtr<nsISupports> currentDescriptor;
michael@0 1185 webPageDescriptor->GetCurrentDescriptor(getter_AddRefs(currentDescriptor));
michael@0 1186 shEntry = do_QueryInterface(currentDescriptor);
michael@0 1187 }
michael@0 1188 }
michael@0 1189
michael@0 1190 if (shEntry)
michael@0 1191 {
michael@0 1192 shEntry->GetCacheKey(getter_AddRefs(cacheKey));
michael@0 1193 }
michael@0 1194 else
michael@0 1195 {
michael@0 1196 // Assume a plain cache key
michael@0 1197 cacheKey = aCacheKey;
michael@0 1198 }
michael@0 1199 }
michael@0 1200
michael@0 1201 // Open a channel to the URI
michael@0 1202 nsCOMPtr<nsIChannel> inputChannel;
michael@0 1203 rv = NS_NewChannel(getter_AddRefs(inputChannel), aURI,
michael@0 1204 nullptr, nullptr, static_cast<nsIInterfaceRequestor*>(this),
michael@0 1205 loadFlags);
michael@0 1206
michael@0 1207 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
michael@0 1208 if (pbChannel)
michael@0 1209 {
michael@0 1210 pbChannel->SetPrivate(aIsPrivate);
michael@0 1211 }
michael@0 1212
michael@0 1213 if (NS_FAILED(rv) || inputChannel == nullptr)
michael@0 1214 {
michael@0 1215 EndDownload(NS_ERROR_FAILURE);
michael@0 1216 return NS_ERROR_FAILURE;
michael@0 1217 }
michael@0 1218
michael@0 1219 // Disable content conversion
michael@0 1220 if (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)
michael@0 1221 {
michael@0 1222 nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
michael@0 1223 if (encodedChannel)
michael@0 1224 {
michael@0 1225 encodedChannel->SetApplyConversion(false);
michael@0 1226 }
michael@0 1227 }
michael@0 1228
michael@0 1229 if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
michael@0 1230 {
michael@0 1231 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
michael@0 1232 do_QueryInterface(inputChannel);
michael@0 1233 if (httpChannelInternal)
michael@0 1234 httpChannelInternal->SetForceAllowThirdPartyCookie(true);
michael@0 1235 }
michael@0 1236
michael@0 1237 // Set the referrer, post data and headers if any
michael@0 1238 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
michael@0 1239 if (httpChannel)
michael@0 1240 {
michael@0 1241 // Referrer
michael@0 1242 if (aReferrer)
michael@0 1243 {
michael@0 1244 httpChannel->SetReferrer(aReferrer);
michael@0 1245 }
michael@0 1246
michael@0 1247 // Post data
michael@0 1248 if (aPostData)
michael@0 1249 {
michael@0 1250 nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
michael@0 1251 if (stream)
michael@0 1252 {
michael@0 1253 // Rewind the postdata stream
michael@0 1254 stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 1255 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
michael@0 1256 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
michael@0 1257 // Attach the postdata to the http channel
michael@0 1258 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
michael@0 1259 }
michael@0 1260 }
michael@0 1261
michael@0 1262 // Cache key
michael@0 1263 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
michael@0 1264 if (cacheChannel && cacheKey)
michael@0 1265 {
michael@0 1266 cacheChannel->SetCacheKey(cacheKey);
michael@0 1267 }
michael@0 1268
michael@0 1269 // Headers
michael@0 1270 if (aExtraHeaders)
michael@0 1271 {
michael@0 1272 nsAutoCString oneHeader;
michael@0 1273 nsAutoCString headerName;
michael@0 1274 nsAutoCString headerValue;
michael@0 1275 int32_t crlf = 0;
michael@0 1276 int32_t colon = 0;
michael@0 1277 const char *kWhitespace = "\b\t\r\n ";
michael@0 1278 nsAutoCString extraHeaders(aExtraHeaders);
michael@0 1279 while (true)
michael@0 1280 {
michael@0 1281 crlf = extraHeaders.Find("\r\n", true);
michael@0 1282 if (crlf == -1)
michael@0 1283 break;
michael@0 1284 extraHeaders.Mid(oneHeader, 0, crlf);
michael@0 1285 extraHeaders.Cut(0, crlf + 2);
michael@0 1286 colon = oneHeader.Find(":");
michael@0 1287 if (colon == -1)
michael@0 1288 break; // Should have a colon
michael@0 1289 oneHeader.Left(headerName, colon);
michael@0 1290 colon++;
michael@0 1291 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
michael@0 1292 headerName.Trim(kWhitespace);
michael@0 1293 headerValue.Trim(kWhitespace);
michael@0 1294 // Add the header (merging if required)
michael@0 1295 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
michael@0 1296 if (NS_FAILED(rv))
michael@0 1297 {
michael@0 1298 EndDownload(NS_ERROR_FAILURE);
michael@0 1299 return NS_ERROR_FAILURE;
michael@0 1300 }
michael@0 1301 }
michael@0 1302 }
michael@0 1303 }
michael@0 1304 return SaveChannelInternal(inputChannel, aFile, aCalcFileExt);
michael@0 1305 }
michael@0 1306
michael@0 1307 nsresult nsWebBrowserPersist::SaveChannelInternal(
michael@0 1308 nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
michael@0 1309 {
michael@0 1310 NS_ENSURE_ARG_POINTER(aChannel);
michael@0 1311 NS_ENSURE_ARG_POINTER(aFile);
michael@0 1312
michael@0 1313 // The default behaviour of SaveChannelInternal is to download the source
michael@0 1314 // into a storage stream and upload that to the target. MakeOutputStream
michael@0 1315 // special-cases a file target and creates a file output stream directly.
michael@0 1316 // We want to special-case a file source and create a file input stream,
michael@0 1317 // but we don't need to do this in the case of a file target.
michael@0 1318 nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
michael@0 1319 nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
michael@0 1320 if (fc && !fu) {
michael@0 1321 nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
michael@0 1322 nsresult rv = aChannel->Open(getter_AddRefs(fileInputStream));
michael@0 1323 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1324 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
michael@0 1325 fileInputStream, BUFFERED_OUTPUT_SIZE);
michael@0 1326 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1327 nsAutoCString contentType;
michael@0 1328 aChannel->GetContentType(contentType);
michael@0 1329 return StartUpload(bufferedInputStream, aFile, contentType);
michael@0 1330 }
michael@0 1331
michael@0 1332 // Read from the input channel
michael@0 1333 nsresult rv = aChannel->AsyncOpen(this, nullptr);
michael@0 1334 if (rv == NS_ERROR_NO_CONTENT)
michael@0 1335 {
michael@0 1336 // Assume this is a protocol such as mailto: which does not feed out
michael@0 1337 // data and just ignore it.
michael@0 1338 return NS_SUCCESS_DONT_FIXUP;
michael@0 1339 }
michael@0 1340
michael@0 1341 if (NS_FAILED(rv))
michael@0 1342 {
michael@0 1343 // Opening failed, but do we care?
michael@0 1344 if (mPersistFlags & PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS)
michael@0 1345 {
michael@0 1346 SendErrorStatusChange(true, rv, aChannel, aFile);
michael@0 1347 EndDownload(NS_ERROR_FAILURE);
michael@0 1348 return NS_ERROR_FAILURE;
michael@0 1349 }
michael@0 1350 return NS_SUCCESS_DONT_FIXUP;
michael@0 1351 }
michael@0 1352
michael@0 1353 // Add the output transport to the output map with the channel as the key
michael@0 1354 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aChannel);
michael@0 1355 mOutputMap.Put(keyPtr, new OutputData(aFile, mURI, aCalcFileExt));
michael@0 1356
michael@0 1357 return NS_OK;
michael@0 1358 }
michael@0 1359
michael@0 1360 nsresult
michael@0 1361 nsWebBrowserPersist::GetExtensionForContentType(const char16_t *aContentType, char16_t **aExt)
michael@0 1362 {
michael@0 1363 NS_ENSURE_ARG_POINTER(aContentType);
michael@0 1364 NS_ENSURE_ARG_POINTER(aExt);
michael@0 1365
michael@0 1366 *aExt = nullptr;
michael@0 1367
michael@0 1368 nsresult rv;
michael@0 1369 if (!mMIMEService)
michael@0 1370 {
michael@0 1371 mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
michael@0 1372 NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
michael@0 1373 }
michael@0 1374
michael@0 1375 nsCOMPtr<nsIMIMEInfo> mimeInfo;
michael@0 1376 nsAutoCString contentType;
michael@0 1377 contentType.AssignWithConversion(aContentType);
michael@0 1378 nsAutoCString ext;
michael@0 1379 rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
michael@0 1380 if (NS_SUCCEEDED(rv))
michael@0 1381 {
michael@0 1382 *aExt = UTF8ToNewUnicode(ext);
michael@0 1383 NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
michael@0 1384 return NS_OK;
michael@0 1385 }
michael@0 1386
michael@0 1387 return NS_ERROR_FAILURE;
michael@0 1388 }
michael@0 1389
michael@0 1390 nsresult
michael@0 1391 nsWebBrowserPersist::GetDocumentExtension(nsIDOMDocument *aDocument, char16_t **aExt)
michael@0 1392 {
michael@0 1393 NS_ENSURE_ARG_POINTER(aDocument);
michael@0 1394 NS_ENSURE_ARG_POINTER(aExt);
michael@0 1395
michael@0 1396 nsXPIDLString contentType;
michael@0 1397 nsresult rv = GetDocEncoderContentType(aDocument, nullptr, getter_Copies(contentType));
michael@0 1398 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1399 return GetExtensionForContentType(contentType.get(), aExt);
michael@0 1400 }
michael@0 1401
michael@0 1402 nsresult
michael@0 1403 nsWebBrowserPersist::GetDocEncoderContentType(nsIDOMDocument *aDocument, const char16_t *aContentType, char16_t **aRealContentType)
michael@0 1404 {
michael@0 1405 NS_ENSURE_ARG_POINTER(aDocument);
michael@0 1406 NS_ENSURE_ARG_POINTER(aRealContentType);
michael@0 1407
michael@0 1408 *aRealContentType = nullptr;
michael@0 1409
michael@0 1410 nsAutoString defaultContentType(NS_LITERAL_STRING("text/html"));
michael@0 1411
michael@0 1412 // Get the desired content type for the document, either by using the one
michael@0 1413 // supplied or from the document itself.
michael@0 1414
michael@0 1415 nsAutoString contentType;
michael@0 1416 if (aContentType)
michael@0 1417 {
michael@0 1418 contentType.Assign(aContentType);
michael@0 1419 }
michael@0 1420 else
michael@0 1421 {
michael@0 1422 // Get the content type from the document
michael@0 1423 nsAutoString type;
michael@0 1424 if (NS_SUCCEEDED(aDocument->GetContentType(type)) && !type.IsEmpty())
michael@0 1425 contentType.Assign(type);
michael@0 1426 }
michael@0 1427
michael@0 1428 // Check that an encoder actually exists for the desired output type. The
michael@0 1429 // following content types will usually yield an encoder.
michael@0 1430 //
michael@0 1431 // text/xml
michael@0 1432 // application/xml
michael@0 1433 // application/xhtml+xml
michael@0 1434 // image/svg+xml
michael@0 1435 // text/html
michael@0 1436 // text/plain
michael@0 1437
michael@0 1438 if (!contentType.IsEmpty() &&
michael@0 1439 !contentType.Equals(defaultContentType, nsCaseInsensitiveStringComparator()))
michael@0 1440 {
michael@0 1441 // Check if there is an encoder for the desired content type
michael@0 1442 nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
michael@0 1443 AppendUTF16toUTF8(contentType, contractID);
michael@0 1444
michael@0 1445 nsCOMPtr<nsIComponentRegistrar> registrar;
michael@0 1446 NS_GetComponentRegistrar(getter_AddRefs(registrar));
michael@0 1447 if (registrar)
michael@0 1448 {
michael@0 1449 bool result;
michael@0 1450 nsresult rv = registrar->IsContractIDRegistered(contractID.get(), &result);
michael@0 1451 if (NS_SUCCEEDED(rv) && result)
michael@0 1452 {
michael@0 1453 *aRealContentType = ToNewUnicode(contentType);
michael@0 1454 }
michael@0 1455 }
michael@0 1456 }
michael@0 1457
michael@0 1458 // Use the default if no encoder exists for the desired one
michael@0 1459 if (!*aRealContentType)
michael@0 1460 {
michael@0 1461 *aRealContentType = ToNewUnicode(defaultContentType);
michael@0 1462 }
michael@0 1463
michael@0 1464 NS_ENSURE_TRUE(*aRealContentType, NS_ERROR_OUT_OF_MEMORY);
michael@0 1465
michael@0 1466 return NS_OK;
michael@0 1467 }
michael@0 1468
michael@0 1469 nsresult nsWebBrowserPersist::SaveDocumentInternal(
michael@0 1470 nsIDOMDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
michael@0 1471 {
michael@0 1472 NS_ENSURE_ARG_POINTER(aDocument);
michael@0 1473 NS_ENSURE_ARG_POINTER(aFile);
michael@0 1474
michael@0 1475 // See if we can get the local file representation of this URI
michael@0 1476 nsCOMPtr<nsIFile> localFile;
michael@0 1477 nsresult rv = GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
michael@0 1478
michael@0 1479 nsCOMPtr<nsIFile> localDataPath;
michael@0 1480 if (NS_SUCCEEDED(rv) && aDataPath)
michael@0 1481 {
michael@0 1482 // See if we can get the local file representation of this URI
michael@0 1483 rv = GetLocalFileFromURI(aDataPath, getter_AddRefs(localDataPath));
michael@0 1484 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1485 }
michael@0 1486
michael@0 1487 nsCOMPtr<nsIDOMNode> docAsNode = do_QueryInterface(aDocument);
michael@0 1488
michael@0 1489 // Persist the main document
michael@0 1490 nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDocument));
michael@0 1491 if (!doc) {
michael@0 1492 return NS_ERROR_UNEXPECTED;
michael@0 1493 }
michael@0 1494 mURI = doc->GetDocumentURI();
michael@0 1495
michael@0 1496 nsCOMPtr<nsIURI> oldBaseURI = mCurrentBaseURI;
michael@0 1497 nsAutoCString oldCharset(mCurrentCharset);
michael@0 1498
michael@0 1499 // Store the base URI and the charset
michael@0 1500 mCurrentBaseURI = doc->GetBaseURI();
michael@0 1501 mCurrentCharset = doc->GetDocumentCharacterSet();
michael@0 1502
michael@0 1503 // Does the caller want to fixup the referenced URIs and save those too?
michael@0 1504 if (aDataPath)
michael@0 1505 {
michael@0 1506 // Basic steps are these.
michael@0 1507 //
michael@0 1508 // 1. Iterate through the document (and subdocuments) building a list
michael@0 1509 // of unique URIs.
michael@0 1510 // 2. For each URI create an OutputData entry and open a channel to save
michael@0 1511 // it. As each URI is saved, discover the mime type and fix up the
michael@0 1512 // local filename with the correct extension.
michael@0 1513 // 3. Store the document in a list and wait for URI persistence to finish
michael@0 1514 // 4. After URI persistence completes save the list of documents,
michael@0 1515 // fixing it up as it goes out to file.
michael@0 1516
michael@0 1517 nsCOMPtr<nsIURI> oldDataPath = mCurrentDataPath;
michael@0 1518 bool oldDataPathIsRelative = mCurrentDataPathIsRelative;
michael@0 1519 nsCString oldCurrentRelativePathToData = mCurrentRelativePathToData;
michael@0 1520 uint32_t oldThingsToPersist = mCurrentThingsToPersist;
michael@0 1521
michael@0 1522 mCurrentDataPathIsRelative = false;
michael@0 1523 mCurrentDataPath = aDataPath;
michael@0 1524 mCurrentRelativePathToData = "";
michael@0 1525 mCurrentThingsToPersist = 0;
michael@0 1526
michael@0 1527 // Determine if the specified data path is relative to the
michael@0 1528 // specified file, (e.g. c:\docs\htmldata is relative to
michael@0 1529 // c:\docs\myfile.htm, but not to d:\foo\data.
michael@0 1530
michael@0 1531 // Starting with the data dir work back through its parents
michael@0 1532 // checking if one of them matches the base directory.
michael@0 1533
michael@0 1534 if (localDataPath && localFile)
michael@0 1535 {
michael@0 1536 nsCOMPtr<nsIFile> baseDir;
michael@0 1537 localFile->GetParent(getter_AddRefs(baseDir));
michael@0 1538
michael@0 1539 nsAutoCString relativePathToData;
michael@0 1540 nsCOMPtr<nsIFile> dataDirParent;
michael@0 1541 dataDirParent = localDataPath;
michael@0 1542 while (dataDirParent)
michael@0 1543 {
michael@0 1544 bool sameDir = false;
michael@0 1545 dataDirParent->Equals(baseDir, &sameDir);
michael@0 1546 if (sameDir)
michael@0 1547 {
michael@0 1548 mCurrentRelativePathToData = relativePathToData;
michael@0 1549 mCurrentDataPathIsRelative = true;
michael@0 1550 break;
michael@0 1551 }
michael@0 1552
michael@0 1553 nsAutoString dirName;
michael@0 1554 dataDirParent->GetLeafName(dirName);
michael@0 1555
michael@0 1556 nsAutoCString newRelativePathToData;
michael@0 1557 newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
michael@0 1558 + NS_LITERAL_CSTRING("/")
michael@0 1559 + relativePathToData;
michael@0 1560 relativePathToData = newRelativePathToData;
michael@0 1561
michael@0 1562 nsCOMPtr<nsIFile> newDataDirParent;
michael@0 1563 rv = dataDirParent->GetParent(getter_AddRefs(newDataDirParent));
michael@0 1564 dataDirParent = newDataDirParent;
michael@0 1565 }
michael@0 1566 }
michael@0 1567 else
michael@0 1568 {
michael@0 1569 // generate a relative path if possible
michael@0 1570 nsCOMPtr<nsIURL> pathToBaseURL(do_QueryInterface(aFile));
michael@0 1571 if (pathToBaseURL)
michael@0 1572 {
michael@0 1573 nsAutoCString relativePath; // nsACString
michael@0 1574 if (NS_SUCCEEDED(pathToBaseURL->GetRelativeSpec(aDataPath, relativePath)))
michael@0 1575 {
michael@0 1576 mCurrentDataPathIsRelative = true;
michael@0 1577 mCurrentRelativePathToData = relativePath;
michael@0 1578 }
michael@0 1579 }
michael@0 1580 }
michael@0 1581
michael@0 1582 // Store the document in a list so when URI persistence is done and the
michael@0 1583 // filenames of saved URIs are known, the documents can be fixed up and
michael@0 1584 // saved
michael@0 1585
michael@0 1586 DocData *docData = new DocData;
michael@0 1587 docData->mBaseURI = mCurrentBaseURI;
michael@0 1588 docData->mCharset = mCurrentCharset;
michael@0 1589 docData->mDocument = aDocument;
michael@0 1590 docData->mFile = aFile;
michael@0 1591 docData->mRelativePathToData = mCurrentRelativePathToData;
michael@0 1592 docData->mDataPath = mCurrentDataPath;
michael@0 1593 docData->mDataPathIsRelative = mCurrentDataPathIsRelative;
michael@0 1594 mDocList.AppendElement(docData);
michael@0 1595
michael@0 1596 // Walk the DOM gathering a list of externally referenced URIs in the uri map
michael@0 1597 nsCOMPtr<nsIDOMTreeWalker> walker;
michael@0 1598 rv = aDocument->CreateTreeWalker(docAsNode,
michael@0 1599 nsIDOMNodeFilter::SHOW_ELEMENT |
michael@0 1600 nsIDOMNodeFilter::SHOW_DOCUMENT |
michael@0 1601 nsIDOMNodeFilter::SHOW_PROCESSING_INSTRUCTION,
michael@0 1602 nullptr, 1, getter_AddRefs(walker));
michael@0 1603 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1604
michael@0 1605 nsCOMPtr<nsIDOMNode> currentNode;
michael@0 1606 walker->GetCurrentNode(getter_AddRefs(currentNode));
michael@0 1607 while (currentNode)
michael@0 1608 {
michael@0 1609 OnWalkDOMNode(currentNode);
michael@0 1610 walker->NextNode(getter_AddRefs(currentNode));
michael@0 1611 }
michael@0 1612
michael@0 1613 // If there are things to persist, create a directory to hold them
michael@0 1614 if (mCurrentThingsToPersist > 0)
michael@0 1615 {
michael@0 1616 if (localDataPath)
michael@0 1617 {
michael@0 1618 bool exists = false;
michael@0 1619 bool haveDir = false;
michael@0 1620
michael@0 1621 localDataPath->Exists(&exists);
michael@0 1622 if (exists)
michael@0 1623 {
michael@0 1624 localDataPath->IsDirectory(&haveDir);
michael@0 1625 }
michael@0 1626 if (!haveDir)
michael@0 1627 {
michael@0 1628 rv = localDataPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
michael@0 1629 if (NS_SUCCEEDED(rv))
michael@0 1630 haveDir = true;
michael@0 1631 else
michael@0 1632 SendErrorStatusChange(false, rv, nullptr, aFile);
michael@0 1633 }
michael@0 1634 if (!haveDir)
michael@0 1635 {
michael@0 1636 EndDownload(NS_ERROR_FAILURE);
michael@0 1637 mCurrentBaseURI = oldBaseURI;
michael@0 1638 mCurrentCharset = oldCharset;
michael@0 1639 return NS_ERROR_FAILURE;
michael@0 1640 }
michael@0 1641 if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
michael@0 1642 {
michael@0 1643 // Add to list of things to delete later if all goes wrong
michael@0 1644 CleanupData *cleanupData = new CleanupData;
michael@0 1645 NS_ENSURE_TRUE(cleanupData, NS_ERROR_OUT_OF_MEMORY);
michael@0 1646 cleanupData->mFile = localDataPath;
michael@0 1647 cleanupData->mIsDirectory = true;
michael@0 1648 mCleanupList.AppendElement(cleanupData);
michael@0 1649 }
michael@0 1650 }
michael@0 1651 }
michael@0 1652
michael@0 1653 mCurrentThingsToPersist = oldThingsToPersist;
michael@0 1654 mCurrentDataPath = oldDataPath;
michael@0 1655 mCurrentDataPathIsRelative = oldDataPathIsRelative;
michael@0 1656 mCurrentRelativePathToData = oldCurrentRelativePathToData;
michael@0 1657 }
michael@0 1658 else
michael@0 1659 {
michael@0 1660 // Set the document base to ensure relative links still work
michael@0 1661 SetDocumentBase(aDocument, mCurrentBaseURI);
michael@0 1662
michael@0 1663 // Get the content type to save with
michael@0 1664 nsXPIDLString realContentType;
michael@0 1665 GetDocEncoderContentType(aDocument,
michael@0 1666 !mContentType.IsEmpty() ? mContentType.get() : nullptr,
michael@0 1667 getter_Copies(realContentType));
michael@0 1668
michael@0 1669 nsAutoCString contentType; contentType.AssignWithConversion(realContentType);
michael@0 1670 nsAutoCString charType; // Empty
michael@0 1671
michael@0 1672 // Save the document
michael@0 1673 rv = SaveDocumentWithFixup(
michael@0 1674 aDocument,
michael@0 1675 nullptr, // no dom fixup
michael@0 1676 aFile,
michael@0 1677 mReplaceExisting,
michael@0 1678 contentType,
michael@0 1679 charType,
michael@0 1680 mEncodingFlags);
michael@0 1681 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1682 }
michael@0 1683
michael@0 1684 mCurrentBaseURI = oldBaseURI;
michael@0 1685 mCurrentCharset = oldCharset;
michael@0 1686
michael@0 1687 return NS_OK;
michael@0 1688 }
michael@0 1689
michael@0 1690 nsresult nsWebBrowserPersist::SaveDocuments()
michael@0 1691 {
michael@0 1692 nsresult rv = NS_OK;
michael@0 1693
michael@0 1694 mStartSaving = true;
michael@0 1695
michael@0 1696 // Iterate through all queued documents, saving them to file and fixing
michael@0 1697 // them up on the way.
michael@0 1698
michael@0 1699 uint32_t i;
michael@0 1700 for (i = 0; i < mDocList.Length(); i++)
michael@0 1701 {
michael@0 1702 DocData *docData = mDocList.ElementAt(i);
michael@0 1703 if (!docData)
michael@0 1704 {
michael@0 1705 rv = NS_ERROR_FAILURE;
michael@0 1706 break;
michael@0 1707 }
michael@0 1708
michael@0 1709 mCurrentBaseURI = docData->mBaseURI;
michael@0 1710 mCurrentCharset = docData->mCharset;
michael@0 1711
michael@0 1712 // Save the document, fixing it up with the new URIs as we do
michael@0 1713
michael@0 1714 nsEncoderNodeFixup *nodeFixup;
michael@0 1715 nodeFixup = new nsEncoderNodeFixup;
michael@0 1716 if (nodeFixup)
michael@0 1717 nodeFixup->mWebBrowserPersist = this;
michael@0 1718
michael@0 1719 // Get the content type
michael@0 1720 nsXPIDLString realContentType;
michael@0 1721 GetDocEncoderContentType(docData->mDocument,
michael@0 1722 !mContentType.IsEmpty() ? mContentType.get() : nullptr,
michael@0 1723 getter_Copies(realContentType));
michael@0 1724
michael@0 1725 nsAutoCString contentType; contentType.AssignWithConversion(realContentType.get());
michael@0 1726 nsAutoCString charType; // Empty
michael@0 1727
michael@0 1728 // Save the document, fixing up the links as it goes out
michael@0 1729 rv = SaveDocumentWithFixup(
michael@0 1730 docData->mDocument,
michael@0 1731 nodeFixup,
michael@0 1732 docData->mFile,
michael@0 1733 mReplaceExisting,
michael@0 1734 contentType,
michael@0 1735 charType,
michael@0 1736 mEncodingFlags);
michael@0 1737
michael@0 1738 if (NS_FAILED(rv))
michael@0 1739 break;
michael@0 1740
michael@0 1741 // if we're serializing, bail after first iteration of loop
michael@0 1742 if (mSerializingOutput)
michael@0 1743 break;
michael@0 1744 }
michael@0 1745
michael@0 1746 // delete, cleanup regardless of errors (bug 132417)
michael@0 1747 for (i = 0; i < mDocList.Length(); i++)
michael@0 1748 {
michael@0 1749 DocData *docData = mDocList.ElementAt(i);
michael@0 1750 delete docData;
michael@0 1751 if (mSerializingOutput)
michael@0 1752 {
michael@0 1753 mDocList.RemoveElementAt(i);
michael@0 1754 break;
michael@0 1755 }
michael@0 1756 }
michael@0 1757
michael@0 1758 if (!mSerializingOutput)
michael@0 1759 {
michael@0 1760 mDocList.Clear();
michael@0 1761 }
michael@0 1762
michael@0 1763 return rv;
michael@0 1764 }
michael@0 1765
michael@0 1766 void nsWebBrowserPersist::Cleanup()
michael@0 1767 {
michael@0 1768 mURIMap.Clear();
michael@0 1769 mOutputMap.EnumerateRead(EnumCleanupOutputMap, this);
michael@0 1770 mOutputMap.Clear();
michael@0 1771 mUploadList.EnumerateRead(EnumCleanupUploadList, this);
michael@0 1772 mUploadList.Clear();
michael@0 1773 uint32_t i;
michael@0 1774 for (i = 0; i < mDocList.Length(); i++)
michael@0 1775 {
michael@0 1776 DocData *docData = mDocList.ElementAt(i);
michael@0 1777 delete docData;
michael@0 1778 }
michael@0 1779 mDocList.Clear();
michael@0 1780 for (i = 0; i < mCleanupList.Length(); i++)
michael@0 1781 {
michael@0 1782 CleanupData *cleanupData = mCleanupList.ElementAt(i);
michael@0 1783 delete cleanupData;
michael@0 1784 }
michael@0 1785 mCleanupList.Clear();
michael@0 1786 mFilenameList.Clear();
michael@0 1787 }
michael@0 1788
michael@0 1789 void nsWebBrowserPersist::CleanupLocalFiles()
michael@0 1790 {
michael@0 1791 // Two passes, the first pass cleans up files, the second pass tests
michael@0 1792 // for and then deletes empty directories. Directories that are not
michael@0 1793 // empty after the first pass must contain files from something else
michael@0 1794 // and are not deleted.
michael@0 1795 int pass;
michael@0 1796 for (pass = 0; pass < 2; pass++)
michael@0 1797 {
michael@0 1798 uint32_t i;
michael@0 1799 for (i = 0; i < mCleanupList.Length(); i++)
michael@0 1800 {
michael@0 1801 CleanupData *cleanupData = mCleanupList.ElementAt(i);
michael@0 1802 nsCOMPtr<nsIFile> file = cleanupData->mFile;
michael@0 1803
michael@0 1804 // Test if the dir / file exists (something in an earlier loop
michael@0 1805 // may have already removed it)
michael@0 1806 bool exists = false;
michael@0 1807 file->Exists(&exists);
michael@0 1808 if (!exists)
michael@0 1809 continue;
michael@0 1810
michael@0 1811 // Test if the file has changed in between creation and deletion
michael@0 1812 // in some way that means it should be ignored
michael@0 1813 bool isDirectory = false;
michael@0 1814 file->IsDirectory(&isDirectory);
michael@0 1815 if (isDirectory != cleanupData->mIsDirectory)
michael@0 1816 continue; // A file has become a dir or vice versa !
michael@0 1817
michael@0 1818 if (pass == 0 && !isDirectory)
michael@0 1819 {
michael@0 1820 file->Remove(false);
michael@0 1821 }
michael@0 1822 else if (pass == 1 && isDirectory) // Directory
michael@0 1823 {
michael@0 1824 // Directories are more complicated. Enumerate through
michael@0 1825 // children looking for files. Any files created by the
michael@0 1826 // persist object would have been deleted by the first
michael@0 1827 // pass so if there are any there at this stage, the dir
michael@0 1828 // cannot be deleted because it has someone else's files
michael@0 1829 // in it. Empty child dirs are deleted but they must be
michael@0 1830 // recursed through to ensure they are actually empty.
michael@0 1831
michael@0 1832 bool isEmptyDirectory = true;
michael@0 1833 nsCOMArray<nsISimpleEnumerator> dirStack;
michael@0 1834 int32_t stackSize = 0;
michael@0 1835
michael@0 1836 // Push the top level enum onto the stack
michael@0 1837 nsCOMPtr<nsISimpleEnumerator> pos;
michael@0 1838 if (NS_SUCCEEDED(file->GetDirectoryEntries(getter_AddRefs(pos))))
michael@0 1839 dirStack.AppendObject(pos);
michael@0 1840
michael@0 1841 while (isEmptyDirectory && (stackSize = dirStack.Count()))
michael@0 1842 {
michael@0 1843 // Pop the last element
michael@0 1844 nsCOMPtr<nsISimpleEnumerator> curPos;
michael@0 1845 curPos = dirStack[stackSize-1];
michael@0 1846 dirStack.RemoveObjectAt(stackSize - 1);
michael@0 1847
michael@0 1848 // Test if the enumerator has any more files in it
michael@0 1849 bool hasMoreElements = false;
michael@0 1850 curPos->HasMoreElements(&hasMoreElements);
michael@0 1851 if (!hasMoreElements)
michael@0 1852 {
michael@0 1853 continue;
michael@0 1854 }
michael@0 1855
michael@0 1856 // Child files automatically make this code drop out,
michael@0 1857 // while child dirs keep the loop going.
michael@0 1858 nsCOMPtr<nsISupports> child;
michael@0 1859 curPos->GetNext(getter_AddRefs(child));
michael@0 1860 NS_ASSERTION(child, "No child element, but hasMoreElements says otherwise");
michael@0 1861 if (!child)
michael@0 1862 continue;
michael@0 1863 nsCOMPtr<nsIFile> childAsFile = do_QueryInterface(child);
michael@0 1864 NS_ASSERTION(childAsFile, "This should be a file but isn't");
michael@0 1865
michael@0 1866 bool childIsSymlink = false;
michael@0 1867 childAsFile->IsSymlink(&childIsSymlink);
michael@0 1868 bool childIsDir = false;
michael@0 1869 childAsFile->IsDirectory(&childIsDir);
michael@0 1870 if (!childIsDir || childIsSymlink)
michael@0 1871 {
michael@0 1872 // Some kind of file or symlink which means dir
michael@0 1873 // is not empty so just drop out.
michael@0 1874 isEmptyDirectory = false;
michael@0 1875 break;
michael@0 1876 }
michael@0 1877 // Push parent enumerator followed by child enumerator
michael@0 1878 nsCOMPtr<nsISimpleEnumerator> childPos;
michael@0 1879 childAsFile->GetDirectoryEntries(getter_AddRefs(childPos));
michael@0 1880 dirStack.AppendObject(curPos);
michael@0 1881 if (childPos)
michael@0 1882 dirStack.AppendObject(childPos);
michael@0 1883
michael@0 1884 }
michael@0 1885 dirStack.Clear();
michael@0 1886
michael@0 1887 // If after all that walking the dir is deemed empty, delete it
michael@0 1888 if (isEmptyDirectory)
michael@0 1889 {
michael@0 1890 file->Remove(true);
michael@0 1891 }
michael@0 1892 }
michael@0 1893 }
michael@0 1894 }
michael@0 1895 }
michael@0 1896
michael@0 1897 nsresult
michael@0 1898 nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
michael@0 1899 {
michael@0 1900 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
michael@0 1901 NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
michael@0 1902
michael@0 1903 bool nameHasChanged = false;
michael@0 1904 nsresult rv;
michael@0 1905
michael@0 1906 // Get the old filename
michael@0 1907 nsAutoCString filename;
michael@0 1908 rv = url->GetFileName(filename);
michael@0 1909 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1910 nsAutoCString directory;
michael@0 1911 rv = url->GetDirectory(directory);
michael@0 1912 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 1913
michael@0 1914 // Split the filename into a base and an extension.
michael@0 1915 // e.g. "foo.html" becomes "foo" & ".html"
michael@0 1916 //
michael@0 1917 // The nsIURL methods GetFileBaseName & GetFileExtension don't
michael@0 1918 // preserve the dot whereas this code does to save some effort
michael@0 1919 // later when everything is put back together.
michael@0 1920 int32_t lastDot = filename.RFind(".");
michael@0 1921 nsAutoCString base;
michael@0 1922 nsAutoCString ext;
michael@0 1923 if (lastDot >= 0)
michael@0 1924 {
michael@0 1925 filename.Mid(base, 0, lastDot);
michael@0 1926 filename.Mid(ext, lastDot, filename.Length() - lastDot); // includes dot
michael@0 1927 }
michael@0 1928 else
michael@0 1929 {
michael@0 1930 // filename contains no dot
michael@0 1931 base = filename;
michael@0 1932 }
michael@0 1933
michael@0 1934 // Test if the filename is longer than allowed by the OS
michael@0 1935 int32_t needToChop = filename.Length() - kDefaultMaxFilenameLength;
michael@0 1936 if (needToChop > 0)
michael@0 1937 {
michael@0 1938 // Truncate the base first and then the ext if necessary
michael@0 1939 if (base.Length() > (uint32_t) needToChop)
michael@0 1940 {
michael@0 1941 base.Truncate(base.Length() - needToChop);
michael@0 1942 }
michael@0 1943 else
michael@0 1944 {
michael@0 1945 needToChop -= base.Length() - 1;
michael@0 1946 base.Truncate(1);
michael@0 1947 if (ext.Length() > (uint32_t) needToChop)
michael@0 1948 {
michael@0 1949 ext.Truncate(ext.Length() - needToChop);
michael@0 1950 }
michael@0 1951 else
michael@0 1952 {
michael@0 1953 ext.Truncate(0);
michael@0 1954 }
michael@0 1955 // If kDefaultMaxFilenameLength were 1 we'd be in trouble here,
michael@0 1956 // but that won't happen because it will be set to a sensible
michael@0 1957 // value.
michael@0 1958 }
michael@0 1959
michael@0 1960 filename.Assign(base);
michael@0 1961 filename.Append(ext);
michael@0 1962 nameHasChanged = true;
michael@0 1963 }
michael@0 1964
michael@0 1965 // Ensure the filename is unique
michael@0 1966 // Create a filename if it's empty, or if the filename / datapath is
michael@0 1967 // already taken by another URI and create an alternate name.
michael@0 1968
michael@0 1969 if (base.IsEmpty() || !mFilenameList.IsEmpty())
michael@0 1970 {
michael@0 1971 nsAutoCString tmpPath;
michael@0 1972 nsAutoCString tmpBase;
michael@0 1973 uint32_t duplicateCounter = 1;
michael@0 1974 while (1)
michael@0 1975 {
michael@0 1976 // Make a file name,
michael@0 1977 // Foo become foo_001, foo_002, etc.
michael@0 1978 // Empty files become _001, _002 etc.
michael@0 1979
michael@0 1980 if (base.IsEmpty() || duplicateCounter > 1)
michael@0 1981 {
michael@0 1982 char * tmp = PR_smprintf("_%03d", duplicateCounter);
michael@0 1983 NS_ENSURE_TRUE(tmp, NS_ERROR_OUT_OF_MEMORY);
michael@0 1984 if (filename.Length() < kDefaultMaxFilenameLength - 4)
michael@0 1985 {
michael@0 1986 tmpBase = base;
michael@0 1987 }
michael@0 1988 else
michael@0 1989 {
michael@0 1990 base.Mid(tmpBase, 0, base.Length() - 4);
michael@0 1991 }
michael@0 1992 tmpBase.Append(tmp);
michael@0 1993 PR_smprintf_free(tmp);
michael@0 1994 }
michael@0 1995 else
michael@0 1996 {
michael@0 1997 tmpBase = base;
michael@0 1998 }
michael@0 1999
michael@0 2000 tmpPath.Assign(directory);
michael@0 2001 tmpPath.Append(tmpBase);
michael@0 2002 tmpPath.Append(ext);
michael@0 2003
michael@0 2004 // Test if the name is a duplicate
michael@0 2005 if (!mFilenameList.Contains(tmpPath))
michael@0 2006 {
michael@0 2007 if (!base.Equals(tmpBase))
michael@0 2008 {
michael@0 2009 filename.Assign(tmpBase);
michael@0 2010 filename.Append(ext);
michael@0 2011 nameHasChanged = true;
michael@0 2012 }
michael@0 2013 break;
michael@0 2014 }
michael@0 2015 duplicateCounter++;
michael@0 2016 }
michael@0 2017 }
michael@0 2018
michael@0 2019 // Add name to list of those already used
michael@0 2020 nsAutoCString newFilepath(directory);
michael@0 2021 newFilepath.Append(filename);
michael@0 2022 mFilenameList.AppendElement(newFilepath);
michael@0 2023
michael@0 2024 // Update the uri accordingly if the filename actually changed
michael@0 2025 if (nameHasChanged)
michael@0 2026 {
michael@0 2027 // Final sanity test
michael@0 2028 if (filename.Length() > kDefaultMaxFilenameLength)
michael@0 2029 {
michael@0 2030 NS_WARNING("Filename wasn't truncated less than the max file length - how can that be?");
michael@0 2031 return NS_ERROR_FAILURE;
michael@0 2032 }
michael@0 2033
michael@0 2034 nsCOMPtr<nsIFile> localFile;
michael@0 2035 GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
michael@0 2036
michael@0 2037 if (localFile)
michael@0 2038 {
michael@0 2039 nsAutoString filenameAsUnichar;
michael@0 2040 filenameAsUnichar.AssignWithConversion(filename.get());
michael@0 2041 localFile->SetLeafName(filenameAsUnichar);
michael@0 2042
michael@0 2043 // Resync the URI with the file after the extension has been appended
michael@0 2044 nsresult rv;
michael@0 2045 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
michael@0 2046 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 2047 fileURL->SetFile(localFile); // this should recalculate uri
michael@0 2048 }
michael@0 2049 else
michael@0 2050 {
michael@0 2051 url->SetFileName(filename);
michael@0 2052 }
michael@0 2053 }
michael@0 2054
michael@0 2055 return NS_OK;
michael@0 2056 }
michael@0 2057
michael@0 2058
michael@0 2059 nsresult
michael@0 2060 nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
michael@0 2061 {
michael@0 2062 // Try to get filename from the URI.
michael@0 2063 nsAutoString fileName;
michael@0 2064
michael@0 2065 // Get a suggested file name from the URL but strip it of characters
michael@0 2066 // likely to cause the name to be illegal.
michael@0 2067
michael@0 2068 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
michael@0 2069 if (url)
michael@0 2070 {
michael@0 2071 nsAutoCString nameFromURL;
michael@0 2072 url->GetFileName(nameFromURL);
michael@0 2073 if (mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES)
michael@0 2074 {
michael@0 2075 fileName.AssignWithConversion(NS_UnescapeURL(nameFromURL).get());
michael@0 2076 goto end;
michael@0 2077 }
michael@0 2078 if (!nameFromURL.IsEmpty())
michael@0 2079 {
michael@0 2080 // Unescape the file name (GetFileName escapes it)
michael@0 2081 NS_UnescapeURL(nameFromURL);
michael@0 2082 uint32_t nameLength = 0;
michael@0 2083 const char *p = nameFromURL.get();
michael@0 2084 for (;*p && *p != ';' && *p != '?' && *p != '#' && *p != '.'
michael@0 2085 ;p++)
michael@0 2086 {
michael@0 2087 if (nsCRT::IsAsciiAlpha(*p) || nsCRT::IsAsciiDigit(*p)
michael@0 2088 || *p == '.' || *p == '-' || *p == '_' || (*p == ' '))
michael@0 2089 {
michael@0 2090 fileName.Append(char16_t(*p));
michael@0 2091 if (++nameLength == kDefaultMaxFilenameLength)
michael@0 2092 {
michael@0 2093 // Note:
michael@0 2094 // There is no point going any further since it will be
michael@0 2095 // truncated in CalculateUniqueFilename anyway.
michael@0 2096 // More importantly, certain implementations of
michael@0 2097 // nsIFile (e.g. the Mac impl) might truncate
michael@0 2098 // names in undesirable ways, such as truncating from
michael@0 2099 // the middle, inserting ellipsis and so on.
michael@0 2100 break;
michael@0 2101 }
michael@0 2102 }
michael@0 2103 }
michael@0 2104 }
michael@0 2105 }
michael@0 2106
michael@0 2107 // Empty filenames can confuse the local file object later
michael@0 2108 // when it attempts to set the leaf name in CalculateUniqueFilename
michael@0 2109 // for duplicates and ends up replacing the parent dir. To avoid
michael@0 2110 // the problem, all filenames are made at least one character long.
michael@0 2111 if (fileName.IsEmpty())
michael@0 2112 {
michael@0 2113 fileName.Append(char16_t('a')); // 'a' is for arbitrary
michael@0 2114 }
michael@0 2115
michael@0 2116 end:
michael@0 2117 aFilename = fileName;
michael@0 2118 return NS_OK;
michael@0 2119 }
michael@0 2120
michael@0 2121
michael@0 2122 nsresult
michael@0 2123 nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
michael@0 2124 {
michael@0 2125 nsresult rv;
michael@0 2126
michael@0 2127 if (!mMIMEService)
michael@0 2128 {
michael@0 2129 mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
michael@0 2130 NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
michael@0 2131 }
michael@0 2132
michael@0 2133 nsAutoCString contentType;
michael@0 2134
michael@0 2135 // Get the content type from the channel
michael@0 2136 aChannel->GetContentType(contentType);
michael@0 2137
michael@0 2138 // Get the content type from the MIME service
michael@0 2139 if (contentType.IsEmpty())
michael@0 2140 {
michael@0 2141 nsCOMPtr<nsIURI> uri;
michael@0 2142 aChannel->GetOriginalURI(getter_AddRefs(uri));
michael@0 2143 mMIMEService->GetTypeFromURI(uri, contentType);
michael@0 2144 }
michael@0 2145
michael@0 2146 // Append the extension onto the file
michael@0 2147 if (!contentType.IsEmpty())
michael@0 2148 {
michael@0 2149 nsCOMPtr<nsIMIMEInfo> mimeInfo;
michael@0 2150 mMIMEService->GetFromTypeAndExtension(
michael@0 2151 contentType, EmptyCString(), getter_AddRefs(mimeInfo));
michael@0 2152
michael@0 2153 nsCOMPtr<nsIFile> localFile;
michael@0 2154 GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
michael@0 2155
michael@0 2156 if (mimeInfo)
michael@0 2157 {
michael@0 2158 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
michael@0 2159 NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
michael@0 2160
michael@0 2161 nsAutoCString newFileName;
michael@0 2162 url->GetFileName(newFileName);
michael@0 2163
michael@0 2164 // Test if the current extension is current for the mime type
michael@0 2165 bool hasExtension = false;
michael@0 2166 int32_t ext = newFileName.RFind(".");
michael@0 2167 if (ext != -1)
michael@0 2168 {
michael@0 2169 mimeInfo->ExtensionExists(Substring(newFileName, ext + 1), &hasExtension);
michael@0 2170 }
michael@0 2171
michael@0 2172 // Append the mime file extension
michael@0 2173 nsAutoCString fileExt;
michael@0 2174 if (!hasExtension)
michael@0 2175 {
michael@0 2176 // Test if previous extension is acceptable
michael@0 2177 nsCOMPtr<nsIURL> oldurl(do_QueryInterface(aOriginalURIWithExtension));
michael@0 2178 NS_ENSURE_TRUE(oldurl, NS_ERROR_FAILURE);
michael@0 2179 oldurl->GetFileExtension(fileExt);
michael@0 2180 bool useOldExt = false;
michael@0 2181 if (!fileExt.IsEmpty())
michael@0 2182 {
michael@0 2183 mimeInfo->ExtensionExists(fileExt, &useOldExt);
michael@0 2184 }
michael@0 2185
michael@0 2186 // can't use old extension so use primary extension
michael@0 2187 if (!useOldExt)
michael@0 2188 {
michael@0 2189 mimeInfo->GetPrimaryExtension(fileExt);
michael@0 2190 }
michael@0 2191
michael@0 2192 if (!fileExt.IsEmpty())
michael@0 2193 {
michael@0 2194 uint32_t newLength = newFileName.Length() + fileExt.Length() + 1;
michael@0 2195 if (newLength > kDefaultMaxFilenameLength)
michael@0 2196 {
michael@0 2197 if (fileExt.Length() > kDefaultMaxFilenameLength/2)
michael@0 2198 fileExt.Truncate(kDefaultMaxFilenameLength/2);
michael@0 2199
michael@0 2200 uint32_t diff = kDefaultMaxFilenameLength - 1 -
michael@0 2201 fileExt.Length();
michael@0 2202 if (newFileName.Length() > diff)
michael@0 2203 newFileName.Truncate(diff);
michael@0 2204 }
michael@0 2205 newFileName.Append(".");
michael@0 2206 newFileName.Append(fileExt);
michael@0 2207 }
michael@0 2208
michael@0 2209 if (localFile)
michael@0 2210 {
michael@0 2211 localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
michael@0 2212
michael@0 2213 // Resync the URI with the file after the extension has been appended
michael@0 2214 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
michael@0 2215 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 2216 fileURL->SetFile(localFile); // this should recalculate uri
michael@0 2217 }
michael@0 2218 else
michael@0 2219 {
michael@0 2220 url->SetFileName(newFileName);
michael@0 2221 }
michael@0 2222 }
michael@0 2223
michael@0 2224 }
michael@0 2225 }
michael@0 2226
michael@0 2227 return NS_OK;
michael@0 2228 }
michael@0 2229
michael@0 2230 nsresult
michael@0 2231 nsWebBrowserPersist::MakeOutputStream(
michael@0 2232 nsIURI *aURI, nsIOutputStream **aOutputStream)
michael@0 2233 {
michael@0 2234 nsresult rv;
michael@0 2235
michael@0 2236 nsCOMPtr<nsIFile> localFile;
michael@0 2237 GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
michael@0 2238 if (localFile)
michael@0 2239 rv = MakeOutputStreamFromFile(localFile, aOutputStream);
michael@0 2240 else
michael@0 2241 rv = MakeOutputStreamFromURI(aURI, aOutputStream);
michael@0 2242
michael@0 2243 return rv;
michael@0 2244 }
michael@0 2245
michael@0 2246 nsresult
michael@0 2247 nsWebBrowserPersist::MakeOutputStreamFromFile(
michael@0 2248 nsIFile *aFile, nsIOutputStream **aOutputStream)
michael@0 2249 {
michael@0 2250 nsresult rv = NS_OK;
michael@0 2251
michael@0 2252 nsCOMPtr<nsIFileOutputStream> fileOutputStream =
michael@0 2253 do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
michael@0 2254 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 2255
michael@0 2256 // XXX brade: get the right flags here!
michael@0 2257 int32_t ioFlags = -1;
michael@0 2258 if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE)
michael@0 2259 ioFlags = PR_APPEND | PR_CREATE_FILE | PR_WRONLY;
michael@0 2260 rv = fileOutputStream->Init(aFile, ioFlags, -1, 0);
michael@0 2261 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2262
michael@0 2263 *aOutputStream = NS_BufferOutputStream(fileOutputStream,
michael@0 2264 BUFFERED_OUTPUT_SIZE).take();
michael@0 2265
michael@0 2266 if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
michael@0 2267 {
michael@0 2268 // Add to cleanup list in event of failure
michael@0 2269 CleanupData *cleanupData = new CleanupData;
michael@0 2270 if (!cleanupData) {
michael@0 2271 NS_RELEASE(*aOutputStream);
michael@0 2272 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2273 }
michael@0 2274 cleanupData->mFile = aFile;
michael@0 2275 cleanupData->mIsDirectory = false;
michael@0 2276 mCleanupList.AppendElement(cleanupData);
michael@0 2277 }
michael@0 2278
michael@0 2279 return NS_OK;
michael@0 2280 }
michael@0 2281
michael@0 2282 nsresult
michael@0 2283 nsWebBrowserPersist::MakeOutputStreamFromURI(
michael@0 2284 nsIURI *aURI, nsIOutputStream **aOutputStream)
michael@0 2285 {
michael@0 2286 uint32_t segsize = 8192;
michael@0 2287 uint32_t maxsize = uint32_t(-1);
michael@0 2288 nsCOMPtr<nsIStorageStream> storStream;
michael@0 2289 nsresult rv = NS_NewStorageStream(segsize, maxsize, getter_AddRefs(storStream));
michael@0 2290 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2291
michael@0 2292 NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
michael@0 2293 return NS_OK;
michael@0 2294 }
michael@0 2295
michael@0 2296 void
michael@0 2297 nsWebBrowserPersist::EndDownload(nsresult aResult)
michael@0 2298 {
michael@0 2299 // Store the error code in the result if it is an error
michael@0 2300 if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(aResult))
michael@0 2301 {
michael@0 2302 mPersistResult = aResult;
michael@0 2303 }
michael@0 2304
michael@0 2305 // Do file cleanup if required
michael@0 2306 if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
michael@0 2307 {
michael@0 2308 CleanupLocalFiles();
michael@0 2309 }
michael@0 2310
michael@0 2311 // Cleanup the channels
michael@0 2312 mCompleted = true;
michael@0 2313 Cleanup();
michael@0 2314 }
michael@0 2315
michael@0 2316 struct MOZ_STACK_CLASS FixRedirectData
michael@0 2317 {
michael@0 2318 nsCOMPtr<nsIChannel> mNewChannel;
michael@0 2319 nsCOMPtr<nsIURI> mOriginalURI;
michael@0 2320 nsCOMPtr<nsISupports> mMatchingKey;
michael@0 2321 };
michael@0 2322
michael@0 2323 nsresult
michael@0 2324 nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
michael@0 2325 {
michael@0 2326 NS_ENSURE_ARG_POINTER(aNewChannel);
michael@0 2327 nsCOMPtr<nsIURI> originalURI;
michael@0 2328
michael@0 2329 // Enumerate through existing open channels looking for one with
michael@0 2330 // a URI matching the one specified.
michael@0 2331
michael@0 2332 FixRedirectData data;
michael@0 2333 data.mNewChannel = aNewChannel;
michael@0 2334 data.mNewChannel->GetOriginalURI(getter_AddRefs(data.mOriginalURI));
michael@0 2335 mOutputMap.EnumerateRead(EnumFixRedirect, &data);
michael@0 2336
michael@0 2337 // If a match is found, remove the data entry with the old channel key
michael@0 2338 // and re-add it with the new channel key.
michael@0 2339
michael@0 2340 if (data.mMatchingKey)
michael@0 2341 {
michael@0 2342 nsAutoPtr<OutputData> outputData;
michael@0 2343 mOutputMap.RemoveAndForget(data.mMatchingKey, outputData);
michael@0 2344 NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
michael@0 2345
michael@0 2346 // Store data again with new channel unless told to ignore redirects
michael@0 2347 if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA))
michael@0 2348 {
michael@0 2349 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
michael@0 2350 mOutputMap.Put(keyPtr, outputData.forget());
michael@0 2351 }
michael@0 2352 }
michael@0 2353
michael@0 2354 return NS_OK;
michael@0 2355 }
michael@0 2356
michael@0 2357 PLDHashOperator
michael@0 2358 nsWebBrowserPersist::EnumFixRedirect(nsISupports *aKey, OutputData *aData, void* aClosure)
michael@0 2359 {
michael@0 2360 FixRedirectData *data = static_cast<FixRedirectData*>(aClosure);
michael@0 2361
michael@0 2362 nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(aKey);
michael@0 2363 nsCOMPtr<nsIURI> thisURI;
michael@0 2364
michael@0 2365 thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
michael@0 2366
michael@0 2367 // Compare this channel's URI to the one passed in.
michael@0 2368 bool matchingURI = false;
michael@0 2369 thisURI->Equals(data->mOriginalURI, &matchingURI);
michael@0 2370 if (matchingURI)
michael@0 2371 {
michael@0 2372 data->mMatchingKey = aKey;
michael@0 2373 return PL_DHASH_STOP;
michael@0 2374 }
michael@0 2375
michael@0 2376 return PL_DHASH_NEXT;
michael@0 2377 }
michael@0 2378
michael@0 2379 void
michael@0 2380 nsWebBrowserPersist::CalcTotalProgress()
michael@0 2381 {
michael@0 2382 mTotalCurrentProgress = 0;
michael@0 2383 mTotalMaxProgress = 0;
michael@0 2384
michael@0 2385 if (mOutputMap.Count() > 0)
michael@0 2386 {
michael@0 2387 // Total up the progress of each output stream
michael@0 2388 mOutputMap.EnumerateRead(EnumCalcProgress, this);
michael@0 2389 }
michael@0 2390
michael@0 2391 if (mUploadList.Count() > 0)
michael@0 2392 {
michael@0 2393 // Total up the progress of each upload
michael@0 2394 mUploadList.EnumerateRead(EnumCalcUploadProgress, this);
michael@0 2395 }
michael@0 2396
michael@0 2397 // XXX this code seems pretty bogus and pointless
michael@0 2398 if (mTotalCurrentProgress == 0 && mTotalMaxProgress == 0)
michael@0 2399 {
michael@0 2400 // No output streams so we must be complete
michael@0 2401 mTotalCurrentProgress = 10000;
michael@0 2402 mTotalMaxProgress = 10000;
michael@0 2403 }
michael@0 2404 }
michael@0 2405
michael@0 2406 PLDHashOperator
michael@0 2407 nsWebBrowserPersist::EnumCalcProgress(nsISupports *aKey, OutputData *aData, void* aClosure)
michael@0 2408 {
michael@0 2409 nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
michael@0 2410
michael@0 2411 // only count toward total progress if destination file is local
michael@0 2412 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aData->mFile);
michael@0 2413 if (fileURL)
michael@0 2414 {
michael@0 2415 pthis->mTotalCurrentProgress += aData->mSelfProgress;
michael@0 2416 pthis->mTotalMaxProgress += aData->mSelfProgressMax;
michael@0 2417 }
michael@0 2418 return PL_DHASH_NEXT;
michael@0 2419 }
michael@0 2420
michael@0 2421 PLDHashOperator
michael@0 2422 nsWebBrowserPersist::EnumCalcUploadProgress(nsISupports *aKey, UploadData *aData, void* aClosure)
michael@0 2423 {
michael@0 2424 if (aData && aClosure)
michael@0 2425 {
michael@0 2426 nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
michael@0 2427 pthis->mTotalCurrentProgress += aData->mSelfProgress;
michael@0 2428 pthis->mTotalMaxProgress += aData->mSelfProgressMax;
michael@0 2429 }
michael@0 2430 return PL_DHASH_NEXT;
michael@0 2431 }
michael@0 2432
michael@0 2433 PLDHashOperator
michael@0 2434 nsWebBrowserPersist::EnumCountURIsToPersist(const nsACString &aKey, URIData *aData, void* aClosure)
michael@0 2435 {
michael@0 2436 uint32_t *count = static_cast<uint32_t*>(aClosure);
michael@0 2437 if (aData->mNeedsPersisting && !aData->mSaved)
michael@0 2438 {
michael@0 2439 (*count)++;
michael@0 2440 }
michael@0 2441 return PL_DHASH_NEXT;
michael@0 2442 }
michael@0 2443
michael@0 2444 PLDHashOperator
michael@0 2445 nsWebBrowserPersist::EnumPersistURIs(const nsACString &aKey, URIData *aData, void* aClosure)
michael@0 2446 {
michael@0 2447 if (!aData->mNeedsPersisting || aData->mSaved)
michael@0 2448 {
michael@0 2449 return PL_DHASH_NEXT;
michael@0 2450 }
michael@0 2451
michael@0 2452 nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
michael@0 2453 nsresult rv;
michael@0 2454
michael@0 2455 // Create a URI from the key
michael@0 2456 nsAutoCString key = nsAutoCString(aKey);
michael@0 2457 nsCOMPtr<nsIURI> uri;
michael@0 2458 rv = NS_NewURI(getter_AddRefs(uri),
michael@0 2459 nsDependentCString(key.get(), key.Length()),
michael@0 2460 aData->mCharset.get());
michael@0 2461 NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
michael@0 2462
michael@0 2463 // Make a URI to save the data to
michael@0 2464 nsCOMPtr<nsIURI> fileAsURI;
michael@0 2465 rv = aData->mDataPath->Clone(getter_AddRefs(fileAsURI));
michael@0 2466 NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
michael@0 2467 rv = pthis->AppendPathToURI(fileAsURI, aData->mFilename);
michael@0 2468 NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
michael@0 2469
michael@0 2470 rv = pthis->SaveURIInternal(uri, nullptr, nullptr, nullptr, nullptr, fileAsURI, true,
michael@0 2471 pthis->mIsPrivate);
michael@0 2472 // if SaveURIInternal fails, then it will have called EndDownload,
michael@0 2473 // which means that |aData| is no longer valid memory. we MUST bail.
michael@0 2474 NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
michael@0 2475
michael@0 2476 if (rv == NS_OK)
michael@0 2477 {
michael@0 2478 // Store the actual object because once it's persisted this
michael@0 2479 // will be fixed up with the right file extension.
michael@0 2480
michael@0 2481 aData->mFile = fileAsURI;
michael@0 2482 aData->mSaved = true;
michael@0 2483 }
michael@0 2484 else
michael@0 2485 {
michael@0 2486 aData->mNeedsFixup = false;
michael@0 2487 }
michael@0 2488
michael@0 2489 if (pthis->mSerializingOutput)
michael@0 2490 return PL_DHASH_STOP;
michael@0 2491
michael@0 2492 return PL_DHASH_NEXT;
michael@0 2493 }
michael@0 2494
michael@0 2495 PLDHashOperator
michael@0 2496 nsWebBrowserPersist::EnumCleanupOutputMap(nsISupports *aKey, OutputData *aData, void* aClosure)
michael@0 2497 {
michael@0 2498 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
michael@0 2499 if (channel)
michael@0 2500 {
michael@0 2501 channel->Cancel(NS_BINDING_ABORTED);
michael@0 2502 }
michael@0 2503 return PL_DHASH_NEXT;
michael@0 2504 }
michael@0 2505
michael@0 2506 PLDHashOperator
michael@0 2507 nsWebBrowserPersist::EnumCleanupUploadList(nsISupports *aKey, UploadData *aData, void* aClosure)
michael@0 2508 {
michael@0 2509 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aKey);
michael@0 2510 if (channel)
michael@0 2511 {
michael@0 2512 channel->Cancel(NS_BINDING_ABORTED);
michael@0 2513 }
michael@0 2514 return PL_DHASH_NEXT;
michael@0 2515 }
michael@0 2516
michael@0 2517 nsresult nsWebBrowserPersist::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, const nsAString &aHref)
michael@0 2518 {
michael@0 2519 NS_ENSURE_ARG_POINTER(aPI);
michael@0 2520 nsresult rv = NS_OK;
michael@0 2521
michael@0 2522 nsAutoString data;
michael@0 2523 rv = aPI->GetData(data);
michael@0 2524 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 2525
michael@0 2526 nsAutoString href;
michael@0 2527 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2528 nsGkAtoms::href,
michael@0 2529 href);
michael@0 2530
michael@0 2531 // Construct and set a new data value for the xml-stylesheet
michael@0 2532 if (!aHref.IsEmpty() && !href.IsEmpty())
michael@0 2533 {
michael@0 2534 nsAutoString alternate;
michael@0 2535 nsAutoString charset;
michael@0 2536 nsAutoString title;
michael@0 2537 nsAutoString type;
michael@0 2538 nsAutoString media;
michael@0 2539
michael@0 2540 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2541 nsGkAtoms::alternate,
michael@0 2542 alternate);
michael@0 2543 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2544 nsGkAtoms::charset,
michael@0 2545 charset);
michael@0 2546 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2547 nsGkAtoms::title,
michael@0 2548 title);
michael@0 2549 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2550 nsGkAtoms::type,
michael@0 2551 type);
michael@0 2552 nsContentUtils::GetPseudoAttributeValue(data,
michael@0 2553 nsGkAtoms::media,
michael@0 2554 media);
michael@0 2555
michael@0 2556 NS_NAMED_LITERAL_STRING(kCloseAttr, "\" ");
michael@0 2557 nsAutoString newData;
michael@0 2558 newData += NS_LITERAL_STRING("href=\"") + aHref + kCloseAttr;
michael@0 2559 if (!title.IsEmpty())
michael@0 2560 {
michael@0 2561 newData += NS_LITERAL_STRING("title=\"") + title + kCloseAttr;
michael@0 2562 }
michael@0 2563 if (!media.IsEmpty())
michael@0 2564 {
michael@0 2565 newData += NS_LITERAL_STRING("media=\"") + media + kCloseAttr;
michael@0 2566 }
michael@0 2567 if (!type.IsEmpty())
michael@0 2568 {
michael@0 2569 newData += NS_LITERAL_STRING("type=\"") + type + kCloseAttr;
michael@0 2570 }
michael@0 2571 if (!charset.IsEmpty())
michael@0 2572 {
michael@0 2573 newData += NS_LITERAL_STRING("charset=\"") + charset + kCloseAttr;
michael@0 2574 }
michael@0 2575 if (!alternate.IsEmpty())
michael@0 2576 {
michael@0 2577 newData += NS_LITERAL_STRING("alternate=\"") + alternate + kCloseAttr;
michael@0 2578 }
michael@0 2579 newData.Truncate(newData.Length() - 1); // Remove the extra space on the end.
michael@0 2580 aPI->SetData(newData);
michael@0 2581 }
michael@0 2582
michael@0 2583 return rv;
michael@0 2584 }
michael@0 2585
michael@0 2586 nsresult nsWebBrowserPersist::GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
michael@0 2587 {
michael@0 2588 NS_ENSURE_ARG_POINTER(aPI);
michael@0 2589
michael@0 2590 nsresult rv = NS_OK;
michael@0 2591 nsAutoString data;
michael@0 2592 rv = aPI->GetData(data);
michael@0 2593 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 2594
michael@0 2595 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
michael@0 2596
michael@0 2597 return NS_OK;
michael@0 2598 }
michael@0 2599
michael@0 2600 nsresult nsWebBrowserPersist::OnWalkDOMNode(nsIDOMNode *aNode)
michael@0 2601 {
michael@0 2602 // Fixup xml-stylesheet processing instructions
michael@0 2603 nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNode);
michael@0 2604 if (nodeAsPI)
michael@0 2605 {
michael@0 2606 nsAutoString target;
michael@0 2607 nodeAsPI->GetTarget(target);
michael@0 2608 if (target.EqualsLiteral("xml-stylesheet"))
michael@0 2609 {
michael@0 2610 nsAutoString href;
michael@0 2611 GetXMLStyleSheetLink(nodeAsPI, href);
michael@0 2612 if (!href.IsEmpty())
michael@0 2613 {
michael@0 2614 StoreURI(NS_ConvertUTF16toUTF8(href).get());
michael@0 2615 }
michael@0 2616 }
michael@0 2617 return NS_OK;
michael@0 2618 }
michael@0 2619
michael@0 2620 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
michael@0 2621 if (!content)
michael@0 2622 {
michael@0 2623 return NS_OK;
michael@0 2624 }
michael@0 2625
michael@0 2626 // Test the node to see if it's an image, frame, iframe, css, js
michael@0 2627 nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNode);
michael@0 2628 if (nodeAsImage)
michael@0 2629 {
michael@0 2630 StoreURIAttribute(aNode, "src");
michael@0 2631 return NS_OK;
michael@0 2632 }
michael@0 2633
michael@0 2634 if (content->IsSVG(nsGkAtoms::img))
michael@0 2635 {
michael@0 2636 StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
michael@0 2637 return NS_OK;
michael@0 2638 }
michael@0 2639
michael@0 2640 nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNode);
michael@0 2641 if (nodeAsMedia)
michael@0 2642 {
michael@0 2643 StoreURIAttribute(aNode, "src");
michael@0 2644 return NS_OK;
michael@0 2645 }
michael@0 2646 nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNode);
michael@0 2647 if (nodeAsSource)
michael@0 2648 {
michael@0 2649 StoreURIAttribute(aNode, "src");
michael@0 2650 return NS_OK;
michael@0 2651 }
michael@0 2652
michael@0 2653 if (content->IsHTML(nsGkAtoms::body)) {
michael@0 2654 StoreURIAttribute(aNode, "background");
michael@0 2655 return NS_OK;
michael@0 2656 }
michael@0 2657
michael@0 2658 if (content->IsHTML(nsGkAtoms::table)) {
michael@0 2659 StoreURIAttribute(aNode, "background");
michael@0 2660 return NS_OK;
michael@0 2661 }
michael@0 2662
michael@0 2663 if (content->IsHTML(nsGkAtoms::tr)) {
michael@0 2664 StoreURIAttribute(aNode, "background");
michael@0 2665 return NS_OK;
michael@0 2666 }
michael@0 2667
michael@0 2668 if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
michael@0 2669 StoreURIAttribute(aNode, "background");
michael@0 2670 return NS_OK;
michael@0 2671 }
michael@0 2672
michael@0 2673 nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
michael@0 2674 if (nodeAsScript)
michael@0 2675 {
michael@0 2676 StoreURIAttribute(aNode, "src");
michael@0 2677 return NS_OK;
michael@0 2678 }
michael@0 2679
michael@0 2680 if (content->IsSVG(nsGkAtoms::script))
michael@0 2681 {
michael@0 2682 StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
michael@0 2683 return NS_OK;
michael@0 2684 }
michael@0 2685
michael@0 2686 nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
michael@0 2687 if (nodeAsEmbed)
michael@0 2688 {
michael@0 2689 StoreURIAttribute(aNode, "src");
michael@0 2690 return NS_OK;
michael@0 2691 }
michael@0 2692
michael@0 2693 nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
michael@0 2694 if (nodeAsObject)
michael@0 2695 {
michael@0 2696 StoreURIAttribute(aNode, "data");
michael@0 2697 return NS_OK;
michael@0 2698 }
michael@0 2699
michael@0 2700 nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNode);
michael@0 2701 if (nodeAsApplet)
michael@0 2702 {
michael@0 2703 // For an applet, relative URIs are resolved relative to the
michael@0 2704 // codebase (which is resolved relative to the base URI).
michael@0 2705 nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
michael@0 2706 nsAutoString codebase;
michael@0 2707 nodeAsApplet->GetCodeBase(codebase);
michael@0 2708 if (!codebase.IsEmpty()) {
michael@0 2709 nsCOMPtr<nsIURI> baseURI;
michael@0 2710 NS_NewURI(getter_AddRefs(baseURI), codebase,
michael@0 2711 mCurrentCharset.get(), mCurrentBaseURI);
michael@0 2712 if (baseURI) {
michael@0 2713 mCurrentBaseURI = baseURI;
michael@0 2714 }
michael@0 2715 }
michael@0 2716
michael@0 2717 URIData *archiveURIData = nullptr;
michael@0 2718 StoreURIAttribute(aNode, "archive", true, &archiveURIData);
michael@0 2719 // We only store 'code' locally if there is no 'archive',
michael@0 2720 // otherwise we assume the archive file(s) contains it (bug 430283).
michael@0 2721 if (!archiveURIData)
michael@0 2722 StoreURIAttribute(aNode, "code");
michael@0 2723
michael@0 2724 // restore the base URI we really want to have
michael@0 2725 mCurrentBaseURI = oldBase;
michael@0 2726 return NS_OK;
michael@0 2727 }
michael@0 2728
michael@0 2729 nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNode);
michael@0 2730 if (nodeAsLink)
michael@0 2731 {
michael@0 2732 // Test if the link has a rel value indicating it to be a stylesheet
michael@0 2733 nsAutoString linkRel;
michael@0 2734 if (NS_SUCCEEDED(nodeAsLink->GetRel(linkRel)) && !linkRel.IsEmpty())
michael@0 2735 {
michael@0 2736 nsReadingIterator<char16_t> start;
michael@0 2737 nsReadingIterator<char16_t> end;
michael@0 2738 nsReadingIterator<char16_t> current;
michael@0 2739
michael@0 2740 linkRel.BeginReading(start);
michael@0 2741 linkRel.EndReading(end);
michael@0 2742
michael@0 2743 // Walk through space delimited string looking for "stylesheet"
michael@0 2744 for (current = start; current != end; ++current)
michael@0 2745 {
michael@0 2746 // Ignore whitespace
michael@0 2747 if (nsCRT::IsAsciiSpace(*current))
michael@0 2748 continue;
michael@0 2749
michael@0 2750 // Grab the next space delimited word
michael@0 2751 nsReadingIterator<char16_t> startWord = current;
michael@0 2752 do {
michael@0 2753 ++current;
michael@0 2754 } while (current != end && !nsCRT::IsAsciiSpace(*current));
michael@0 2755
michael@0 2756 // Store the link for fix up if it says "stylesheet"
michael@0 2757 if (Substring(startWord, current)
michael@0 2758 .LowerCaseEqualsLiteral("stylesheet"))
michael@0 2759 {
michael@0 2760 StoreURIAttribute(aNode, "href");
michael@0 2761 return NS_OK;
michael@0 2762 }
michael@0 2763 if (current == end)
michael@0 2764 break;
michael@0 2765 }
michael@0 2766 }
michael@0 2767 return NS_OK;
michael@0 2768 }
michael@0 2769
michael@0 2770 nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
michael@0 2771 if (nodeAsFrame)
michael@0 2772 {
michael@0 2773 URIData *data = nullptr;
michael@0 2774 StoreURIAttribute(aNode, "src", false, &data);
michael@0 2775 if (data)
michael@0 2776 {
michael@0 2777 data->mIsSubFrame = true;
michael@0 2778 // Save the frame content
michael@0 2779 nsCOMPtr<nsIDOMDocument> content;
michael@0 2780 nodeAsFrame->GetContentDocument(getter_AddRefs(content));
michael@0 2781 if (content)
michael@0 2782 {
michael@0 2783 SaveSubframeContent(content, data);
michael@0 2784 }
michael@0 2785 }
michael@0 2786 return NS_OK;
michael@0 2787 }
michael@0 2788
michael@0 2789 nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
michael@0 2790 if (nodeAsIFrame && !(mPersistFlags & PERSIST_FLAGS_IGNORE_IFRAMES))
michael@0 2791 {
michael@0 2792 URIData *data = nullptr;
michael@0 2793 StoreURIAttribute(aNode, "src", false, &data);
michael@0 2794 if (data)
michael@0 2795 {
michael@0 2796 data->mIsSubFrame = true;
michael@0 2797 // Save the frame content
michael@0 2798 nsCOMPtr<nsIDOMDocument> content;
michael@0 2799 nodeAsIFrame->GetContentDocument(getter_AddRefs(content));
michael@0 2800 if (content)
michael@0 2801 {
michael@0 2802 SaveSubframeContent(content, data);
michael@0 2803 }
michael@0 2804 }
michael@0 2805 return NS_OK;
michael@0 2806 }
michael@0 2807
michael@0 2808 nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
michael@0 2809 if (nodeAsInput)
michael@0 2810 {
michael@0 2811 StoreURIAttribute(aNode, "src");
michael@0 2812 return NS_OK;
michael@0 2813 }
michael@0 2814
michael@0 2815 return NS_OK;
michael@0 2816 }
michael@0 2817
michael@0 2818 nsresult
michael@0 2819 nsWebBrowserPersist::GetNodeToFixup(nsIDOMNode *aNodeIn, nsIDOMNode **aNodeOut)
michael@0 2820 {
michael@0 2821 if (!(mPersistFlags & PERSIST_FLAGS_FIXUP_ORIGINAL_DOM))
michael@0 2822 {
michael@0 2823 nsresult rv = aNodeIn->CloneNode(false, 1, aNodeOut);
michael@0 2824 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2825 }
michael@0 2826 else
michael@0 2827 {
michael@0 2828 NS_ADDREF(*aNodeOut = aNodeIn);
michael@0 2829 }
michael@0 2830 nsCOMPtr<nsIDOMHTMLElement> element(do_QueryInterface(*aNodeOut));
michael@0 2831 if (element) {
michael@0 2832 // Make sure this is not XHTML
michael@0 2833 nsAutoString namespaceURI;
michael@0 2834 element->GetNamespaceURI(namespaceURI);
michael@0 2835 if (namespaceURI.IsEmpty()) {
michael@0 2836 // This is a tag-soup node. It may have a _base_href attribute
michael@0 2837 // stuck on it by the parser, but since we're fixing up all URIs
michael@0 2838 // relative to the overall document base that will screw us up.
michael@0 2839 // Just remove the _base_href.
michael@0 2840 element->RemoveAttribute(NS_LITERAL_STRING("_base_href"));
michael@0 2841 }
michael@0 2842 }
michael@0 2843 return NS_OK;
michael@0 2844 }
michael@0 2845
michael@0 2846 nsresult
michael@0 2847 nsWebBrowserPersist::CloneNodeWithFixedUpAttributes(
michael@0 2848 nsIDOMNode *aNodeIn, bool *aSerializeCloneKids, nsIDOMNode **aNodeOut)
michael@0 2849 {
michael@0 2850 nsresult rv;
michael@0 2851 *aNodeOut = nullptr;
michael@0 2852 *aSerializeCloneKids = false;
michael@0 2853
michael@0 2854 // Fixup xml-stylesheet processing instructions
michael@0 2855 nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNodeIn);
michael@0 2856 if (nodeAsPI)
michael@0 2857 {
michael@0 2858 nsAutoString target;
michael@0 2859 nodeAsPI->GetTarget(target);
michael@0 2860 if (target.EqualsLiteral("xml-stylesheet"))
michael@0 2861 {
michael@0 2862 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2863 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2864 {
michael@0 2865 nsCOMPtr<nsIDOMProcessingInstruction> outNode = do_QueryInterface(*aNodeOut);
michael@0 2866 nsAutoString href;
michael@0 2867 GetXMLStyleSheetLink(nodeAsPI, href);
michael@0 2868 if (!href.IsEmpty())
michael@0 2869 {
michael@0 2870 FixupURI(href);
michael@0 2871 FixupXMLStyleSheetLink(outNode, href);
michael@0 2872 }
michael@0 2873 }
michael@0 2874 }
michael@0 2875 }
michael@0 2876
michael@0 2877 // BASE elements are replaced by a comment so relative links are not hosed.
michael@0 2878
michael@0 2879 if (!(mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS))
michael@0 2880 {
michael@0 2881 nsCOMPtr<nsIDOMHTMLBaseElement> nodeAsBase = do_QueryInterface(aNodeIn);
michael@0 2882 if (nodeAsBase)
michael@0 2883 {
michael@0 2884 nsCOMPtr<nsIDOMDocument> ownerDocument;
michael@0 2885 HTMLSharedElement* base = static_cast<HTMLSharedElement*>(nodeAsBase.get());
michael@0 2886 base->GetOwnerDocument(getter_AddRefs(ownerDocument));
michael@0 2887 if (ownerDocument)
michael@0 2888 {
michael@0 2889 nsAutoString href;
michael@0 2890 base->GetHref(href); // Doesn't matter if this fails
michael@0 2891 nsCOMPtr<nsIDOMComment> comment;
michael@0 2892 nsAutoString commentText; commentText.AssignLiteral(" base ");
michael@0 2893 if (!href.IsEmpty())
michael@0 2894 {
michael@0 2895 commentText += NS_LITERAL_STRING("href=\"") + href + NS_LITERAL_STRING("\" ");
michael@0 2896 }
michael@0 2897 rv = ownerDocument->CreateComment(commentText, getter_AddRefs(comment));
michael@0 2898 if (comment)
michael@0 2899 {
michael@0 2900 return CallQueryInterface(comment, aNodeOut);
michael@0 2901 }
michael@0 2902 }
michael@0 2903 }
michael@0 2904 }
michael@0 2905
michael@0 2906 nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
michael@0 2907 if (!content)
michael@0 2908 {
michael@0 2909 return NS_OK;
michael@0 2910 }
michael@0 2911
michael@0 2912 // Fix up href and file links in the elements
michael@0 2913
michael@0 2914 nsCOMPtr<nsIDOMHTMLAnchorElement> nodeAsAnchor = do_QueryInterface(aNodeIn);
michael@0 2915 if (nodeAsAnchor)
michael@0 2916 {
michael@0 2917 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2918 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2919 {
michael@0 2920 FixupAnchor(*aNodeOut);
michael@0 2921 }
michael@0 2922 return rv;
michael@0 2923 }
michael@0 2924
michael@0 2925 nsCOMPtr<nsIDOMHTMLAreaElement> nodeAsArea = do_QueryInterface(aNodeIn);
michael@0 2926 if (nodeAsArea)
michael@0 2927 {
michael@0 2928 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2929 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2930 {
michael@0 2931 FixupAnchor(*aNodeOut);
michael@0 2932 }
michael@0 2933 return rv;
michael@0 2934 }
michael@0 2935
michael@0 2936 if (content->IsHTML(nsGkAtoms::body)) {
michael@0 2937 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2938 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2939 {
michael@0 2940 FixupNodeAttribute(*aNodeOut, "background");
michael@0 2941 }
michael@0 2942 return rv;
michael@0 2943 }
michael@0 2944
michael@0 2945 if (content->IsHTML(nsGkAtoms::table)) {
michael@0 2946 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2947 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2948 {
michael@0 2949 FixupNodeAttribute(*aNodeOut, "background");
michael@0 2950 }
michael@0 2951 return rv;
michael@0 2952 }
michael@0 2953
michael@0 2954 if (content->IsHTML(nsGkAtoms::tr)) {
michael@0 2955 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2956 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2957 {
michael@0 2958 FixupNodeAttribute(*aNodeOut, "background");
michael@0 2959 }
michael@0 2960 return rv;
michael@0 2961 }
michael@0 2962
michael@0 2963 if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
michael@0 2964 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2965 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2966 {
michael@0 2967 FixupNodeAttribute(*aNodeOut, "background");
michael@0 2968 }
michael@0 2969 return rv;
michael@0 2970 }
michael@0 2971
michael@0 2972 nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNodeIn);
michael@0 2973 if (nodeAsImage)
michael@0 2974 {
michael@0 2975 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2976 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2977 {
michael@0 2978 // Disable image loads
michael@0 2979 nsCOMPtr<nsIImageLoadingContent> imgCon =
michael@0 2980 do_QueryInterface(*aNodeOut);
michael@0 2981 if (imgCon)
michael@0 2982 imgCon->SetLoadingEnabled(false);
michael@0 2983
michael@0 2984 FixupAnchor(*aNodeOut);
michael@0 2985 FixupNodeAttribute(*aNodeOut, "src");
michael@0 2986 }
michael@0 2987 return rv;
michael@0 2988 }
michael@0 2989
michael@0 2990 nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNodeIn);
michael@0 2991 if (nodeAsMedia)
michael@0 2992 {
michael@0 2993 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 2994 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 2995 {
michael@0 2996 FixupNodeAttribute(*aNodeOut, "src");
michael@0 2997 }
michael@0 2998
michael@0 2999 return rv;
michael@0 3000 }
michael@0 3001
michael@0 3002 nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNodeIn);
michael@0 3003 if (nodeAsSource)
michael@0 3004 {
michael@0 3005 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3006 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3007 {
michael@0 3008 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3009 }
michael@0 3010
michael@0 3011 return rv;
michael@0 3012 }
michael@0 3013
michael@0 3014 if (content->IsSVG(nsGkAtoms::img))
michael@0 3015 {
michael@0 3016 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3017 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3018 {
michael@0 3019 // Disable image loads
michael@0 3020 nsCOMPtr<nsIImageLoadingContent> imgCon =
michael@0 3021 do_QueryInterface(*aNodeOut);
michael@0 3022 if (imgCon)
michael@0 3023 imgCon->SetLoadingEnabled(false);
michael@0 3024
michael@0 3025 // FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed?
michael@0 3026 FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
michael@0 3027 }
michael@0 3028 return rv;
michael@0 3029 }
michael@0 3030
michael@0 3031 nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNodeIn);
michael@0 3032 if (nodeAsScript)
michael@0 3033 {
michael@0 3034 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3035 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3036 {
michael@0 3037 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3038 }
michael@0 3039 return rv;
michael@0 3040 }
michael@0 3041
michael@0 3042 if (content->IsSVG(nsGkAtoms::script))
michael@0 3043 {
michael@0 3044 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3045 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3046 {
michael@0 3047 FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
michael@0 3048 }
michael@0 3049 return rv;
michael@0 3050 }
michael@0 3051
michael@0 3052 nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNodeIn);
michael@0 3053 if (nodeAsEmbed)
michael@0 3054 {
michael@0 3055 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3056 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3057 {
michael@0 3058 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3059 }
michael@0 3060 return rv;
michael@0 3061 }
michael@0 3062
michael@0 3063 nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNodeIn);
michael@0 3064 if (nodeAsObject)
michael@0 3065 {
michael@0 3066 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3067 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3068 {
michael@0 3069 FixupNodeAttribute(*aNodeOut, "data");
michael@0 3070 }
michael@0 3071 return rv;
michael@0 3072 }
michael@0 3073
michael@0 3074 nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNodeIn);
michael@0 3075 if (nodeAsApplet)
michael@0 3076 {
michael@0 3077 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3078 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3079 {
michael@0 3080 nsCOMPtr<nsIDOMHTMLAppletElement> newApplet =
michael@0 3081 do_QueryInterface(*aNodeOut);
michael@0 3082 // For an applet, relative URIs are resolved relative to the
michael@0 3083 // codebase (which is resolved relative to the base URI).
michael@0 3084 nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
michael@0 3085 nsAutoString codebase;
michael@0 3086 nodeAsApplet->GetCodeBase(codebase);
michael@0 3087 if (!codebase.IsEmpty()) {
michael@0 3088 nsCOMPtr<nsIURI> baseURI;
michael@0 3089 NS_NewURI(getter_AddRefs(baseURI), codebase,
michael@0 3090 mCurrentCharset.get(), mCurrentBaseURI);
michael@0 3091 if (baseURI) {
michael@0 3092 mCurrentBaseURI = baseURI;
michael@0 3093 }
michael@0 3094 }
michael@0 3095 // Unset the codebase too, since we'll correctly relativize the
michael@0 3096 // code and archive paths.
michael@0 3097 static_cast<HTMLSharedObjectElement*>(newApplet.get())->
michael@0 3098 RemoveAttribute(NS_LITERAL_STRING("codebase"));
michael@0 3099 FixupNodeAttribute(*aNodeOut, "code");
michael@0 3100 FixupNodeAttribute(*aNodeOut, "archive");
michael@0 3101 // restore the base URI we really want to have
michael@0 3102 mCurrentBaseURI = oldBase;
michael@0 3103 }
michael@0 3104 return rv;
michael@0 3105 }
michael@0 3106
michael@0 3107 nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNodeIn);
michael@0 3108 if (nodeAsLink)
michael@0 3109 {
michael@0 3110 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3111 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3112 {
michael@0 3113 // First see if the link represents linked content
michael@0 3114 rv = FixupNodeAttribute(*aNodeOut, "href");
michael@0 3115 if (NS_FAILED(rv))
michael@0 3116 {
michael@0 3117 // Perhaps this link is actually an anchor to related content
michael@0 3118 FixupAnchor(*aNodeOut);
michael@0 3119 }
michael@0 3120 // TODO if "type" attribute == "text/css"
michael@0 3121 // fixup stylesheet
michael@0 3122 }
michael@0 3123 return rv;
michael@0 3124 }
michael@0 3125
michael@0 3126 nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNodeIn);
michael@0 3127 if (nodeAsFrame)
michael@0 3128 {
michael@0 3129 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3130 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3131 {
michael@0 3132 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3133 }
michael@0 3134 return rv;
michael@0 3135 }
michael@0 3136
michael@0 3137 nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNodeIn);
michael@0 3138 if (nodeAsIFrame)
michael@0 3139 {
michael@0 3140 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3141 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3142 {
michael@0 3143 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3144 }
michael@0 3145 return rv;
michael@0 3146 }
michael@0 3147
michael@0 3148 nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNodeIn);
michael@0 3149 if (nodeAsInput)
michael@0 3150 {
michael@0 3151 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3152 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3153 {
michael@0 3154 // Disable image loads
michael@0 3155 nsCOMPtr<nsIImageLoadingContent> imgCon =
michael@0 3156 do_QueryInterface(*aNodeOut);
michael@0 3157 if (imgCon)
michael@0 3158 imgCon->SetLoadingEnabled(false);
michael@0 3159
michael@0 3160 FixupNodeAttribute(*aNodeOut, "src");
michael@0 3161
michael@0 3162 nsAutoString valueStr;
michael@0 3163 NS_NAMED_LITERAL_STRING(valueAttr, "value");
michael@0 3164 // Update element node attributes with user-entered form state
michael@0 3165 nsCOMPtr<nsIContent> content = do_QueryInterface(*aNodeOut);
michael@0 3166 nsRefPtr<HTMLInputElement> outElt =
michael@0 3167 HTMLInputElement::FromContentOrNull(content);
michael@0 3168 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
michael@0 3169 switch (formControl->GetType()) {
michael@0 3170 case NS_FORM_INPUT_EMAIL:
michael@0 3171 case NS_FORM_INPUT_SEARCH:
michael@0 3172 case NS_FORM_INPUT_TEXT:
michael@0 3173 case NS_FORM_INPUT_TEL:
michael@0 3174 case NS_FORM_INPUT_URL:
michael@0 3175 case NS_FORM_INPUT_NUMBER:
michael@0 3176 case NS_FORM_INPUT_RANGE:
michael@0 3177 case NS_FORM_INPUT_DATE:
michael@0 3178 case NS_FORM_INPUT_TIME:
michael@0 3179 case NS_FORM_INPUT_COLOR:
michael@0 3180 nodeAsInput->GetValue(valueStr);
michael@0 3181 // Avoid superfluous value="" serialization
michael@0 3182 if (valueStr.IsEmpty())
michael@0 3183 outElt->RemoveAttribute(valueAttr);
michael@0 3184 else
michael@0 3185 outElt->SetAttribute(valueAttr, valueStr);
michael@0 3186 break;
michael@0 3187 case NS_FORM_INPUT_CHECKBOX:
michael@0 3188 case NS_FORM_INPUT_RADIO:
michael@0 3189 bool checked;
michael@0 3190 nodeAsInput->GetChecked(&checked);
michael@0 3191 outElt->SetDefaultChecked(checked);
michael@0 3192 break;
michael@0 3193 default:
michael@0 3194 break;
michael@0 3195 }
michael@0 3196 }
michael@0 3197 return rv;
michael@0 3198 }
michael@0 3199
michael@0 3200 nsCOMPtr<nsIDOMHTMLTextAreaElement> nodeAsTextArea = do_QueryInterface(aNodeIn);
michael@0 3201 if (nodeAsTextArea)
michael@0 3202 {
michael@0 3203 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3204 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3205 {
michael@0 3206 // Tell the document encoder to serialize the text child we create below
michael@0 3207 *aSerializeCloneKids = true;
michael@0 3208
michael@0 3209 nsAutoString valueStr;
michael@0 3210 nodeAsTextArea->GetValue(valueStr);
michael@0 3211
michael@0 3212 (*aNodeOut)->SetTextContent(valueStr);
michael@0 3213 }
michael@0 3214 return rv;
michael@0 3215 }
michael@0 3216
michael@0 3217 nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
michael@0 3218 if (nodeAsOption)
michael@0 3219 {
michael@0 3220 rv = GetNodeToFixup(aNodeIn, aNodeOut);
michael@0 3221 if (NS_SUCCEEDED(rv) && *aNodeOut)
michael@0 3222 {
michael@0 3223 nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
michael@0 3224 bool selected;
michael@0 3225 nodeAsOption->GetSelected(&selected);
michael@0 3226 outElt->SetDefaultSelected(selected);
michael@0 3227 }
michael@0 3228 return rv;
michael@0 3229 }
michael@0 3230
michael@0 3231 return NS_OK;
michael@0 3232 }
michael@0 3233
michael@0 3234 nsresult
michael@0 3235 nsWebBrowserPersist::StoreURI(
michael@0 3236 const char *aURI, bool aNeedsPersisting, URIData **aData)
michael@0 3237 {
michael@0 3238 NS_ENSURE_ARG_POINTER(aURI);
michael@0 3239
michael@0 3240 nsCOMPtr<nsIURI> uri;
michael@0 3241 nsresult rv = NS_NewURI(getter_AddRefs(uri),
michael@0 3242 nsDependentCString(aURI),
michael@0 3243 mCurrentCharset.get(),
michael@0 3244 mCurrentBaseURI);
michael@0 3245 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3246
michael@0 3247 return StoreURI(uri, aNeedsPersisting, aData);
michael@0 3248 }
michael@0 3249
michael@0 3250 nsresult
michael@0 3251 nsWebBrowserPersist::StoreURI(
michael@0 3252 nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
michael@0 3253 {
michael@0 3254 NS_ENSURE_ARG_POINTER(aURI);
michael@0 3255 if (aData)
michael@0 3256 {
michael@0 3257 *aData = nullptr;
michael@0 3258 }
michael@0 3259
michael@0 3260 // Test if this URI should be persisted. By default
michael@0 3261 // we should assume the URI is persistable.
michael@0 3262 bool doNotPersistURI;
michael@0 3263 nsresult rv = NS_URIChainHasFlags(aURI,
michael@0 3264 nsIProtocolHandler::URI_NON_PERSISTABLE,
michael@0 3265 &doNotPersistURI);
michael@0 3266 if (NS_FAILED(rv))
michael@0 3267 {
michael@0 3268 doNotPersistURI = false;
michael@0 3269 }
michael@0 3270
michael@0 3271 if (doNotPersistURI)
michael@0 3272 {
michael@0 3273 return NS_OK;
michael@0 3274 }
michael@0 3275
michael@0 3276 URIData *data = nullptr;
michael@0 3277 MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
michael@0 3278 if (aData)
michael@0 3279 {
michael@0 3280 *aData = data;
michael@0 3281 }
michael@0 3282
michael@0 3283 return NS_OK;
michael@0 3284 }
michael@0 3285
michael@0 3286 nsresult
michael@0 3287 nsWebBrowserPersist::StoreURIAttributeNS(
michael@0 3288 nsIDOMNode *aNode, const char *aNamespaceURI, const char *aAttribute,
michael@0 3289 bool aNeedsPersisting, URIData **aData)
michael@0 3290 {
michael@0 3291 NS_ENSURE_ARG_POINTER(aNode);
michael@0 3292 NS_ENSURE_ARG_POINTER(aNamespaceURI);
michael@0 3293 NS_ENSURE_ARG_POINTER(aAttribute);
michael@0 3294
michael@0 3295 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
michael@0 3296 MOZ_ASSERT(element);
michael@0 3297
michael@0 3298 // Find the named URI attribute on the (element) node and store
michael@0 3299 // a reference to the URI that maps onto a local file name
michael@0 3300
michael@0 3301 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
michael@0 3302 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
michael@0 3303 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3304
michael@0 3305 NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
michael@0 3306 NS_ConvertASCIItoUTF16 attribute(aAttribute);
michael@0 3307 nsCOMPtr<nsIDOMAttr> attr;
michael@0 3308 rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
michael@0 3309 if (attr)
michael@0 3310 {
michael@0 3311 nsAutoString oldValue;
michael@0 3312 attr->GetValue(oldValue);
michael@0 3313 if (!oldValue.IsEmpty())
michael@0 3314 {
michael@0 3315 NS_ConvertUTF16toUTF8 oldCValue(oldValue);
michael@0 3316 return StoreURI(oldCValue.get(), aNeedsPersisting, aData);
michael@0 3317 }
michael@0 3318 }
michael@0 3319
michael@0 3320 return NS_OK;
michael@0 3321 }
michael@0 3322
michael@0 3323 nsresult
michael@0 3324 nsWebBrowserPersist::FixupURI(nsAString &aURI)
michael@0 3325 {
michael@0 3326 // get the current location of the file (absolutized)
michael@0 3327 nsCOMPtr<nsIURI> uri;
michael@0 3328 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
michael@0 3329 mCurrentCharset.get(), mCurrentBaseURI);
michael@0 3330 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3331 nsAutoCString spec;
michael@0 3332 rv = uri->GetSpec(spec);
michael@0 3333 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3334
michael@0 3335 // Search for the URI in the map and replace it with the local file
michael@0 3336 if (!mURIMap.Contains(spec))
michael@0 3337 {
michael@0 3338 return NS_ERROR_FAILURE;
michael@0 3339 }
michael@0 3340 URIData *data = mURIMap.Get(spec);
michael@0 3341 if (!data->mNeedsFixup)
michael@0 3342 {
michael@0 3343 return NS_OK;
michael@0 3344 }
michael@0 3345 nsCOMPtr<nsIURI> fileAsURI;
michael@0 3346 if (data->mFile)
michael@0 3347 {
michael@0 3348 rv = data->mFile->Clone(getter_AddRefs(fileAsURI));
michael@0 3349 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3350 }
michael@0 3351 else
michael@0 3352 {
michael@0 3353 rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
michael@0 3354 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3355 rv = AppendPathToURI(fileAsURI, data->mFilename);
michael@0 3356 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3357 }
michael@0 3358 nsAutoString newValue;
michael@0 3359
michael@0 3360 // remove username/password if present
michael@0 3361 fileAsURI->SetUserPass(EmptyCString());
michael@0 3362
michael@0 3363 // reset node attribute
michael@0 3364 // Use relative or absolute links
michael@0 3365 if (data->mDataPathIsRelative)
michael@0 3366 {
michael@0 3367 nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
michael@0 3368 if (!url)
michael@0 3369 return NS_ERROR_FAILURE;
michael@0 3370
michael@0 3371 nsAutoCString filename;
michael@0 3372 url->GetFileName(filename);
michael@0 3373
michael@0 3374 nsAutoCString rawPathURL(data->mRelativePathToData);
michael@0 3375 rawPathURL.Append(filename);
michael@0 3376
michael@0 3377 nsAutoCString buf;
michael@0 3378 AppendUTF8toUTF16(NS_EscapeURL(rawPathURL, esc_FilePath, buf),
michael@0 3379 newValue);
michael@0 3380 }
michael@0 3381 else
michael@0 3382 {
michael@0 3383 nsAutoCString fileurl;
michael@0 3384 fileAsURI->GetSpec(fileurl);
michael@0 3385 AppendUTF8toUTF16(fileurl, newValue);
michael@0 3386 }
michael@0 3387 if (data->mIsSubFrame)
michael@0 3388 {
michael@0 3389 newValue.Append(data->mSubFrameExt);
michael@0 3390 }
michael@0 3391
michael@0 3392 aURI = newValue;
michael@0 3393 return NS_OK;
michael@0 3394 }
michael@0 3395
michael@0 3396 nsresult
michael@0 3397 nsWebBrowserPersist::FixupNodeAttributeNS(nsIDOMNode *aNode,
michael@0 3398 const char *aNamespaceURI,
michael@0 3399 const char *aAttribute)
michael@0 3400 {
michael@0 3401 NS_ENSURE_ARG_POINTER(aNode);
michael@0 3402 NS_ENSURE_ARG_POINTER(aNamespaceURI);
michael@0 3403 NS_ENSURE_ARG_POINTER(aAttribute);
michael@0 3404
michael@0 3405 // Find the named URI attribute on the (element) node and change it to reference
michael@0 3406 // a local file.
michael@0 3407
michael@0 3408 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
michael@0 3409 MOZ_ASSERT(element);
michael@0 3410
michael@0 3411 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
michael@0 3412 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
michael@0 3413 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3414
michael@0 3415 NS_ConvertASCIItoUTF16 attribute(aAttribute);
michael@0 3416 NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
michael@0 3417 nsCOMPtr<nsIDOMAttr> attr;
michael@0 3418 rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
michael@0 3419 if (attr) {
michael@0 3420 nsString uri;
michael@0 3421 attr->GetValue(uri);
michael@0 3422 rv = FixupURI(uri);
michael@0 3423 if (NS_SUCCEEDED(rv))
michael@0 3424 {
michael@0 3425 attr->SetValue(uri);
michael@0 3426 }
michael@0 3427 }
michael@0 3428
michael@0 3429 return rv;
michael@0 3430 }
michael@0 3431
michael@0 3432 nsresult
michael@0 3433 nsWebBrowserPersist::FixupAnchor(nsIDOMNode *aNode)
michael@0 3434 {
michael@0 3435 NS_ENSURE_ARG_POINTER(aNode);
michael@0 3436
michael@0 3437 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
michael@0 3438 MOZ_ASSERT(element);
michael@0 3439
michael@0 3440 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
michael@0 3441 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
michael@0 3442 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3443
michael@0 3444 if (mPersistFlags & PERSIST_FLAGS_DONT_FIXUP_LINKS)
michael@0 3445 {
michael@0 3446 return NS_OK;
michael@0 3447 }
michael@0 3448
michael@0 3449 // Make all anchor links absolute so they point off onto the Internet
michael@0 3450 nsString attribute(NS_LITERAL_STRING("href"));
michael@0 3451 nsCOMPtr<nsIDOMAttr> attr;
michael@0 3452 rv = attrMap->GetNamedItem(attribute, getter_AddRefs(attr));
michael@0 3453 if (attr)
michael@0 3454 {
michael@0 3455 nsString oldValue;
michael@0 3456 attr->GetValue(oldValue);
michael@0 3457 NS_ConvertUTF16toUTF8 oldCValue(oldValue);
michael@0 3458
michael@0 3459 // Skip empty values and self-referencing bookmarks
michael@0 3460 if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#')
michael@0 3461 {
michael@0 3462 return NS_OK;
michael@0 3463 }
michael@0 3464
michael@0 3465 // if saving file to same location, we don't need to do any fixup
michael@0 3466 bool isEqual = false;
michael@0 3467 if (NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual))
michael@0 3468 && isEqual)
michael@0 3469 {
michael@0 3470 return NS_OK;
michael@0 3471 }
michael@0 3472
michael@0 3473 nsCOMPtr<nsIURI> relativeURI;
michael@0 3474 relativeURI = (mPersistFlags & PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
michael@0 3475 ? mTargetBaseURI : mCurrentBaseURI;
michael@0 3476 // Make a new URI to replace the current one
michael@0 3477 nsCOMPtr<nsIURI> newURI;
michael@0 3478 rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
michael@0 3479 mCurrentCharset.get(), relativeURI);
michael@0 3480 if (NS_SUCCEEDED(rv) && newURI)
michael@0 3481 {
michael@0 3482 newURI->SetUserPass(EmptyCString());
michael@0 3483 nsAutoCString uriSpec;
michael@0 3484 newURI->GetSpec(uriSpec);
michael@0 3485 attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec));
michael@0 3486 }
michael@0 3487 }
michael@0 3488
michael@0 3489 return NS_OK;
michael@0 3490 }
michael@0 3491
michael@0 3492 nsresult
michael@0 3493 nsWebBrowserPersist::StoreAndFixupStyleSheet(nsIStyleSheet *aStyleSheet)
michael@0 3494 {
michael@0 3495 // TODO go through the style sheet fixing up all links
michael@0 3496 return NS_OK;
michael@0 3497 }
michael@0 3498
michael@0 3499 bool
michael@0 3500 nsWebBrowserPersist::DocumentEncoderExists(const char16_t *aContentType)
michael@0 3501 {
michael@0 3502 // Check if there is an encoder for the desired content type.
michael@0 3503 nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
michael@0 3504 AppendUTF16toUTF8(aContentType, contractID);
michael@0 3505
michael@0 3506 nsCOMPtr<nsIComponentRegistrar> registrar;
michael@0 3507 NS_GetComponentRegistrar(getter_AddRefs(registrar));
michael@0 3508 if (registrar)
michael@0 3509 {
michael@0 3510 bool result;
michael@0 3511 nsresult rv = registrar->IsContractIDRegistered(contractID.get(),
michael@0 3512 &result);
michael@0 3513 if (NS_SUCCEEDED(rv) && result)
michael@0 3514 {
michael@0 3515 return true;
michael@0 3516 }
michael@0 3517 }
michael@0 3518 return false;
michael@0 3519 }
michael@0 3520
michael@0 3521 nsresult
michael@0 3522 nsWebBrowserPersist::SaveSubframeContent(
michael@0 3523 nsIDOMDocument *aFrameContent, URIData *aData)
michael@0 3524 {
michael@0 3525 NS_ENSURE_ARG_POINTER(aData);
michael@0 3526
michael@0 3527 // Extract the content type for the frame's contents.
michael@0 3528 nsCOMPtr<nsIDocument> frameDoc(do_QueryInterface(aFrameContent));
michael@0 3529 NS_ENSURE_STATE(frameDoc);
michael@0 3530
michael@0 3531 nsAutoString contentType;
michael@0 3532 nsresult rv = frameDoc->GetContentType(contentType);
michael@0 3533 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3534
michael@0 3535 nsXPIDLString ext;
michael@0 3536 GetExtensionForContentType(contentType.get(), getter_Copies(ext));
michael@0 3537
michael@0 3538 // We must always have an extension so we will try to re-assign
michael@0 3539 // the original extension if GetExtensionForContentType fails.
michael@0 3540 if (ext.IsEmpty())
michael@0 3541 {
michael@0 3542 nsCOMPtr<nsIURL> url(do_QueryInterface(frameDoc->GetDocumentURI(),
michael@0 3543 &rv));
michael@0 3544 nsAutoCString extension;
michael@0 3545 if (NS_SUCCEEDED(rv))
michael@0 3546 {
michael@0 3547 url->GetFileExtension(extension);
michael@0 3548 }
michael@0 3549 else
michael@0 3550 {
michael@0 3551 extension.AssignLiteral("htm");
michael@0 3552 }
michael@0 3553 aData->mSubFrameExt.Assign(char16_t('.'));
michael@0 3554 AppendUTF8toUTF16(extension, aData->mSubFrameExt);
michael@0 3555 }
michael@0 3556 else
michael@0 3557 {
michael@0 3558 aData->mSubFrameExt.Assign(char16_t('.'));
michael@0 3559 aData->mSubFrameExt.Append(ext);
michael@0 3560 }
michael@0 3561
michael@0 3562 nsString filenameWithExt = aData->mFilename;
michael@0 3563 filenameWithExt.Append(aData->mSubFrameExt);
michael@0 3564
michael@0 3565 // Work out the path for the subframe
michael@0 3566 nsCOMPtr<nsIURI> frameURI;
michael@0 3567 rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
michael@0 3568 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3569 rv = AppendPathToURI(frameURI, filenameWithExt);
michael@0 3570 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3571
michael@0 3572 // Work out the path for the subframe data
michael@0 3573 nsCOMPtr<nsIURI> frameDataURI;
michael@0 3574 rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
michael@0 3575 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3576 nsAutoString newFrameDataPath(aData->mFilename);
michael@0 3577
michael@0 3578 // Append _data
michael@0 3579 newFrameDataPath.AppendLiteral("_data");
michael@0 3580 rv = AppendPathToURI(frameDataURI, newFrameDataPath);
michael@0 3581 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3582
michael@0 3583 // Make frame document & data path conformant and unique
michael@0 3584 rv = CalculateUniqueFilename(frameURI);
michael@0 3585 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3586 rv = CalculateUniqueFilename(frameDataURI);
michael@0 3587 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3588
michael@0 3589 mCurrentThingsToPersist++;
michael@0 3590
michael@0 3591 // We shouldn't use SaveDocumentInternal for the contents
michael@0 3592 // of frames that are not documents, e.g. images.
michael@0 3593 if (DocumentEncoderExists(contentType.get()))
michael@0 3594 {
michael@0 3595 rv = SaveDocumentInternal(aFrameContent, frameURI, frameDataURI);
michael@0 3596 }
michael@0 3597 else
michael@0 3598 {
michael@0 3599 rv = StoreURI(frameDoc->GetDocumentURI());
michael@0 3600 }
michael@0 3601 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3602
michael@0 3603 // Store the updated uri to the frame
michael@0 3604 aData->mFile = frameURI;
michael@0 3605 aData->mSubFrameExt.Truncate(); // we already put this in frameURI
michael@0 3606
michael@0 3607 return NS_OK;
michael@0 3608 }
michael@0 3609
michael@0 3610 nsresult
michael@0 3611 nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
michael@0 3612 {
michael@0 3613 nsresult rv = NS_OK;
michael@0 3614 *aChannel = nullptr;
michael@0 3615
michael@0 3616 nsCOMPtr<nsIIOService> ioserv;
michael@0 3617 ioserv = do_GetIOService(&rv);
michael@0 3618 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3619
michael@0 3620 rv = ioserv->NewChannelFromURI(aURI, aChannel);
michael@0 3621 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3622 NS_ENSURE_ARG_POINTER(*aChannel);
michael@0 3623
michael@0 3624 rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
michael@0 3625 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3626 return NS_OK;
michael@0 3627 }
michael@0 3628
michael@0 3629 nsresult
michael@0 3630 nsWebBrowserPersist::SaveDocumentWithFixup(
michael@0 3631 nsIDOMDocument *aDocument, nsIDocumentEncoderNodeFixup *aNodeFixup,
michael@0 3632 nsIURI *aFile, bool aReplaceExisting, const nsACString &aFormatType,
michael@0 3633 const nsCString &aSaveCharset, uint32_t aFlags)
michael@0 3634 {
michael@0 3635 NS_ENSURE_ARG_POINTER(aFile);
michael@0 3636
michael@0 3637 nsresult rv = NS_OK;
michael@0 3638 nsCOMPtr<nsIFile> localFile;
michael@0 3639 GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
michael@0 3640 if (localFile)
michael@0 3641 {
michael@0 3642 // if we're not replacing an existing file but the file
michael@0 3643 // exists, something is wrong
michael@0 3644 bool fileExists = false;
michael@0 3645 rv = localFile->Exists(&fileExists);
michael@0 3646 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3647
michael@0 3648 if (!aReplaceExisting && fileExists)
michael@0 3649 return NS_ERROR_FAILURE; // where are the file I/O errors?
michael@0 3650 }
michael@0 3651
michael@0 3652 nsCOMPtr<nsIOutputStream> outputStream;
michael@0 3653 rv = MakeOutputStream(aFile, getter_AddRefs(outputStream));
michael@0 3654 if (NS_FAILED(rv))
michael@0 3655 {
michael@0 3656 SendErrorStatusChange(false, rv, nullptr, aFile);
michael@0 3657 return NS_ERROR_FAILURE;
michael@0 3658 }
michael@0 3659 NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
michael@0 3660
michael@0 3661 // Get a document encoder instance
michael@0 3662 nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
michael@0 3663 contractID.Append(aFormatType);
michael@0 3664
michael@0 3665 nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(contractID.get(), &rv);
michael@0 3666 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3667
michael@0 3668 NS_ConvertASCIItoUTF16 newContentType(aFormatType);
michael@0 3669 rv = encoder->Init(aDocument, newContentType, aFlags);
michael@0 3670 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3671
michael@0 3672 mTargetBaseURI = aFile;
michael@0 3673
michael@0 3674 // Set the node fixup callback
michael@0 3675 encoder->SetNodeFixup(aNodeFixup);
michael@0 3676
michael@0 3677 if (mWrapColumn && (aFlags & ENCODE_FLAGS_WRAP))
michael@0 3678 encoder->SetWrapColumn(mWrapColumn);
michael@0 3679
michael@0 3680 nsAutoCString charsetStr(aSaveCharset);
michael@0 3681 if (charsetStr.IsEmpty())
michael@0 3682 {
michael@0 3683 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
michael@0 3684 NS_ASSERTION(doc, "Need a document");
michael@0 3685 charsetStr = doc->GetDocumentCharacterSet();
michael@0 3686 }
michael@0 3687
michael@0 3688 rv = encoder->SetCharset(charsetStr);
michael@0 3689 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3690
michael@0 3691 rv = encoder->EncodeToStream(outputStream);
michael@0 3692 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3693
michael@0 3694 if (!localFile)
michael@0 3695 {
michael@0 3696 nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(outputStream));
michael@0 3697 if (storStream)
michael@0 3698 {
michael@0 3699 outputStream->Close();
michael@0 3700 rv = StartUpload(storStream, aFile, aFormatType);
michael@0 3701 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3702 }
michael@0 3703 }
michael@0 3704
michael@0 3705 return rv;
michael@0 3706 }
michael@0 3707
michael@0 3708
michael@0 3709 // we store the current location as the key (absolutized version of domnode's attribute's value)
michael@0 3710 nsresult
michael@0 3711 nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
michael@0 3712 nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
michael@0 3713 {
michael@0 3714 NS_ENSURE_ARG_POINTER(aURI);
michael@0 3715
michael@0 3716 nsAutoCString spec;
michael@0 3717 nsresult rv = aURI->GetSpec(spec);
michael@0 3718 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3719
michael@0 3720 // Create a sensibly named filename for the URI and store in the URI map
michael@0 3721 URIData *data;
michael@0 3722 if (mURIMap.Contains(spec))
michael@0 3723 {
michael@0 3724 data = mURIMap.Get(spec);
michael@0 3725 if (aNeedsPersisting)
michael@0 3726 {
michael@0 3727 data->mNeedsPersisting = true;
michael@0 3728 }
michael@0 3729 if (aData)
michael@0 3730 {
michael@0 3731 *aData = data;
michael@0 3732 }
michael@0 3733 return NS_OK;
michael@0 3734 }
michael@0 3735
michael@0 3736 // Create a unique file name for the uri
michael@0 3737 nsString filename;
michael@0 3738 rv = MakeFilenameFromURI(aURI, filename);
michael@0 3739 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 3740
michael@0 3741 // Store the file name
michael@0 3742 data = new URIData;
michael@0 3743 NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
michael@0 3744
michael@0 3745 data->mNeedsPersisting = aNeedsPersisting;
michael@0 3746 data->mNeedsFixup = true;
michael@0 3747 data->mFilename = filename;
michael@0 3748 data->mSaved = false;
michael@0 3749 data->mIsSubFrame = false;
michael@0 3750 data->mDataPath = mCurrentDataPath;
michael@0 3751 data->mDataPathIsRelative = mCurrentDataPathIsRelative;
michael@0 3752 data->mRelativePathToData = mCurrentRelativePathToData;
michael@0 3753 data->mCharset = mCurrentCharset;
michael@0 3754
michael@0 3755 if (aNeedsPersisting)
michael@0 3756 mCurrentThingsToPersist++;
michael@0 3757
michael@0 3758 mURIMap.Put(spec, data);
michael@0 3759 if (aData)
michael@0 3760 {
michael@0 3761 *aData = data;
michael@0 3762 }
michael@0 3763
michael@0 3764 return NS_OK;
michael@0 3765 }
michael@0 3766
michael@0 3767 // Ordered so that typical documents work fastest.
michael@0 3768 // strlen("blockquote")==10
michael@0 3769 static const char kSpecialXHTMLTags[][11] = {
michael@0 3770 "body",
michael@0 3771 "head",
michael@0 3772 "img",
michael@0 3773 "script",
michael@0 3774 "a",
michael@0 3775 "area",
michael@0 3776 "link",
michael@0 3777 "input",
michael@0 3778 "frame",
michael@0 3779 "iframe",
michael@0 3780 "object",
michael@0 3781 "applet",
michael@0 3782 "form",
michael@0 3783 "blockquote",
michael@0 3784 "q",
michael@0 3785 "del",
michael@0 3786 "ins"
michael@0 3787 };
michael@0 3788
michael@0 3789 static bool IsSpecialXHTMLTag(nsIDOMNode *aNode)
michael@0 3790 {
michael@0 3791 nsAutoString tmp;
michael@0 3792 aNode->GetNamespaceURI(tmp);
michael@0 3793 if (!tmp.EqualsLiteral("http://www.w3.org/1999/xhtml"))
michael@0 3794 return false;
michael@0 3795
michael@0 3796 aNode->GetLocalName(tmp);
michael@0 3797 for (uint32_t i = 0; i < ArrayLength(kSpecialXHTMLTags); i++) {
michael@0 3798 if (tmp.EqualsASCII(kSpecialXHTMLTags[i]))
michael@0 3799 {
michael@0 3800 // XXX This element MAY have URI attributes, but
michael@0 3801 // we are not actually checking if they are present.
michael@0 3802 // That would slow us down further, and I am not so sure
michael@0 3803 // how important that would be.
michael@0 3804 return true;
michael@0 3805 }
michael@0 3806 }
michael@0 3807
michael@0 3808 return false;
michael@0 3809 }
michael@0 3810
michael@0 3811 static bool HasSpecialXHTMLTags(nsIDOMNode *aParent)
michael@0 3812 {
michael@0 3813 if (IsSpecialXHTMLTag(aParent))
michael@0 3814 return true;
michael@0 3815
michael@0 3816 nsCOMPtr<nsIDOMNodeList> list;
michael@0 3817 aParent->GetChildNodes(getter_AddRefs(list));
michael@0 3818 if (list)
michael@0 3819 {
michael@0 3820 uint32_t count;
michael@0 3821 list->GetLength(&count);
michael@0 3822 uint32_t i;
michael@0 3823 for (i = 0; i < count; i++) {
michael@0 3824 nsCOMPtr<nsIDOMNode> node;
michael@0 3825 list->Item(i, getter_AddRefs(node));
michael@0 3826 if (!node)
michael@0 3827 break;
michael@0 3828 uint16_t nodeType;
michael@0 3829 node->GetNodeType(&nodeType);
michael@0 3830 if (nodeType == nsIDOMNode::ELEMENT_NODE) {
michael@0 3831 return HasSpecialXHTMLTags(node);
michael@0 3832 }
michael@0 3833 }
michael@0 3834 }
michael@0 3835
michael@0 3836 return false;
michael@0 3837 }
michael@0 3838
michael@0 3839 static bool NeedXHTMLBaseTag(nsIDOMDocument *aDocument)
michael@0 3840 {
michael@0 3841 nsCOMPtr<nsIDOMElement> docElement;
michael@0 3842 aDocument->GetDocumentElement(getter_AddRefs(docElement));
michael@0 3843
michael@0 3844 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(docElement));
michael@0 3845 if (node)
michael@0 3846 {
michael@0 3847 return HasSpecialXHTMLTags(node);
michael@0 3848 }
michael@0 3849
michael@0 3850 return false;
michael@0 3851 }
michael@0 3852
michael@0 3853 // Set document base. This could create an invalid XML document (still well-formed).
michael@0 3854 nsresult
michael@0 3855 nsWebBrowserPersist::SetDocumentBase(
michael@0 3856 nsIDOMDocument *aDocument, nsIURI *aBaseURI)
michael@0 3857 {
michael@0 3858 if (mPersistFlags & PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS)
michael@0 3859 {
michael@0 3860 return NS_OK;
michael@0 3861 }
michael@0 3862
michael@0 3863 NS_ENSURE_ARG_POINTER(aBaseURI);
michael@0 3864
michael@0 3865 nsCOMPtr<nsIDOMXMLDocument> xmlDoc;
michael@0 3866 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
michael@0 3867 if (!htmlDoc)
michael@0 3868 {
michael@0 3869 xmlDoc = do_QueryInterface(aDocument);
michael@0 3870 if (!xmlDoc)
michael@0 3871 {
michael@0 3872 return NS_ERROR_FAILURE;
michael@0 3873 }
michael@0 3874 }
michael@0 3875
michael@0 3876 NS_NAMED_LITERAL_STRING(kXHTMLNS, "http://www.w3.org/1999/xhtml");
michael@0 3877 NS_NAMED_LITERAL_STRING(kHead, "head");
michael@0 3878
michael@0 3879 // Find the head element
michael@0 3880 nsCOMPtr<nsIDOMElement> headElement;
michael@0 3881 nsCOMPtr<nsIDOMNodeList> headList;
michael@0 3882 if (xmlDoc)
michael@0 3883 {
michael@0 3884 // First see if there is XHTML content that needs base
michael@0 3885 // tags.
michael@0 3886 if (!NeedXHTMLBaseTag(aDocument))
michael@0 3887 return NS_OK;
michael@0 3888
michael@0 3889 aDocument->GetElementsByTagNameNS(
michael@0 3890 kXHTMLNS,
michael@0 3891 kHead, getter_AddRefs(headList));
michael@0 3892 }
michael@0 3893 else
michael@0 3894 {
michael@0 3895 aDocument->GetElementsByTagName(
michael@0 3896 kHead, getter_AddRefs(headList));
michael@0 3897 }
michael@0 3898 if (headList)
michael@0 3899 {
michael@0 3900 nsCOMPtr<nsIDOMNode> headNode;
michael@0 3901 headList->Item(0, getter_AddRefs(headNode));
michael@0 3902 headElement = do_QueryInterface(headNode);
michael@0 3903 }
michael@0 3904 if (!headElement)
michael@0 3905 {
michael@0 3906 // Create head and insert as first element
michael@0 3907 nsCOMPtr<nsIDOMNode> firstChildNode;
michael@0 3908 nsCOMPtr<nsIDOMNode> newNode;
michael@0 3909 if (xmlDoc)
michael@0 3910 {
michael@0 3911 aDocument->CreateElementNS(
michael@0 3912 kXHTMLNS,
michael@0 3913 kHead, getter_AddRefs(headElement));
michael@0 3914 }
michael@0 3915 else
michael@0 3916 {
michael@0 3917 aDocument->CreateElement(
michael@0 3918 kHead, getter_AddRefs(headElement));
michael@0 3919 }
michael@0 3920 nsCOMPtr<nsIDOMElement> documentElement;
michael@0 3921 aDocument->GetDocumentElement(getter_AddRefs(documentElement));
michael@0 3922 if (documentElement)
michael@0 3923 {
michael@0 3924 documentElement->GetFirstChild(getter_AddRefs(firstChildNode));
michael@0 3925 documentElement->InsertBefore(headElement, firstChildNode, getter_AddRefs(newNode));
michael@0 3926 }
michael@0 3927 }
michael@0 3928 if (!headElement)
michael@0 3929 {
michael@0 3930 return NS_ERROR_FAILURE;
michael@0 3931 }
michael@0 3932
michael@0 3933 // Find or create the BASE element
michael@0 3934 NS_NAMED_LITERAL_STRING(kBase, "base");
michael@0 3935 nsCOMPtr<nsIDOMElement> baseElement;
michael@0 3936 nsCOMPtr<nsIDOMHTMLCollection> baseList;
michael@0 3937 if (xmlDoc)
michael@0 3938 {
michael@0 3939 headElement->GetElementsByTagNameNS(
michael@0 3940 kXHTMLNS,
michael@0 3941 kBase, getter_AddRefs(baseList));
michael@0 3942 }
michael@0 3943 else
michael@0 3944 {
michael@0 3945 headElement->GetElementsByTagName(
michael@0 3946 kBase, getter_AddRefs(baseList));
michael@0 3947 }
michael@0 3948 if (baseList)
michael@0 3949 {
michael@0 3950 nsCOMPtr<nsIDOMNode> baseNode;
michael@0 3951 baseList->Item(0, getter_AddRefs(baseNode));
michael@0 3952 baseElement = do_QueryInterface(baseNode);
michael@0 3953 }
michael@0 3954
michael@0 3955 // Add the BASE element
michael@0 3956 if (!baseElement)
michael@0 3957 {
michael@0 3958 nsCOMPtr<nsIDOMNode> newNode;
michael@0 3959 if (xmlDoc)
michael@0 3960 {
michael@0 3961 aDocument->CreateElementNS(
michael@0 3962 kXHTMLNS,
michael@0 3963 kBase, getter_AddRefs(baseElement));
michael@0 3964 }
michael@0 3965 else
michael@0 3966 {
michael@0 3967 aDocument->CreateElement(
michael@0 3968 kBase, getter_AddRefs(baseElement));
michael@0 3969 }
michael@0 3970 headElement->AppendChild(baseElement, getter_AddRefs(newNode));
michael@0 3971 }
michael@0 3972 if (!baseElement)
michael@0 3973 {
michael@0 3974 return NS_ERROR_FAILURE;
michael@0 3975 }
michael@0 3976 nsAutoCString uriSpec;
michael@0 3977 aBaseURI->GetSpec(uriSpec);
michael@0 3978 NS_ConvertUTF8toUTF16 href(uriSpec);
michael@0 3979 baseElement->SetAttribute(NS_LITERAL_STRING("href"), href);
michael@0 3980
michael@0 3981 return NS_OK;
michael@0 3982 }
michael@0 3983
michael@0 3984 // Decide if we need to apply conversion to the passed channel.
michael@0 3985 void nsWebBrowserPersist::SetApplyConversionIfNeeded(nsIChannel *aChannel)
michael@0 3986 {
michael@0 3987 nsresult rv = NS_OK;
michael@0 3988 nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aChannel, &rv);
michael@0 3989 if (NS_FAILED(rv))
michael@0 3990 return;
michael@0 3991
michael@0 3992 // Set the default conversion preference:
michael@0 3993 encChannel->SetApplyConversion(false);
michael@0 3994
michael@0 3995 nsCOMPtr<nsIURI> thisURI;
michael@0 3996 aChannel->GetURI(getter_AddRefs(thisURI));
michael@0 3997 nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(thisURI));
michael@0 3998 if (!sourceURL)
michael@0 3999 return;
michael@0 4000 nsAutoCString extension;
michael@0 4001 sourceURL->GetFileExtension(extension);
michael@0 4002
michael@0 4003 nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
michael@0 4004 encChannel->GetContentEncodings(getter_AddRefs(encEnum));
michael@0 4005 if (!encEnum)
michael@0 4006 return;
michael@0 4007 nsCOMPtr<nsIExternalHelperAppService> helperAppService =
michael@0 4008 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
michael@0 4009 if (NS_FAILED(rv))
michael@0 4010 return;
michael@0 4011 bool hasMore;
michael@0 4012 rv = encEnum->HasMore(&hasMore);
michael@0 4013 if (NS_SUCCEEDED(rv) && hasMore)
michael@0 4014 {
michael@0 4015 nsAutoCString encType;
michael@0 4016 rv = encEnum->GetNext(encType);
michael@0 4017 if (NS_SUCCEEDED(rv))
michael@0 4018 {
michael@0 4019 bool applyConversion = false;
michael@0 4020 rv = helperAppService->ApplyDecodingForExtension(extension, encType,
michael@0 4021 &applyConversion);
michael@0 4022 if (NS_SUCCEEDED(rv))
michael@0 4023 encChannel->SetApplyConversion(applyConversion);
michael@0 4024 }
michael@0 4025 }
michael@0 4026 }
michael@0 4027
michael@0 4028 ///////////////////////////////////////////////////////////////////////////////
michael@0 4029
michael@0 4030
michael@0 4031 nsEncoderNodeFixup::nsEncoderNodeFixup() : mWebBrowserPersist(nullptr)
michael@0 4032 {
michael@0 4033 }
michael@0 4034
michael@0 4035
michael@0 4036 nsEncoderNodeFixup::~nsEncoderNodeFixup()
michael@0 4037 {
michael@0 4038 }
michael@0 4039
michael@0 4040
michael@0 4041 NS_IMPL_ADDREF(nsEncoderNodeFixup)
michael@0 4042 NS_IMPL_RELEASE(nsEncoderNodeFixup)
michael@0 4043
michael@0 4044
michael@0 4045 NS_INTERFACE_MAP_BEGIN(nsEncoderNodeFixup)
michael@0 4046 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentEncoderNodeFixup)
michael@0 4047 NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoderNodeFixup)
michael@0 4048 NS_INTERFACE_MAP_END
michael@0 4049
michael@0 4050
michael@0 4051 NS_IMETHODIMP nsEncoderNodeFixup::FixupNode(
michael@0 4052 nsIDOMNode *aNode, bool *aSerializeCloneKids, nsIDOMNode **aOutNode)
michael@0 4053 {
michael@0 4054 NS_ENSURE_ARG_POINTER(aNode);
michael@0 4055 NS_ENSURE_ARG_POINTER(aOutNode);
michael@0 4056 NS_ENSURE_TRUE(mWebBrowserPersist, NS_ERROR_FAILURE);
michael@0 4057
michael@0 4058 *aOutNode = nullptr;
michael@0 4059
michael@0 4060 // Test whether we need to fixup the node
michael@0 4061 uint16_t type = 0;
michael@0 4062 aNode->GetNodeType(&type);
michael@0 4063 if (type == nsIDOMNode::ELEMENT_NODE ||
michael@0 4064 type == nsIDOMNode::PROCESSING_INSTRUCTION_NODE)
michael@0 4065 {
michael@0 4066 return mWebBrowserPersist->CloneNodeWithFixedUpAttributes(aNode, aSerializeCloneKids, aOutNode);
michael@0 4067 }
michael@0 4068
michael@0 4069 return NS_OK;
michael@0 4070 }

mercurial