embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:db1fb3fcdb5f
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/. */
5
6 #include "mozilla/ArrayUtils.h"
7
8 #include "nspr.h"
9
10 #include "nsIFileStreams.h" // New Necko file streams
11 #include <algorithm>
12
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"
31
32 #include "nsCExternalHandlerService.h"
33
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"
52
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"
73
74 #include "nsIImageLoadingContent.h"
75
76 #include "ftpCore.h"
77 #include "nsITransport.h"
78 #include "nsISocketTransport.h"
79 #include "nsIStringBundle.h"
80 #include "nsIProtocolHandler.h"
81
82 #include "nsWebBrowserPersist.h"
83
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"
89
90 using namespace mozilla;
91 using namespace mozilla::dom;
92
93 // Buffer file writes in 32kb chunks
94 #define BUFFERED_OUTPUT_SIZE (1024 * 32)
95
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 };
107
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 };
123
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;
133
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 };
150
151 struct UploadData
152 {
153 nsCOMPtr<nsIURI> mFile;
154 int64_t mSelfProgress;
155 int64_t mSelfProgressMax;
156
157 UploadData(nsIURI *aFile) :
158 mFile(aFile),
159 mSelfProgress(0),
160 mSelfProgressMax(10000)
161 {
162 }
163 };
164
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 };
173
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;
179
180 // Default flags for persistence
181 const uint32_t kDefaultPersistFlags =
182 nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
183 nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
184
185 // String bundle where error messages come from
186 const char *kWebBrowserPersistStringBundle =
187 "chrome://global/locale/nsWebBrowserPersist.properties";
188
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 }
207
208 nsWebBrowserPersist::~nsWebBrowserPersist()
209 {
210 Cleanup();
211 }
212
213 //*****************************************************************************
214 // nsWebBrowserPersist::nsISupports
215 //*****************************************************************************
216
217 NS_IMPL_ADDREF(nsWebBrowserPersist)
218 NS_IMPL_RELEASE(nsWebBrowserPersist)
219
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
230
231
232 //*****************************************************************************
233 // nsWebBrowserPersist::nsIInterfaceRequestor
234 //*****************************************************************************
235
236 NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
237 {
238 NS_ENSURE_ARG_POINTER(aIFace);
239
240 *aIFace = nullptr;
241
242 nsresult rv = QueryInterface(aIID, aIFace);
243 if (NS_SUCCEEDED(rv))
244 {
245 return rv;
246 }
247
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 }
255
256 nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
257 if (req)
258 {
259 return req->GetInterface(aIID, aIFace);
260 }
261
262 return NS_ERROR_NO_INTERFACE;
263 }
264
265
266 //*****************************************************************************
267 // nsWebBrowserPersist::nsIWebBrowserPersist
268 //*****************************************************************************
269
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 }
284
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 }
303
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 }
311
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 }
321
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 }
330
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 }
340
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!
346
347 nsCOMPtr<nsIURI> fileAsURI;
348 nsresult rv;
349 rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
350 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
351
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 }
357
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!
364
365 nsCOMPtr<nsIURI> fileAsURI;
366 nsresult rv;
367 rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
368 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
369
370 rv = aChannel->GetURI(getter_AddRefs(mURI));
371 NS_ENSURE_SUCCESS(rv, rv);
372
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 }
378
379
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!
389
390 nsCOMPtr<nsIURI> fileAsURI;
391 nsCOMPtr<nsIURI> datapathAsURI;
392 nsresult rv;
393
394 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
395 nsCOMPtr<nsILoadContext> privacyContext = doc ? doc->GetLoadContext() : nullptr;
396 mIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
397
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 }
405
406 mWrapColumn = aWrapColumn;
407
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;
442
443 if (aOutputContentType)
444 {
445 mContentType.AssignASCII(aOutputContentType);
446 }
447
448 rv = SaveDocumentInternal(aDocument, fileAsURI, datapathAsURI);
449
450 // Now save the URIs that have been gathered
451
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 }
468
469 return rv;
470 }
471
472 /* void cancel(nsresult aReason); */
473 NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
474 {
475 mCancel = true;
476 EndDownload(aReason);
477 return NS_OK;
478 }
479
480
481 /* void cancelSave(); */
482 NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
483 {
484 return Cancel(NS_BINDING_ABORTED);
485 }
486
487
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 }
499
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);
508
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);
515
516 // add this to the upload list
517 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
518 mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
519
520 return NS_OK;
521 }
522
523 nsresult
524 nsWebBrowserPersist::SaveGatheredURIs(nsIURI *aFileAsURI)
525 {
526 nsresult rv = NS_OK;
527
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 }
534
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 }
541
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)
547
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 }
559
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 }
571
572 // State stop notification
573 if (mProgressListener)
574 {
575 mProgressListener->OnStateChange(nullptr, nullptr,
576 nsIWebProgressListener::STATE_STOP | addToStateFlags, rv);
577 }
578 }
579
580 return rv;
581 }
582
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 }
591
592 nsresult rv = SaveGatheredURIs(nullptr);
593 if (NS_FAILED(rv))
594 {
595 return false;
596 }
597
598 return (mURIMap.Count()
599 || mUploadList.Count()
600 || mDocList.Length()
601 || mOutputMap.Count());
602 }
603
604
605 //*****************************************************************************
606 // nsWebBrowserPersist::nsIRequestObserver
607 //*****************************************************************************
608
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 }
622
623 mJustStartedLoading = false;
624
625 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
626 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
627
628 nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
629 OutputData *data = mOutputMap.Get(keyPtr);
630
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);
643
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 }
653
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);
664
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);
669
670 // now make filename conformant and unique
671 CalculateUniqueFilename(data->mFile);
672 }
673
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);
681
682 // cancel; we don't need to know any more
683 // stop request will get called
684 request->Cancel(NS_BINDING_ABORTED);
685 }
686 }
687
688 return NS_OK;
689 }
690
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);
700
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 }
713
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 }
723
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 }
735
736 if (completed)
737 {
738 // we're all done, do our cleanup
739 EndDownload(status);
740 }
741
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 }
758
759 return NS_OK;
760 }
761
762 //*****************************************************************************
763 // nsWebBrowserPersist::nsIStreamListener
764 //*****************************************************************************
765
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;
776
777 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
778 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
779
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 }
787
788 bool readError = true;
789
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 }
800
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 }
849
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 }
881
882 // Notify listener if an error occurred.
883 if (cancel)
884 {
885 SendErrorStatusChange(readError, rv,
886 readError ? request : nullptr, data->mFile);
887 }
888 }
889
890 // Cancel reading?
891 if (cancel)
892 {
893 EndDownload(NS_BINDING_ABORTED);
894 }
895
896 return NS_OK;
897 }
898
899
900 //*****************************************************************************
901 // nsWebBrowserPersist::nsIProgressEventSink
902 //*****************************************************************************
903
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 }
914
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 }
932
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 }
946
947 // If our progress listener implements nsIProgressEventSink,
948 // forward the notification
949 if (mEventSink)
950 {
951 mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
952 }
953
954 return NS_OK;
955 }
956
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;
981
982 default:
983 // Pass other notifications (for legitimate errors) along.
984 mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
985 break;
986 }
987
988 }
989
990 // If our progress listener implements nsIProgressEventSink,
991 // forward the notification
992 if (mEventSink)
993 {
994 mEventSink->OnStatus(request, ctxt, status, statusArg);
995 }
996
997 return NS_OK;
998 }
999
1000
1001 //*****************************************************************************
1002 // nsWebBrowserPersist private methods
1003 //*****************************************************************************
1004
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);
1011
1012 if (!mProgressListener)
1013 {
1014 // Do nothing
1015 return NS_OK;
1016 }
1017
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 }
1032
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;
1049
1050 case NS_ERROR_FILE_READ_ONLY:
1051 // Attempt to write to read/only file.
1052 msgId.AssignLiteral("readOnly");
1053 break;
1054
1055 case NS_ERROR_FILE_ACCESS_DENIED:
1056 // Attempt to write without sufficient permissions.
1057 msgId.AssignLiteral("accessError");
1058 break;
1059
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);
1072
1073 nsCOMPtr<nsIStringBundle> bundle;
1074 rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
1075 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
1076
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);
1082
1083 mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
1084
1085 return NS_OK;
1086 }
1087
1088 nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
1089 {
1090 NS_ENSURE_ARG_POINTER(aObject);
1091 NS_ENSURE_ARG_POINTER(aURI);
1092
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 }
1105
1106 return NS_ERROR_FAILURE;
1107 }
1108
1109 nsresult nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile) const
1110 {
1111 nsresult rv;
1112
1113 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
1114 if (NS_FAILED(rv))
1115 return rv;
1116
1117 nsCOMPtr<nsIFile> file;
1118 rv = fileURL->GetFile(getter_AddRefs(file));
1119 if (NS_FAILED(rv)) {
1120 return rv;
1121 }
1122
1123 file.forget(aLocalFile);
1124 return NS_OK;
1125 }
1126
1127 nsresult nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath) const
1128 {
1129 NS_ENSURE_ARG_POINTER(aURI);
1130
1131 nsAutoCString newPath;
1132 nsresult rv = aURI->GetPath(newPath);
1133 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1134
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 }
1141
1142 // Store the path back on the URI
1143 AppendUTF16toUTF8(aPath, newPath);
1144 aURI->SetPath(newPath);
1145
1146 return NS_OK;
1147 }
1148
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);
1156
1157 nsresult rv = NS_OK;
1158
1159 mURI = aURI;
1160
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 }
1170
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 }
1189
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 }
1200
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);
1206
1207 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
1208 if (pbChannel)
1209 {
1210 pbChannel->SetPrivate(aIsPrivate);
1211 }
1212
1213 if (NS_FAILED(rv) || inputChannel == nullptr)
1214 {
1215 EndDownload(NS_ERROR_FAILURE);
1216 return NS_ERROR_FAILURE;
1217 }
1218
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 }
1228
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 }
1236
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 }
1246
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 }
1261
1262 // Cache key
1263 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
1264 if (cacheChannel && cacheKey)
1265 {
1266 cacheChannel->SetCacheKey(cacheKey);
1267 }
1268
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 }
1306
1307 nsresult nsWebBrowserPersist::SaveChannelInternal(
1308 nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
1309 {
1310 NS_ENSURE_ARG_POINTER(aChannel);
1311 NS_ENSURE_ARG_POINTER(aFile);
1312
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 }
1331
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 }
1340
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 }
1352
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));
1356
1357 return NS_OK;
1358 }
1359
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);
1365
1366 *aExt = nullptr;
1367
1368 nsresult rv;
1369 if (!mMIMEService)
1370 {
1371 mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
1372 NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
1373 }
1374
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 }
1386
1387 return NS_ERROR_FAILURE;
1388 }
1389
1390 nsresult
1391 nsWebBrowserPersist::GetDocumentExtension(nsIDOMDocument *aDocument, char16_t **aExt)
1392 {
1393 NS_ENSURE_ARG_POINTER(aDocument);
1394 NS_ENSURE_ARG_POINTER(aExt);
1395
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 }
1401
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);
1407
1408 *aRealContentType = nullptr;
1409
1410 nsAutoString defaultContentType(NS_LITERAL_STRING("text/html"));
1411
1412 // Get the desired content type for the document, either by using the one
1413 // supplied or from the document itself.
1414
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 }
1427
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
1437
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);
1444
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 }
1457
1458 // Use the default if no encoder exists for the desired one
1459 if (!*aRealContentType)
1460 {
1461 *aRealContentType = ToNewUnicode(defaultContentType);
1462 }
1463
1464 NS_ENSURE_TRUE(*aRealContentType, NS_ERROR_OUT_OF_MEMORY);
1465
1466 return NS_OK;
1467 }
1468
1469 nsresult nsWebBrowserPersist::SaveDocumentInternal(
1470 nsIDOMDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
1471 {
1472 NS_ENSURE_ARG_POINTER(aDocument);
1473 NS_ENSURE_ARG_POINTER(aFile);
1474
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));
1478
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 }
1486
1487 nsCOMPtr<nsIDOMNode> docAsNode = do_QueryInterface(aDocument);
1488
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();
1495
1496 nsCOMPtr<nsIURI> oldBaseURI = mCurrentBaseURI;
1497 nsAutoCString oldCharset(mCurrentCharset);
1498
1499 // Store the base URI and the charset
1500 mCurrentBaseURI = doc->GetBaseURI();
1501 mCurrentCharset = doc->GetDocumentCharacterSet();
1502
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.
1516
1517 nsCOMPtr<nsIURI> oldDataPath = mCurrentDataPath;
1518 bool oldDataPathIsRelative = mCurrentDataPathIsRelative;
1519 nsCString oldCurrentRelativePathToData = mCurrentRelativePathToData;
1520 uint32_t oldThingsToPersist = mCurrentThingsToPersist;
1521
1522 mCurrentDataPathIsRelative = false;
1523 mCurrentDataPath = aDataPath;
1524 mCurrentRelativePathToData = "";
1525 mCurrentThingsToPersist = 0;
1526
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.
1530
1531 // Starting with the data dir work back through its parents
1532 // checking if one of them matches the base directory.
1533
1534 if (localDataPath && localFile)
1535 {
1536 nsCOMPtr<nsIFile> baseDir;
1537 localFile->GetParent(getter_AddRefs(baseDir));
1538
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 }
1552
1553 nsAutoString dirName;
1554 dataDirParent->GetLeafName(dirName);
1555
1556 nsAutoCString newRelativePathToData;
1557 newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
1558 + NS_LITERAL_CSTRING("/")
1559 + relativePathToData;
1560 relativePathToData = newRelativePathToData;
1561
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 }
1581
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
1585
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);
1595
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);
1604
1605 nsCOMPtr<nsIDOMNode> currentNode;
1606 walker->GetCurrentNode(getter_AddRefs(currentNode));
1607 while (currentNode)
1608 {
1609 OnWalkDOMNode(currentNode);
1610 walker->NextNode(getter_AddRefs(currentNode));
1611 }
1612
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;
1620
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 }
1652
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);
1662
1663 // Get the content type to save with
1664 nsXPIDLString realContentType;
1665 GetDocEncoderContentType(aDocument,
1666 !mContentType.IsEmpty() ? mContentType.get() : nullptr,
1667 getter_Copies(realContentType));
1668
1669 nsAutoCString contentType; contentType.AssignWithConversion(realContentType);
1670 nsAutoCString charType; // Empty
1671
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 }
1683
1684 mCurrentBaseURI = oldBaseURI;
1685 mCurrentCharset = oldCharset;
1686
1687 return NS_OK;
1688 }
1689
1690 nsresult nsWebBrowserPersist::SaveDocuments()
1691 {
1692 nsresult rv = NS_OK;
1693
1694 mStartSaving = true;
1695
1696 // Iterate through all queued documents, saving them to file and fixing
1697 // them up on the way.
1698
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 }
1708
1709 mCurrentBaseURI = docData->mBaseURI;
1710 mCurrentCharset = docData->mCharset;
1711
1712 // Save the document, fixing it up with the new URIs as we do
1713
1714 nsEncoderNodeFixup *nodeFixup;
1715 nodeFixup = new nsEncoderNodeFixup;
1716 if (nodeFixup)
1717 nodeFixup->mWebBrowserPersist = this;
1718
1719 // Get the content type
1720 nsXPIDLString realContentType;
1721 GetDocEncoderContentType(docData->mDocument,
1722 !mContentType.IsEmpty() ? mContentType.get() : nullptr,
1723 getter_Copies(realContentType));
1724
1725 nsAutoCString contentType; contentType.AssignWithConversion(realContentType.get());
1726 nsAutoCString charType; // Empty
1727
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);
1737
1738 if (NS_FAILED(rv))
1739 break;
1740
1741 // if we're serializing, bail after first iteration of loop
1742 if (mSerializingOutput)
1743 break;
1744 }
1745
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 }
1757
1758 if (!mSerializingOutput)
1759 {
1760 mDocList.Clear();
1761 }
1762
1763 return rv;
1764 }
1765
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 }
1788
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;
1803
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;
1810
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 !
1817
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.
1831
1832 bool isEmptyDirectory = true;
1833 nsCOMArray<nsISimpleEnumerator> dirStack;
1834 int32_t stackSize = 0;
1835
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);
1840
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);
1847
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 }
1855
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");
1865
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);
1883
1884 }
1885 dirStack.Clear();
1886
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 }
1896
1897 nsresult
1898 nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
1899 {
1900 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
1901 NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
1902
1903 bool nameHasChanged = false;
1904 nsresult rv;
1905
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);
1913
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 }
1933
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 }
1959
1960 filename.Assign(base);
1961 filename.Append(ext);
1962 nameHasChanged = true;
1963 }
1964
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.
1968
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.
1979
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 }
1999
2000 tmpPath.Assign(directory);
2001 tmpPath.Append(tmpBase);
2002 tmpPath.Append(ext);
2003
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 }
2018
2019 // Add name to list of those already used
2020 nsAutoCString newFilepath(directory);
2021 newFilepath.Append(filename);
2022 mFilenameList.AppendElement(newFilepath);
2023
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 }
2033
2034 nsCOMPtr<nsIFile> localFile;
2035 GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
2036
2037 if (localFile)
2038 {
2039 nsAutoString filenameAsUnichar;
2040 filenameAsUnichar.AssignWithConversion(filename.get());
2041 localFile->SetLeafName(filenameAsUnichar);
2042
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 }
2054
2055 return NS_OK;
2056 }
2057
2058
2059 nsresult
2060 nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
2061 {
2062 // Try to get filename from the URI.
2063 nsAutoString fileName;
2064
2065 // Get a suggested file name from the URL but strip it of characters
2066 // likely to cause the name to be illegal.
2067
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 }
2106
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 }
2115
2116 end:
2117 aFilename = fileName;
2118 return NS_OK;
2119 }
2120
2121
2122 nsresult
2123 nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
2124 {
2125 nsresult rv;
2126
2127 if (!mMIMEService)
2128 {
2129 mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
2130 NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
2131 }
2132
2133 nsAutoCString contentType;
2134
2135 // Get the content type from the channel
2136 aChannel->GetContentType(contentType);
2137
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 }
2145
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));
2152
2153 nsCOMPtr<nsIFile> localFile;
2154 GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
2155
2156 if (mimeInfo)
2157 {
2158 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
2159 NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
2160
2161 nsAutoCString newFileName;
2162 url->GetFileName(newFileName);
2163
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 }
2171
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 }
2185
2186 // can't use old extension so use primary extension
2187 if (!useOldExt)
2188 {
2189 mimeInfo->GetPrimaryExtension(fileExt);
2190 }
2191
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);
2199
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 }
2208
2209 if (localFile)
2210 {
2211 localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
2212
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 }
2223
2224 }
2225 }
2226
2227 return NS_OK;
2228 }
2229
2230 nsresult
2231 nsWebBrowserPersist::MakeOutputStream(
2232 nsIURI *aURI, nsIOutputStream **aOutputStream)
2233 {
2234 nsresult rv;
2235
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);
2242
2243 return rv;
2244 }
2245
2246 nsresult
2247 nsWebBrowserPersist::MakeOutputStreamFromFile(
2248 nsIFile *aFile, nsIOutputStream **aOutputStream)
2249 {
2250 nsresult rv = NS_OK;
2251
2252 nsCOMPtr<nsIFileOutputStream> fileOutputStream =
2253 do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
2254 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2255
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);
2262
2263 *aOutputStream = NS_BufferOutputStream(fileOutputStream,
2264 BUFFERED_OUTPUT_SIZE).take();
2265
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 }
2278
2279 return NS_OK;
2280 }
2281
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);
2291
2292 NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
2293 return NS_OK;
2294 }
2295
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 }
2304
2305 // Do file cleanup if required
2306 if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
2307 {
2308 CleanupLocalFiles();
2309 }
2310
2311 // Cleanup the channels
2312 mCompleted = true;
2313 Cleanup();
2314 }
2315
2316 struct MOZ_STACK_CLASS FixRedirectData
2317 {
2318 nsCOMPtr<nsIChannel> mNewChannel;
2319 nsCOMPtr<nsIURI> mOriginalURI;
2320 nsCOMPtr<nsISupports> mMatchingKey;
2321 };
2322
2323 nsresult
2324 nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
2325 {
2326 NS_ENSURE_ARG_POINTER(aNewChannel);
2327 nsCOMPtr<nsIURI> originalURI;
2328
2329 // Enumerate through existing open channels looking for one with
2330 // a URI matching the one specified.
2331
2332 FixRedirectData data;
2333 data.mNewChannel = aNewChannel;
2334 data.mNewChannel->GetOriginalURI(getter_AddRefs(data.mOriginalURI));
2335 mOutputMap.EnumerateRead(EnumFixRedirect, &data);
2336
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.
2339
2340 if (data.mMatchingKey)
2341 {
2342 nsAutoPtr<OutputData> outputData;
2343 mOutputMap.RemoveAndForget(data.mMatchingKey, outputData);
2344 NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
2345
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 }
2353
2354 return NS_OK;
2355 }
2356
2357 PLDHashOperator
2358 nsWebBrowserPersist::EnumFixRedirect(nsISupports *aKey, OutputData *aData, void* aClosure)
2359 {
2360 FixRedirectData *data = static_cast<FixRedirectData*>(aClosure);
2361
2362 nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(aKey);
2363 nsCOMPtr<nsIURI> thisURI;
2364
2365 thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
2366
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 }
2375
2376 return PL_DHASH_NEXT;
2377 }
2378
2379 void
2380 nsWebBrowserPersist::CalcTotalProgress()
2381 {
2382 mTotalCurrentProgress = 0;
2383 mTotalMaxProgress = 0;
2384
2385 if (mOutputMap.Count() > 0)
2386 {
2387 // Total up the progress of each output stream
2388 mOutputMap.EnumerateRead(EnumCalcProgress, this);
2389 }
2390
2391 if (mUploadList.Count() > 0)
2392 {
2393 // Total up the progress of each upload
2394 mUploadList.EnumerateRead(EnumCalcUploadProgress, this);
2395 }
2396
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 }
2405
2406 PLDHashOperator
2407 nsWebBrowserPersist::EnumCalcProgress(nsISupports *aKey, OutputData *aData, void* aClosure)
2408 {
2409 nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
2410
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 }
2420
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 }
2432
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 }
2443
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 }
2451
2452 nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
2453 nsresult rv;
2454
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);
2462
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);
2469
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);
2475
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.
2480
2481 aData->mFile = fileAsURI;
2482 aData->mSaved = true;
2483 }
2484 else
2485 {
2486 aData->mNeedsFixup = false;
2487 }
2488
2489 if (pthis->mSerializingOutput)
2490 return PL_DHASH_STOP;
2491
2492 return PL_DHASH_NEXT;
2493 }
2494
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 }
2505
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 }
2516
2517 nsresult nsWebBrowserPersist::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, const nsAString &aHref)
2518 {
2519 NS_ENSURE_ARG_POINTER(aPI);
2520 nsresult rv = NS_OK;
2521
2522 nsAutoString data;
2523 rv = aPI->GetData(data);
2524 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2525
2526 nsAutoString href;
2527 nsContentUtils::GetPseudoAttributeValue(data,
2528 nsGkAtoms::href,
2529 href);
2530
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;
2539
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);
2555
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 }
2582
2583 return rv;
2584 }
2585
2586 nsresult nsWebBrowserPersist::GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
2587 {
2588 NS_ENSURE_ARG_POINTER(aPI);
2589
2590 nsresult rv = NS_OK;
2591 nsAutoString data;
2592 rv = aPI->GetData(data);
2593 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2594
2595 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
2596
2597 return NS_OK;
2598 }
2599
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 }
2619
2620 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
2621 if (!content)
2622 {
2623 return NS_OK;
2624 }
2625
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 }
2633
2634 if (content->IsSVG(nsGkAtoms::img))
2635 {
2636 StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
2637 return NS_OK;
2638 }
2639
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 }
2652
2653 if (content->IsHTML(nsGkAtoms::body)) {
2654 StoreURIAttribute(aNode, "background");
2655 return NS_OK;
2656 }
2657
2658 if (content->IsHTML(nsGkAtoms::table)) {
2659 StoreURIAttribute(aNode, "background");
2660 return NS_OK;
2661 }
2662
2663 if (content->IsHTML(nsGkAtoms::tr)) {
2664 StoreURIAttribute(aNode, "background");
2665 return NS_OK;
2666 }
2667
2668 if (content->IsHTML(nsGkAtoms::td) || content->IsHTML(nsGkAtoms::th)) {
2669 StoreURIAttribute(aNode, "background");
2670 return NS_OK;
2671 }
2672
2673 nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
2674 if (nodeAsScript)
2675 {
2676 StoreURIAttribute(aNode, "src");
2677 return NS_OK;
2678 }
2679
2680 if (content->IsSVG(nsGkAtoms::script))
2681 {
2682 StoreURIAttributeNS(aNode, "http://www.w3.org/1999/xlink", "href");
2683 return NS_OK;
2684 }
2685
2686 nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
2687 if (nodeAsEmbed)
2688 {
2689 StoreURIAttribute(aNode, "src");
2690 return NS_OK;
2691 }
2692
2693 nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
2694 if (nodeAsObject)
2695 {
2696 StoreURIAttribute(aNode, "data");
2697 return NS_OK;
2698 }
2699
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 }
2716
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");
2723
2724 // restore the base URI we really want to have
2725 mCurrentBaseURI = oldBase;
2726 return NS_OK;
2727 }
2728
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;
2739
2740 linkRel.BeginReading(start);
2741 linkRel.EndReading(end);
2742
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;
2749
2750 // Grab the next space delimited word
2751 nsReadingIterator<char16_t> startWord = current;
2752 do {
2753 ++current;
2754 } while (current != end && !nsCRT::IsAsciiSpace(*current));
2755
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 }
2769
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 }
2788
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 }
2807
2808 nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
2809 if (nodeAsInput)
2810 {
2811 StoreURIAttribute(aNode, "src");
2812 return NS_OK;
2813 }
2814
2815 return NS_OK;
2816 }
2817
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 }
2845
2846 nsresult
2847 nsWebBrowserPersist::CloneNodeWithFixedUpAttributes(
2848 nsIDOMNode *aNodeIn, bool *aSerializeCloneKids, nsIDOMNode **aNodeOut)
2849 {
2850 nsresult rv;
2851 *aNodeOut = nullptr;
2852 *aSerializeCloneKids = false;
2853
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 }
2876
2877 // BASE elements are replaced by a comment so relative links are not hosed.
2878
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 }
2905
2906 nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
2907 if (!content)
2908 {
2909 return NS_OK;
2910 }
2911
2912 // Fix up href and file links in the elements
2913
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 }
2924
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 }
2935
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 }
2944
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 }
2953
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 }
2962
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 }
2971
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);
2983
2984 FixupAnchor(*aNodeOut);
2985 FixupNodeAttribute(*aNodeOut, "src");
2986 }
2987 return rv;
2988 }
2989
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 }
2998
2999 return rv;
3000 }
3001
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 }
3010
3011 return rv;
3012 }
3013
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);
3024
3025 // FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed?
3026 FixupNodeAttributeNS(*aNodeOut, "http://www.w3.org/1999/xlink", "href");
3027 }
3028 return rv;
3029 }
3030
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 }
3041
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 }
3051
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 }
3062
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 }
3073
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 }
3106
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 }
3125
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 }
3136
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 }
3147
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);
3159
3160 FixupNodeAttribute(*aNodeOut, "src");
3161
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 }
3199
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;
3208
3209 nsAutoString valueStr;
3210 nodeAsTextArea->GetValue(valueStr);
3211
3212 (*aNodeOut)->SetTextContent(valueStr);
3213 }
3214 return rv;
3215 }
3216
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 }
3230
3231 return NS_OK;
3232 }
3233
3234 nsresult
3235 nsWebBrowserPersist::StoreURI(
3236 const char *aURI, bool aNeedsPersisting, URIData **aData)
3237 {
3238 NS_ENSURE_ARG_POINTER(aURI);
3239
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);
3246
3247 return StoreURI(uri, aNeedsPersisting, aData);
3248 }
3249
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 }
3259
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 }
3270
3271 if (doNotPersistURI)
3272 {
3273 return NS_OK;
3274 }
3275
3276 URIData *data = nullptr;
3277 MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
3278 if (aData)
3279 {
3280 *aData = data;
3281 }
3282
3283 return NS_OK;
3284 }
3285
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);
3294
3295 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
3296 MOZ_ASSERT(element);
3297
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
3300
3301 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
3302 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
3303 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3304
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 }
3319
3320 return NS_OK;
3321 }
3322
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);
3334
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;
3359
3360 // remove username/password if present
3361 fileAsURI->SetUserPass(EmptyCString());
3362
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;
3370
3371 nsAutoCString filename;
3372 url->GetFileName(filename);
3373
3374 nsAutoCString rawPathURL(data->mRelativePathToData);
3375 rawPathURL.Append(filename);
3376
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 }
3391
3392 aURI = newValue;
3393 return NS_OK;
3394 }
3395
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);
3404
3405 // Find the named URI attribute on the (element) node and change it to reference
3406 // a local file.
3407
3408 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
3409 MOZ_ASSERT(element);
3410
3411 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
3412 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
3413 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3414
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 }
3428
3429 return rv;
3430 }
3431
3432 nsresult
3433 nsWebBrowserPersist::FixupAnchor(nsIDOMNode *aNode)
3434 {
3435 NS_ENSURE_ARG_POINTER(aNode);
3436
3437 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
3438 MOZ_ASSERT(element);
3439
3440 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
3441 nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
3442 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3443
3444 if (mPersistFlags & PERSIST_FLAGS_DONT_FIXUP_LINKS)
3445 {
3446 return NS_OK;
3447 }
3448
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);
3458
3459 // Skip empty values and self-referencing bookmarks
3460 if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#')
3461 {
3462 return NS_OK;
3463 }
3464
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 }
3472
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 }
3488
3489 return NS_OK;
3490 }
3491
3492 nsresult
3493 nsWebBrowserPersist::StoreAndFixupStyleSheet(nsIStyleSheet *aStyleSheet)
3494 {
3495 // TODO go through the style sheet fixing up all links
3496 return NS_OK;
3497 }
3498
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);
3505
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 }
3520
3521 nsresult
3522 nsWebBrowserPersist::SaveSubframeContent(
3523 nsIDOMDocument *aFrameContent, URIData *aData)
3524 {
3525 NS_ENSURE_ARG_POINTER(aData);
3526
3527 // Extract the content type for the frame's contents.
3528 nsCOMPtr<nsIDocument> frameDoc(do_QueryInterface(aFrameContent));
3529 NS_ENSURE_STATE(frameDoc);
3530
3531 nsAutoString contentType;
3532 nsresult rv = frameDoc->GetContentType(contentType);
3533 NS_ENSURE_SUCCESS(rv, rv);
3534
3535 nsXPIDLString ext;
3536 GetExtensionForContentType(contentType.get(), getter_Copies(ext));
3537
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 }
3561
3562 nsString filenameWithExt = aData->mFilename;
3563 filenameWithExt.Append(aData->mSubFrameExt);
3564
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);
3571
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);
3577
3578 // Append _data
3579 newFrameDataPath.AppendLiteral("_data");
3580 rv = AppendPathToURI(frameDataURI, newFrameDataPath);
3581 NS_ENSURE_SUCCESS(rv, rv);
3582
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);
3588
3589 mCurrentThingsToPersist++;
3590
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);
3602
3603 // Store the updated uri to the frame
3604 aData->mFile = frameURI;
3605 aData->mSubFrameExt.Truncate(); // we already put this in frameURI
3606
3607 return NS_OK;
3608 }
3609
3610 nsresult
3611 nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
3612 {
3613 nsresult rv = NS_OK;
3614 *aChannel = nullptr;
3615
3616 nsCOMPtr<nsIIOService> ioserv;
3617 ioserv = do_GetIOService(&rv);
3618 NS_ENSURE_SUCCESS(rv, rv);
3619
3620 rv = ioserv->NewChannelFromURI(aURI, aChannel);
3621 NS_ENSURE_SUCCESS(rv, rv);
3622 NS_ENSURE_ARG_POINTER(*aChannel);
3623
3624 rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
3625 NS_ENSURE_SUCCESS(rv, rv);
3626 return NS_OK;
3627 }
3628
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);
3636
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);
3647
3648 if (!aReplaceExisting && fileExists)
3649 return NS_ERROR_FAILURE; // where are the file I/O errors?
3650 }
3651
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);
3660
3661 // Get a document encoder instance
3662 nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
3663 contractID.Append(aFormatType);
3664
3665 nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(contractID.get(), &rv);
3666 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3667
3668 NS_ConvertASCIItoUTF16 newContentType(aFormatType);
3669 rv = encoder->Init(aDocument, newContentType, aFlags);
3670 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3671
3672 mTargetBaseURI = aFile;
3673
3674 // Set the node fixup callback
3675 encoder->SetNodeFixup(aNodeFixup);
3676
3677 if (mWrapColumn && (aFlags & ENCODE_FLAGS_WRAP))
3678 encoder->SetWrapColumn(mWrapColumn);
3679
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 }
3687
3688 rv = encoder->SetCharset(charsetStr);
3689 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3690
3691 rv = encoder->EncodeToStream(outputStream);
3692 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3693
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 }
3704
3705 return rv;
3706 }
3707
3708
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);
3715
3716 nsAutoCString spec;
3717 nsresult rv = aURI->GetSpec(spec);
3718 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
3719
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 }
3735
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);
3740
3741 // Store the file name
3742 data = new URIData;
3743 NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
3744
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;
3754
3755 if (aNeedsPersisting)
3756 mCurrentThingsToPersist++;
3757
3758 mURIMap.Put(spec, data);
3759 if (aData)
3760 {
3761 *aData = data;
3762 }
3763
3764 return NS_OK;
3765 }
3766
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 };
3788
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;
3795
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 }
3807
3808 return false;
3809 }
3810
3811 static bool HasSpecialXHTMLTags(nsIDOMNode *aParent)
3812 {
3813 if (IsSpecialXHTMLTag(aParent))
3814 return true;
3815
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 }
3835
3836 return false;
3837 }
3838
3839 static bool NeedXHTMLBaseTag(nsIDOMDocument *aDocument)
3840 {
3841 nsCOMPtr<nsIDOMElement> docElement;
3842 aDocument->GetDocumentElement(getter_AddRefs(docElement));
3843
3844 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(docElement));
3845 if (node)
3846 {
3847 return HasSpecialXHTMLTags(node);
3848 }
3849
3850 return false;
3851 }
3852
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 }
3862
3863 NS_ENSURE_ARG_POINTER(aBaseURI);
3864
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 }
3875
3876 NS_NAMED_LITERAL_STRING(kXHTMLNS, "http://www.w3.org/1999/xhtml");
3877 NS_NAMED_LITERAL_STRING(kHead, "head");
3878
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;
3888
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 }
3932
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 }
3954
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);
3980
3981 return NS_OK;
3982 }
3983
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;
3991
3992 // Set the default conversion preference:
3993 encChannel->SetApplyConversion(false);
3994
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);
4002
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 }
4027
4028 ///////////////////////////////////////////////////////////////////////////////
4029
4030
4031 nsEncoderNodeFixup::nsEncoderNodeFixup() : mWebBrowserPersist(nullptr)
4032 {
4033 }
4034
4035
4036 nsEncoderNodeFixup::~nsEncoderNodeFixup()
4037 {
4038 }
4039
4040
4041 NS_IMPL_ADDREF(nsEncoderNodeFixup)
4042 NS_IMPL_RELEASE(nsEncoderNodeFixup)
4043
4044
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
4049
4050
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);
4057
4058 *aOutNode = nullptr;
4059
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 }
4068
4069 return NS_OK;
4070 }

mercurial