Wed, 31 Dec 2014 06:09:35 +0100
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 | } |