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