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