Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/ArrayUtils.h"
7 :
8 : #include "nspr.h"
9 :
10 : #include "nsIFileStreams.h" // New Necko file streams
11 : #include <algorithm>
12 :
13 : #include "nsAutoPtr.h"
14 : #include "nsNetCID.h"
15 : #include "nsNetUtil.h"
16 : #include "nsIInterfaceRequestorUtils.h"
17 : #include "nsILoadContext.h"
18 : #include "nsIPrivateBrowsingChannel.h"
19 : #include "nsComponentManagerUtils.h"
20 : #include "nsIComponentRegistrar.h"
21 : #include "nsIStorageStream.h"
22 : #include "nsISeekableStream.h"
23 : #include "nsIHttpChannel.h"
24 : #include "nsIHttpChannelInternal.h"
25 : #include "nsIEncodedChannel.h"
26 : #include "nsIUploadChannel.h"
27 : #include "nsICacheInfoChannel.h"
28 : #include "nsIFileChannel.h"
29 : #include "nsEscape.h"
30 : #include "nsUnicharUtils.h"
31 : #include "nsIStringEnumerator.h"
32 : #include "nsCRT.h"
33 : #include "nsContentCID.h"
34 : #include "nsStreamUtils.h"
35 :
36 : #include "nsCExternalHandlerService.h"
37 :
38 : #include "nsIURL.h"
39 : #include "nsIFileURL.h"
40 : #include "nsIWebProgressListener.h"
41 : #include "nsIAuthPrompt.h"
42 : #include "nsIPrompt.h"
43 : #include "nsISHEntry.h"
44 : #include "nsIWebPageDescriptor.h"
45 : #include "nsIFormControl.h"
46 : #include "nsContentUtils.h"
47 :
48 : #include "nsIImageLoadingContent.h"
49 :
50 : #include "ftpCore.h"
51 : #include "nsITransport.h"
52 : #include "nsISocketTransport.h"
53 : #include "nsIStringBundle.h"
54 : #include "nsIProtocolHandler.h"
55 :
56 : #include "nsIWebBrowserPersistable.h"
57 : #include "nsWebBrowserPersist.h"
58 : #include "WebBrowserPersistLocalDocument.h"
59 :
60 : #include "nsIContent.h"
61 : #include "nsIMIMEInfo.h"
62 : #include "mozilla/dom/HTMLInputElement.h"
63 : #include "mozilla/dom/HTMLSharedElement.h"
64 : #include "mozilla/dom/HTMLSharedObjectElement.h"
65 : #include "mozilla/Printf.h"
66 :
67 : using namespace mozilla;
68 : using namespace mozilla::dom;
69 :
70 : // Buffer file writes in 32kb chunks
71 : #define BUFFERED_OUTPUT_SIZE (1024 * 32)
72 :
73 0 : struct nsWebBrowserPersist::WalkData
74 : {
75 : nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
76 : nsCOMPtr<nsIURI> mFile;
77 : nsCOMPtr<nsIURI> mDataPath;
78 : };
79 :
80 : // Information about a DOM document
81 0 : struct nsWebBrowserPersist::DocData
82 : {
83 : nsCOMPtr<nsIURI> mBaseURI;
84 : nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
85 : nsCOMPtr<nsIURI> mFile;
86 : nsCString mCharset;
87 : };
88 :
89 : // Information about a URI
90 0 : struct nsWebBrowserPersist::URIData
91 : {
92 : bool mNeedsPersisting;
93 : bool mSaved;
94 : bool mIsSubFrame;
95 : bool mDataPathIsRelative;
96 : bool mNeedsFixup;
97 : nsString mFilename;
98 : nsString mSubFrameExt;
99 : nsCOMPtr<nsIURI> mFile;
100 : nsCOMPtr<nsIURI> mDataPath;
101 : nsCOMPtr<nsIURI> mRelativeDocumentURI;
102 : nsCString mRelativePathToData;
103 : nsCString mCharset;
104 :
105 : nsresult GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut);
106 : };
107 :
108 : // Information about the output stream
109 : struct nsWebBrowserPersist::OutputData
110 : {
111 : nsCOMPtr<nsIURI> mFile;
112 : nsCOMPtr<nsIURI> mOriginalLocation;
113 : nsCOMPtr<nsIOutputStream> mStream;
114 : int64_t mSelfProgress;
115 : int64_t mSelfProgressMax;
116 : bool mCalcFileExt;
117 :
118 0 : OutputData(nsIURI *aFile, nsIURI *aOriginalLocation, bool aCalcFileExt) :
119 : mFile(aFile),
120 : mOriginalLocation(aOriginalLocation),
121 : mSelfProgress(0),
122 : mSelfProgressMax(10000),
123 0 : mCalcFileExt(aCalcFileExt)
124 : {
125 0 : }
126 0 : ~OutputData()
127 0 : {
128 0 : if (mStream)
129 : {
130 0 : mStream->Close();
131 : }
132 0 : }
133 : };
134 :
135 0 : struct nsWebBrowserPersist::UploadData
136 : {
137 : nsCOMPtr<nsIURI> mFile;
138 : int64_t mSelfProgress;
139 : int64_t mSelfProgressMax;
140 :
141 0 : explicit UploadData(nsIURI *aFile) :
142 : mFile(aFile),
143 : mSelfProgress(0),
144 0 : mSelfProgressMax(10000)
145 : {
146 0 : }
147 : };
148 :
149 0 : struct nsWebBrowserPersist::CleanupData
150 : {
151 : nsCOMPtr<nsIFile> mFile;
152 : // Snapshot of what the file actually is at the time of creation so that if
153 : // it transmutes into something else later on it can be ignored. For example,
154 : // catch files that turn into dirs or vice versa.
155 : bool mIsDirectory;
156 : };
157 :
158 : class nsWebBrowserPersist::OnWalk final
159 : : public nsIWebBrowserPersistResourceVisitor
160 : {
161 : public:
162 0 : OnWalk(nsWebBrowserPersist* aParent, nsIURI* aFile, nsIFile* aDataPath)
163 0 : : mParent(aParent)
164 : , mFile(aFile)
165 0 : , mDataPath(aDataPath)
166 0 : { }
167 :
168 : NS_DECL_NSIWEBBROWSERPERSISTRESOURCEVISITOR
169 : NS_DECL_ISUPPORTS
170 : private:
171 : RefPtr<nsWebBrowserPersist> mParent;
172 : nsCOMPtr<nsIURI> mFile;
173 : nsCOMPtr<nsIFile> mDataPath;
174 :
175 0 : virtual ~OnWalk() = default;
176 : };
177 :
178 0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnWalk,
179 : nsIWebBrowserPersistResourceVisitor)
180 :
181 : class nsWebBrowserPersist::OnWrite final
182 : : public nsIWebBrowserPersistWriteCompletion
183 : {
184 : public:
185 0 : OnWrite(nsWebBrowserPersist* aParent,
186 : nsIURI* aFile,
187 : nsIFile* aLocalFile)
188 0 : : mParent(aParent)
189 : , mFile(aFile)
190 0 : , mLocalFile(aLocalFile)
191 0 : { }
192 :
193 : NS_DECL_NSIWEBBROWSERPERSISTWRITECOMPLETION
194 : NS_DECL_ISUPPORTS
195 : private:
196 : RefPtr<nsWebBrowserPersist> mParent;
197 : nsCOMPtr<nsIURI> mFile;
198 : nsCOMPtr<nsIFile> mLocalFile;
199 :
200 0 : virtual ~OnWrite() = default;
201 : };
202 :
203 0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::OnWrite,
204 : nsIWebBrowserPersistWriteCompletion)
205 :
206 : class nsWebBrowserPersist::FlatURIMap final
207 : : public nsIWebBrowserPersistURIMap
208 : {
209 : public:
210 0 : explicit FlatURIMap(const nsACString& aTargetBase)
211 0 : : mTargetBase(aTargetBase) { }
212 :
213 0 : void Add(const nsACString& aMapFrom, const nsACString& aMapTo) {
214 0 : mMapFrom.AppendElement(aMapFrom);
215 0 : mMapTo.AppendElement(aMapTo);
216 0 : }
217 :
218 : NS_DECL_NSIWEBBROWSERPERSISTURIMAP
219 : NS_DECL_ISUPPORTS
220 :
221 : private:
222 : nsTArray<nsCString> mMapFrom;
223 : nsTArray<nsCString> mMapTo;
224 : nsCString mTargetBase;
225 :
226 0 : virtual ~FlatURIMap() = default;
227 : };
228 :
229 0 : NS_IMPL_ISUPPORTS(nsWebBrowserPersist::FlatURIMap, nsIWebBrowserPersistURIMap)
230 :
231 : NS_IMETHODIMP
232 0 : nsWebBrowserPersist::FlatURIMap::GetNumMappedURIs(uint32_t* aNum)
233 : {
234 0 : MOZ_ASSERT(mMapFrom.Length() == mMapTo.Length());
235 0 : *aNum = mMapTo.Length();
236 0 : return NS_OK;
237 : }
238 :
239 : NS_IMETHODIMP
240 0 : nsWebBrowserPersist::FlatURIMap::GetTargetBaseURI(nsACString& aTargetBase)
241 : {
242 0 : aTargetBase = mTargetBase;
243 0 : return NS_OK;
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : nsWebBrowserPersist::FlatURIMap::GetURIMapping(uint32_t aIndex,
248 : nsACString& aMapFrom,
249 : nsACString& aMapTo)
250 : {
251 0 : MOZ_ASSERT(mMapFrom.Length() == mMapTo.Length());
252 0 : if (aIndex >= mMapTo.Length()) {
253 0 : return NS_ERROR_INVALID_ARG;
254 : }
255 0 : aMapFrom = mMapFrom[aIndex];
256 0 : aMapTo = mMapTo[aIndex];
257 0 : return NS_OK;
258 : }
259 :
260 :
261 : // Maximum file length constant. The max file name length is
262 : // volume / server dependent but it is difficult to obtain
263 : // that information. Instead this constant is a reasonable value that
264 : // modern systems should able to cope with.
265 : const uint32_t kDefaultMaxFilenameLength = 64;
266 :
267 : // Default flags for persistence
268 : const uint32_t kDefaultPersistFlags =
269 : nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
270 : nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
271 :
272 : // String bundle where error messages come from
273 : const char *kWebBrowserPersistStringBundle =
274 : "chrome://global/locale/nsWebBrowserPersist.properties";
275 :
276 0 : nsWebBrowserPersist::nsWebBrowserPersist() :
277 : mCurrentDataPathIsRelative(false),
278 : mCurrentThingsToPersist(0),
279 : mFirstAndOnlyUse(true),
280 : mSavingDocument(false),
281 : mCancel(false),
282 : mCompleted(false),
283 : mStartSaving(false),
284 : mReplaceExisting(true),
285 : mSerializingOutput(false),
286 : mIsPrivate(false),
287 : mPersistFlags(kDefaultPersistFlags),
288 : mPersistResult(NS_OK),
289 : mTotalCurrentProgress(0),
290 : mTotalMaxProgress(0),
291 : mWrapColumn(72),
292 0 : mEncodingFlags(0)
293 : {
294 0 : }
295 :
296 0 : nsWebBrowserPersist::~nsWebBrowserPersist()
297 : {
298 0 : Cleanup();
299 0 : }
300 :
301 : //*****************************************************************************
302 : // nsWebBrowserPersist::nsISupports
303 : //*****************************************************************************
304 :
305 0 : NS_IMPL_ADDREF(nsWebBrowserPersist)
306 0 : NS_IMPL_RELEASE(nsWebBrowserPersist)
307 :
308 0 : NS_INTERFACE_MAP_BEGIN(nsWebBrowserPersist)
309 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersist)
310 0 : NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersist)
311 0 : NS_INTERFACE_MAP_ENTRY(nsICancelable)
312 0 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
313 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
314 0 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
315 0 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
316 0 : NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
317 0 : NS_INTERFACE_MAP_END
318 :
319 :
320 : //*****************************************************************************
321 : // nsWebBrowserPersist::nsIInterfaceRequestor
322 : //*****************************************************************************
323 :
324 0 : NS_IMETHODIMP nsWebBrowserPersist::GetInterface(const nsIID & aIID, void **aIFace)
325 : {
326 0 : NS_ENSURE_ARG_POINTER(aIFace);
327 :
328 0 : *aIFace = nullptr;
329 :
330 0 : nsresult rv = QueryInterface(aIID, aIFace);
331 0 : if (NS_SUCCEEDED(rv))
332 : {
333 0 : return rv;
334 : }
335 :
336 0 : if (mProgressListener && (aIID.Equals(NS_GET_IID(nsIAuthPrompt))
337 0 : || aIID.Equals(NS_GET_IID(nsIPrompt))))
338 : {
339 0 : mProgressListener->QueryInterface(aIID, aIFace);
340 0 : if (*aIFace)
341 0 : return NS_OK;
342 : }
343 :
344 0 : nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mProgressListener);
345 0 : if (req)
346 : {
347 0 : return req->GetInterface(aIID, aIFace);
348 : }
349 :
350 0 : return NS_ERROR_NO_INTERFACE;
351 : }
352 :
353 :
354 : //*****************************************************************************
355 : // nsWebBrowserPersist::nsIWebBrowserPersist
356 : //*****************************************************************************
357 :
358 0 : NS_IMETHODIMP nsWebBrowserPersist::GetPersistFlags(uint32_t *aPersistFlags)
359 : {
360 0 : NS_ENSURE_ARG_POINTER(aPersistFlags);
361 0 : *aPersistFlags = mPersistFlags;
362 0 : return NS_OK;
363 : }
364 0 : NS_IMETHODIMP nsWebBrowserPersist::SetPersistFlags(uint32_t aPersistFlags)
365 : {
366 0 : mPersistFlags = aPersistFlags;
367 0 : mReplaceExisting = (mPersistFlags & PERSIST_FLAGS_REPLACE_EXISTING_FILES) ? true : false;
368 0 : mSerializingOutput = (mPersistFlags & PERSIST_FLAGS_SERIALIZE_OUTPUT) ? true : false;
369 0 : return NS_OK;
370 : }
371 :
372 0 : NS_IMETHODIMP nsWebBrowserPersist::GetCurrentState(uint32_t *aCurrentState)
373 : {
374 0 : NS_ENSURE_ARG_POINTER(aCurrentState);
375 0 : if (mCompleted)
376 : {
377 0 : *aCurrentState = PERSIST_STATE_FINISHED;
378 : }
379 0 : else if (mFirstAndOnlyUse)
380 : {
381 0 : *aCurrentState = PERSIST_STATE_SAVING;
382 : }
383 : else
384 : {
385 0 : *aCurrentState = PERSIST_STATE_READY;
386 : }
387 0 : return NS_OK;
388 : }
389 :
390 0 : NS_IMETHODIMP nsWebBrowserPersist::GetResult(nsresult *aResult)
391 : {
392 0 : NS_ENSURE_ARG_POINTER(aResult);
393 0 : *aResult = mPersistResult;
394 0 : return NS_OK;
395 : }
396 :
397 0 : NS_IMETHODIMP nsWebBrowserPersist::GetProgressListener(
398 : nsIWebProgressListener * *aProgressListener)
399 : {
400 0 : NS_ENSURE_ARG_POINTER(aProgressListener);
401 0 : *aProgressListener = mProgressListener;
402 0 : NS_IF_ADDREF(*aProgressListener);
403 0 : return NS_OK;
404 : }
405 :
406 0 : NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
407 : nsIWebProgressListener * aProgressListener)
408 : {
409 0 : mProgressListener = aProgressListener;
410 0 : mProgressListener2 = do_QueryInterface(aProgressListener);
411 0 : mEventSink = do_GetInterface(aProgressListener);
412 0 : return NS_OK;
413 : }
414 :
415 0 : NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
416 : nsIURI *aURI, nsISupports *aCacheKey,
417 : nsIURI *aReferrer, uint32_t aReferrerPolicy,
418 : nsIInputStream *aPostData, const char *aExtraHeaders,
419 : nsISupports *aFile, nsILoadContext* aPrivacyContext)
420 : {
421 0 : return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aReferrerPolicy,
422 : aPostData, aExtraHeaders, aFile,
423 0 : aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
424 : }
425 :
426 0 : NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
427 : nsIURI *aURI, nsISupports *aCacheKey,
428 : nsIURI *aReferrer, uint32_t aReferrerPolicy,
429 : nsIInputStream *aPostData, const char *aExtraHeaders,
430 : nsISupports *aFile, bool aIsPrivate)
431 : {
432 0 : NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
433 0 : mFirstAndOnlyUse = false; // Stop people from reusing this object!
434 :
435 0 : nsCOMPtr<nsIURI> fileAsURI;
436 : nsresult rv;
437 0 : rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
438 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
439 :
440 : // SaveURI doesn't like broken uris.
441 0 : mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
442 0 : rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aReferrerPolicy,
443 : aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
444 0 : return NS_FAILED(rv) ? rv : NS_OK;
445 : }
446 :
447 0 : NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
448 : nsIChannel *aChannel, nsISupports *aFile)
449 : {
450 0 : NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
451 0 : mFirstAndOnlyUse = false; // Stop people from reusing this object!
452 :
453 0 : nsCOMPtr<nsIURI> fileAsURI;
454 : nsresult rv;
455 0 : rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
456 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
457 :
458 0 : rv = aChannel->GetURI(getter_AddRefs(mURI));
459 0 : NS_ENSURE_SUCCESS(rv, rv);
460 :
461 : // SaveURI doesn't like broken uris.
462 0 : mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
463 0 : rv = SaveChannelInternal(aChannel, fileAsURI, false);
464 0 : return NS_FAILED(rv) ? rv : NS_OK;
465 : }
466 :
467 :
468 0 : NS_IMETHODIMP nsWebBrowserPersist::SaveDocument(
469 : nsISupports *aDocument, nsISupports *aFile, nsISupports *aDataPath,
470 : const char *aOutputContentType, uint32_t aEncodingFlags, uint32_t aWrapColumn)
471 : {
472 0 : NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
473 0 : mFirstAndOnlyUse = false; // Stop people from reusing this object!
474 :
475 : // We need a STATE_IS_NETWORK start/stop pair to bracket the
476 : // notification callbacks. For a whole document we generate those
477 : // here and in EndDownload(), but for the single-request methods
478 : // that's done in On{Start,Stop}Request instead.
479 0 : mSavingDocument = true;
480 :
481 0 : NS_ENSURE_ARG_POINTER(aDocument);
482 0 : NS_ENSURE_ARG_POINTER(aFile);
483 :
484 0 : nsCOMPtr<nsIURI> fileAsURI;
485 0 : nsCOMPtr<nsIURI> datapathAsURI;
486 : nsresult rv;
487 :
488 0 : rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
489 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
490 0 : if (aDataPath)
491 : {
492 0 : rv = GetValidURIFromObject(aDataPath, getter_AddRefs(datapathAsURI));
493 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
494 : }
495 :
496 0 : mWrapColumn = aWrapColumn;
497 0 : mEncodingFlags = aEncodingFlags;
498 :
499 0 : if (aOutputContentType)
500 : {
501 0 : mContentType.AssignASCII(aOutputContentType);
502 : }
503 :
504 : // State start notification
505 0 : if (mProgressListener) {
506 0 : mProgressListener->OnStateChange(nullptr, nullptr,
507 : nsIWebProgressListener::STATE_START
508 0 : | nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
509 : }
510 :
511 0 : nsCOMPtr<nsIWebBrowserPersistDocument> doc = do_QueryInterface(aDocument);
512 0 : if (!doc) {
513 0 : nsCOMPtr<nsIDocument> localDoc = do_QueryInterface(aDocument);
514 0 : if (localDoc) {
515 0 : doc = new mozilla::WebBrowserPersistLocalDocument(localDoc);
516 : } else {
517 0 : rv = NS_ERROR_NO_INTERFACE;
518 : }
519 : }
520 0 : if (doc) {
521 0 : rv = SaveDocumentInternal(doc, fileAsURI, datapathAsURI);
522 : }
523 0 : if (NS_FAILED(rv)) {
524 0 : SendErrorStatusChange(true, rv, nullptr, mURI);
525 0 : EndDownload(rv);
526 : }
527 0 : return rv;
528 : }
529 :
530 0 : NS_IMETHODIMP nsWebBrowserPersist::Cancel(nsresult aReason)
531 : {
532 0 : mCancel = true;
533 0 : EndDownload(aReason);
534 0 : return NS_OK;
535 : }
536 :
537 :
538 0 : NS_IMETHODIMP nsWebBrowserPersist::CancelSave()
539 : {
540 0 : return Cancel(NS_BINDING_ABORTED);
541 : }
542 :
543 :
544 : nsresult
545 0 : nsWebBrowserPersist::StartUpload(nsIStorageStream *storStream,
546 : nsIURI *aDestinationURI, const nsACString &aContentType)
547 : {
548 : // setup the upload channel if the destination is not local
549 0 : nsCOMPtr<nsIInputStream> inputstream;
550 0 : nsresult rv = storStream->NewInputStream(0, getter_AddRefs(inputstream));
551 0 : NS_ENSURE_TRUE(inputstream, NS_ERROR_FAILURE);
552 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
553 0 : return StartUpload(inputstream, aDestinationURI, aContentType);
554 : }
555 :
556 : nsresult
557 0 : nsWebBrowserPersist::StartUpload(nsIInputStream *aInputStream,
558 : nsIURI *aDestinationURI, const nsACString &aContentType)
559 : {
560 0 : nsCOMPtr<nsIChannel> destChannel;
561 0 : CreateChannelFromURI(aDestinationURI, getter_AddRefs(destChannel));
562 0 : nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(destChannel));
563 0 : NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
564 :
565 : // Set the upload stream
566 : // NOTE: ALL data must be available in "inputstream"
567 0 : nsresult rv = uploadChannel->SetUploadStream(aInputStream, aContentType, -1);
568 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
569 0 : rv = destChannel->AsyncOpen2(this);
570 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
571 :
572 : // add this to the upload list
573 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(destChannel);
574 0 : mUploadList.Put(keyPtr, new UploadData(aDestinationURI));
575 :
576 0 : return NS_OK;
577 : }
578 :
579 : void
580 0 : nsWebBrowserPersist::SerializeNextFile()
581 : {
582 0 : nsresult rv = NS_OK;
583 0 : MOZ_ASSERT(mWalkStack.Length() == 0);
584 :
585 : // First, handle gathered URIs.
586 : // Count how many URIs in the URI map require persisting
587 0 : uint32_t urisToPersist = 0;
588 0 : if (mURIMap.Count() > 0) {
589 : // This is potentially O(n^2), when taking into account the
590 : // number of times this method is called. If it becomes a
591 : // bottleneck, the count of not-yet-persisted URIs could be
592 : // maintained separately.
593 0 : for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
594 0 : URIData *data = iter.UserData();
595 0 : if (data->mNeedsPersisting && !data->mSaved) {
596 0 : urisToPersist++;
597 : }
598 : }
599 : }
600 :
601 0 : if (urisToPersist > 0) {
602 : // Persist each file in the uri map. The document(s)
603 : // will be saved after the last one of these is saved.
604 0 : for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
605 0 : URIData *data = iter.UserData();
606 :
607 0 : if (!data->mNeedsPersisting || data->mSaved) {
608 0 : continue;
609 : }
610 :
611 : nsresult rv;
612 :
613 : // Create a URI from the key.
614 0 : nsCOMPtr<nsIURI> uri;
615 0 : rv = NS_NewURI(getter_AddRefs(uri), iter.Key(),
616 0 : data->mCharset.get());
617 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
618 0 : break;
619 : }
620 :
621 : // Make a URI to save the data to.
622 0 : nsCOMPtr<nsIURI> fileAsURI;
623 0 : rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
624 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
625 0 : break;
626 : }
627 0 : rv = AppendPathToURI(fileAsURI, data->mFilename);
628 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
629 0 : break;
630 : }
631 :
632 : // The Referrer Policy doesn't matter here since the referrer is
633 : // nullptr.
634 0 : rv = SaveURIInternal(uri, nullptr, nullptr,
635 : mozilla::net::RP_Unset, nullptr, nullptr,
636 0 : fileAsURI, true, mIsPrivate);
637 : // If SaveURIInternal fails, then it will have called EndDownload,
638 : // which means that |data| is no longer valid memory. We MUST bail.
639 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
640 0 : break;
641 : }
642 :
643 0 : if (rv == NS_OK) {
644 : // Store the actual object because once it's persisted this
645 : // will be fixed up with the right file extension.
646 0 : data->mFile = fileAsURI;
647 0 : data->mSaved = true;
648 : } else {
649 0 : data->mNeedsFixup = false;
650 : }
651 :
652 0 : if (mSerializingOutput) {
653 0 : break;
654 : }
655 : }
656 : }
657 :
658 : // If there are downloads happening, wait until they're done; the
659 : // OnStopRequest handler will call this method again.
660 0 : if (mOutputMap.Count() > 0) {
661 0 : return;
662 : }
663 :
664 : // If serializing, also wait until last upload is done.
665 0 : if (mSerializingOutput && mUploadList.Count() > 0) {
666 0 : return;
667 : }
668 :
669 : // If there are also no more documents, then we're done.
670 0 : if (mDocList.Length() == 0) {
671 : // ...or not quite done, if there are still uploads.
672 0 : if (mUploadList.Count() > 0) {
673 0 : return;
674 : }
675 : // Finish and clean things up. Defer this because the caller
676 : // may have been expecting to use the listeners that that
677 : // method will clear.
678 0 : NS_DispatchToCurrentThread(
679 0 : NewRunnableMethod("nsWebBrowserPersist::FinishDownload",
680 : this,
681 0 : &nsWebBrowserPersist::FinishDownload));
682 0 : return;
683 : }
684 :
685 : // There are no URIs to save, so just save the next document.
686 0 : mStartSaving = true;
687 0 : mozilla::UniquePtr<DocData> docData(mDocList.ElementAt(0));
688 0 : mDocList.RemoveElementAt(0); // O(n^2) but probably doesn't matter.
689 0 : MOZ_ASSERT(docData);
690 0 : if (!docData) {
691 0 : EndDownload(NS_ERROR_FAILURE);
692 0 : return;
693 : }
694 :
695 0 : mCurrentBaseURI = docData->mBaseURI;
696 0 : mCurrentCharset = docData->mCharset;
697 0 : mTargetBaseURI = docData->mFile;
698 :
699 : // Save the document, fixing it up with the new URIs as we do
700 :
701 0 : nsAutoCString targetBaseSpec;
702 0 : if (mTargetBaseURI) {
703 0 : rv = mTargetBaseURI->GetSpec(targetBaseSpec);
704 0 : if (NS_FAILED(rv)) {
705 0 : SendErrorStatusChange(true, rv, nullptr, nullptr);
706 0 : EndDownload(rv);
707 0 : return;
708 : }
709 : }
710 :
711 : // mFlatURIMap must be rebuilt each time through SerializeNextFile, as
712 : // mTargetBaseURI is used to create the relative URLs and will be different
713 : // with each serialized document.
714 0 : RefPtr<FlatURIMap> flatMap = new FlatURIMap(targetBaseSpec);
715 0 : for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
716 0 : nsAutoCString mapTo;
717 0 : nsresult rv = iter.UserData()->GetLocalURI(mTargetBaseURI, mapTo);
718 0 : if (NS_SUCCEEDED(rv) || !mapTo.IsVoid()) {
719 0 : flatMap->Add(iter.Key(), mapTo);
720 : }
721 : }
722 0 : mFlatURIMap = flatMap.forget();
723 :
724 0 : nsCOMPtr<nsIFile> localFile;
725 0 : GetLocalFileFromURI(docData->mFile, getter_AddRefs(localFile));
726 0 : if (localFile) {
727 : // if we're not replacing an existing file but the file
728 : // exists, something is wrong
729 0 : bool fileExists = false;
730 0 : rv = localFile->Exists(&fileExists);
731 0 : if (NS_SUCCEEDED(rv) && !mReplaceExisting && fileExists) {
732 0 : rv = NS_ERROR_FILE_ALREADY_EXISTS;
733 : }
734 0 : if (NS_FAILED(rv)) {
735 0 : SendErrorStatusChange(false, rv, nullptr, docData->mFile);
736 0 : EndDownload(rv);
737 0 : return;
738 : }
739 : }
740 0 : nsCOMPtr<nsIOutputStream> outputStream;
741 0 : rv = MakeOutputStream(docData->mFile, getter_AddRefs(outputStream));
742 0 : if (NS_SUCCEEDED(rv) && !outputStream) {
743 0 : rv = NS_ERROR_FAILURE;
744 : }
745 0 : if (NS_FAILED(rv)) {
746 0 : SendErrorStatusChange(false, rv, nullptr, docData->mFile);
747 0 : EndDownload(rv);
748 0 : return;
749 : }
750 :
751 0 : RefPtr<OnWrite> finish = new OnWrite(this, docData->mFile, localFile);
752 0 : rv = docData->mDocument->WriteContent(outputStream,
753 : mFlatURIMap,
754 0 : NS_ConvertUTF16toUTF8(mContentType),
755 : mEncodingFlags,
756 0 : mWrapColumn,
757 0 : finish);
758 0 : if (NS_FAILED(rv)) {
759 0 : SendErrorStatusChange(false, rv, nullptr, docData->mFile);
760 0 : EndDownload(rv);
761 : }
762 : }
763 :
764 : NS_IMETHODIMP
765 0 : nsWebBrowserPersist::OnWrite::OnFinish(nsIWebBrowserPersistDocument* aDoc,
766 : nsIOutputStream *aStream,
767 : const nsACString& aContentType,
768 : nsresult aStatus)
769 : {
770 0 : nsresult rv = aStatus;
771 :
772 0 : if (NS_FAILED(rv)) {
773 0 : mParent->SendErrorStatusChange(false, rv, nullptr, mFile);
774 0 : mParent->EndDownload(rv);
775 0 : return NS_OK;
776 : }
777 0 : if (!mLocalFile) {
778 0 : nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(aStream));
779 0 : if (storStream) {
780 0 : aStream->Close();
781 0 : rv = mParent->StartUpload(storStream, mFile, aContentType);
782 0 : if (NS_FAILED(rv)) {
783 0 : mParent->SendErrorStatusChange(false, rv, nullptr, mFile);
784 0 : mParent->EndDownload(rv);
785 : }
786 : // Either we failed and we're done, or we're uploading and
787 : // the OnStopRequest callback is responsible for the next
788 : // SerializeNextFile().
789 0 : return NS_OK;
790 : }
791 : }
792 0 : NS_DispatchToCurrentThread(
793 0 : NewRunnableMethod("nsWebBrowserPersist::SerializeNextFile",
794 : mParent,
795 0 : &nsWebBrowserPersist::SerializeNextFile));
796 0 : return NS_OK;
797 : }
798 :
799 : //*****************************************************************************
800 : // nsWebBrowserPersist::nsIRequestObserver
801 : //*****************************************************************************
802 :
803 0 : NS_IMETHODIMP nsWebBrowserPersist::OnStartRequest(
804 : nsIRequest* request, nsISupports *ctxt)
805 : {
806 0 : if (mProgressListener)
807 : {
808 : uint32_t stateFlags = nsIWebProgressListener::STATE_START |
809 0 : nsIWebProgressListener::STATE_IS_REQUEST;
810 0 : if (!mSavingDocument) {
811 0 : stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
812 : }
813 0 : mProgressListener->OnStateChange(nullptr, request, stateFlags, NS_OK);
814 : }
815 :
816 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
817 0 : NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
818 :
819 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
820 0 : OutputData *data = mOutputMap.Get(keyPtr);
821 :
822 : // NOTE: This code uses the channel as a hash key so it will not
823 : // recognize redirected channels because the key is not the same.
824 : // When that happens we remove and add the data entry to use the
825 : // new channel as the hash key.
826 0 : if (!data)
827 : {
828 0 : UploadData *upData = mUploadList.Get(keyPtr);
829 0 : if (!upData)
830 : {
831 : // Redirect? Try and fixup the output table
832 0 : nsresult rv = FixRedirectedChannelEntry(channel);
833 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
834 :
835 : // Should be able to find the data after fixup unless redirects
836 : // are disabled.
837 0 : data = mOutputMap.Get(keyPtr);
838 0 : if (!data)
839 : {
840 0 : return NS_ERROR_FAILURE;
841 : }
842 : }
843 : }
844 :
845 0 : if (data && data->mFile)
846 : {
847 : // If PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION is set in mPersistFlags,
848 : // try to determine whether this channel needs to apply Content-Encoding
849 : // conversions.
850 0 : NS_ASSERTION(!((mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION) &&
851 : (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)),
852 : "Conflict in persist flags: both AUTODETECT and NO_CONVERSION set");
853 0 : if (mPersistFlags & PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION)
854 0 : SetApplyConversionIfNeeded(channel);
855 :
856 0 : if (data->mCalcFileExt && !(mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES))
857 : {
858 : // this is the first point at which the server can tell us the mimetype
859 0 : CalculateAndAppendFileExt(data->mFile, channel, data->mOriginalLocation);
860 :
861 : // now make filename conformant and unique
862 0 : CalculateUniqueFilename(data->mFile);
863 : }
864 :
865 : // compare uris and bail before we add to output map if they are equal
866 0 : bool isEqual = false;
867 0 : if (NS_SUCCEEDED(data->mFile->Equals(data->mOriginalLocation, &isEqual))
868 0 : && isEqual)
869 : {
870 : // remove from output map
871 0 : mOutputMap.Remove(keyPtr);
872 :
873 : // cancel; we don't need to know any more
874 : // stop request will get called
875 0 : request->Cancel(NS_BINDING_ABORTED);
876 : }
877 : }
878 :
879 0 : return NS_OK;
880 : }
881 :
882 0 : NS_IMETHODIMP nsWebBrowserPersist::OnStopRequest(
883 : nsIRequest* request, nsISupports *ctxt, nsresult status)
884 : {
885 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
886 0 : OutputData *data = mOutputMap.Get(keyPtr);
887 0 : if (data) {
888 0 : if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(status)) {
889 0 : SendErrorStatusChange(true, status, request, data->mFile);
890 : }
891 :
892 : // This will automatically close the output stream
893 0 : mOutputMap.Remove(keyPtr);
894 : } else {
895 : // if we didn't find the data in mOutputMap, try mUploadList
896 0 : UploadData *upData = mUploadList.Get(keyPtr);
897 0 : if (upData) {
898 0 : mUploadList.Remove(keyPtr);
899 : }
900 : }
901 :
902 : // Do more work.
903 0 : SerializeNextFile();
904 :
905 0 : if (mProgressListener) {
906 : uint32_t stateFlags = nsIWebProgressListener::STATE_STOP |
907 0 : nsIWebProgressListener::STATE_IS_REQUEST;
908 0 : if (!mSavingDocument) {
909 0 : stateFlags |= nsIWebProgressListener::STATE_IS_NETWORK;
910 : }
911 0 : mProgressListener->OnStateChange(nullptr, request, stateFlags, status);
912 : }
913 :
914 0 : return NS_OK;
915 : }
916 :
917 : //*****************************************************************************
918 : // nsWebBrowserPersist::nsIStreamListener
919 : //*****************************************************************************
920 :
921 : NS_IMETHODIMP
922 0 : nsWebBrowserPersist::OnDataAvailable(
923 : nsIRequest* request, nsISupports *aContext, nsIInputStream *aIStream,
924 : uint64_t aOffset, uint32_t aLength)
925 : {
926 0 : bool cancel = mCancel;
927 0 : if (!cancel)
928 : {
929 0 : nsresult rv = NS_OK;
930 0 : uint32_t bytesRemaining = aLength;
931 :
932 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
933 0 : NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
934 :
935 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
936 0 : OutputData *data = mOutputMap.Get(keyPtr);
937 0 : if (!data) {
938 : // might be uploadData; consume necko's buffer and bail...
939 : uint32_t n;
940 0 : return aIStream->ReadSegments(NS_DiscardSegment, nullptr, aLength, &n);
941 : }
942 :
943 0 : bool readError = true;
944 :
945 : // Make the output stream
946 0 : if (!data->mStream)
947 : {
948 0 : rv = MakeOutputStream(data->mFile, getter_AddRefs(data->mStream));
949 0 : if (NS_FAILED(rv))
950 : {
951 0 : readError = false;
952 0 : cancel = true;
953 : }
954 : }
955 :
956 : // Read data from the input and write to the output
957 : char buffer[8192];
958 : uint32_t bytesRead;
959 0 : while (!cancel && bytesRemaining)
960 : {
961 0 : readError = true;
962 0 : rv = aIStream->Read(buffer,
963 0 : std::min(uint32_t(sizeof(buffer)), bytesRemaining),
964 0 : &bytesRead);
965 0 : if (NS_SUCCEEDED(rv))
966 : {
967 0 : readError = false;
968 : // Write out the data until something goes wrong, or, it is
969 : // all written. We loop because for some errors (e.g., disk
970 : // full), we get NS_OK with some bytes written, then an error.
971 : // So, we want to write again in that case to get the actual
972 : // error code.
973 0 : const char *bufPtr = buffer; // Where to write from.
974 0 : while (NS_SUCCEEDED(rv) && bytesRead)
975 : {
976 0 : uint32_t bytesWritten = 0;
977 0 : rv = data->mStream->Write(bufPtr, bytesRead, &bytesWritten);
978 0 : if (NS_SUCCEEDED(rv))
979 : {
980 0 : bytesRead -= bytesWritten;
981 0 : bufPtr += bytesWritten;
982 0 : bytesRemaining -= bytesWritten;
983 : // Force an error if (for some reason) we get NS_OK but
984 : // no bytes written.
985 0 : if (!bytesWritten)
986 : {
987 0 : rv = NS_ERROR_FAILURE;
988 0 : cancel = true;
989 : }
990 : }
991 : else
992 : {
993 : // Disaster - can't write out the bytes - disk full / permission?
994 0 : cancel = true;
995 : }
996 : }
997 : }
998 : else
999 : {
1000 : // Disaster - can't read the bytes - broken link / file error?
1001 0 : cancel = true;
1002 : }
1003 : }
1004 :
1005 0 : int64_t channelContentLength = -1;
1006 0 : if (!cancel &&
1007 0 : NS_SUCCEEDED(channel->GetContentLength(&channelContentLength)))
1008 : {
1009 : // if we get -1 at this point, we didn't get content-length header
1010 : // assume that we got all of the data and push what we have;
1011 : // that's the best we can do now
1012 0 : if ((-1 == channelContentLength) ||
1013 0 : ((channelContentLength - (aOffset + aLength)) == 0))
1014 : {
1015 0 : NS_WARNING_ASSERTION(
1016 : channelContentLength != -1,
1017 : "nsWebBrowserPersist::OnDataAvailable() no content length "
1018 : "header, pushing what we have");
1019 : // we're done with this pass; see if we need to do upload
1020 0 : nsAutoCString contentType;
1021 0 : channel->GetContentType(contentType);
1022 : // if we don't have the right type of output stream then it's a local file
1023 0 : nsCOMPtr<nsIStorageStream> storStream(do_QueryInterface(data->mStream));
1024 0 : if (storStream)
1025 : {
1026 0 : data->mStream->Close();
1027 0 : data->mStream = nullptr; // null out stream so we don't close it later
1028 0 : rv = StartUpload(storStream, data->mFile, contentType);
1029 0 : if (NS_FAILED(rv))
1030 : {
1031 0 : readError = false;
1032 0 : cancel = true;
1033 : }
1034 : }
1035 : }
1036 : }
1037 :
1038 : // Notify listener if an error occurred.
1039 0 : if (cancel)
1040 : {
1041 0 : SendErrorStatusChange(readError, rv,
1042 0 : readError ? request : nullptr, data->mFile);
1043 : }
1044 : }
1045 :
1046 : // Cancel reading?
1047 0 : if (cancel)
1048 : {
1049 0 : EndDownload(NS_BINDING_ABORTED);
1050 : }
1051 :
1052 0 : return NS_OK;
1053 : }
1054 :
1055 :
1056 : //*****************************************************************************
1057 : // nsWebBrowserPersist::nsIProgressEventSink
1058 : //*****************************************************************************
1059 :
1060 0 : NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
1061 : nsIRequest *request, nsISupports *ctxt, int64_t aProgress,
1062 : int64_t aProgressMax)
1063 : {
1064 0 : if (!mProgressListener)
1065 : {
1066 0 : return NS_OK;
1067 : }
1068 :
1069 : // Store the progress of this request
1070 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
1071 0 : OutputData *data = mOutputMap.Get(keyPtr);
1072 0 : if (data)
1073 : {
1074 0 : data->mSelfProgress = aProgress;
1075 0 : data->mSelfProgressMax = aProgressMax;
1076 : }
1077 : else
1078 : {
1079 0 : UploadData *upData = mUploadList.Get(keyPtr);
1080 0 : if (upData)
1081 : {
1082 0 : upData->mSelfProgress = aProgress;
1083 0 : upData->mSelfProgressMax = aProgressMax;
1084 : }
1085 : }
1086 :
1087 : // Notify listener of total progress
1088 0 : CalcTotalProgress();
1089 0 : if (mProgressListener2)
1090 : {
1091 0 : mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
1092 0 : aProgressMax, mTotalCurrentProgress, mTotalMaxProgress);
1093 : }
1094 : else
1095 : {
1096 : // have to truncate 64-bit to 32bit
1097 0 : mProgressListener->OnProgressChange(nullptr, request, uint64_t(aProgress),
1098 0 : uint64_t(aProgressMax), mTotalCurrentProgress, mTotalMaxProgress);
1099 : }
1100 :
1101 : // If our progress listener implements nsIProgressEventSink,
1102 : // forward the notification
1103 0 : if (mEventSink)
1104 : {
1105 0 : mEventSink->OnProgress(request, ctxt, aProgress, aProgressMax);
1106 : }
1107 :
1108 0 : return NS_OK;
1109 : }
1110 :
1111 0 : NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
1112 : nsIRequest *request, nsISupports *ctxt, nsresult status,
1113 : const char16_t *statusArg)
1114 : {
1115 0 : if (mProgressListener)
1116 : {
1117 : // We need to filter out non-error error codes.
1118 : // Is the only NS_SUCCEEDED value NS_OK?
1119 0 : switch ( status )
1120 : {
1121 : case NS_NET_STATUS_RESOLVING_HOST:
1122 : case NS_NET_STATUS_RESOLVED_HOST:
1123 : case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
1124 : case NS_NET_STATUS_END_FTP_TRANSACTION:
1125 : case NS_NET_STATUS_CONNECTING_TO:
1126 : case NS_NET_STATUS_CONNECTED_TO:
1127 : case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
1128 : case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
1129 : case NS_NET_STATUS_SENDING_TO:
1130 : case NS_NET_STATUS_RECEIVING_FROM:
1131 : case NS_NET_STATUS_WAITING_FOR:
1132 : case NS_NET_STATUS_READING:
1133 : case NS_NET_STATUS_WRITING:
1134 0 : break;
1135 :
1136 : default:
1137 : // Pass other notifications (for legitimate errors) along.
1138 0 : mProgressListener->OnStatusChange(nullptr, request, status, statusArg);
1139 0 : break;
1140 : }
1141 :
1142 : }
1143 :
1144 : // If our progress listener implements nsIProgressEventSink,
1145 : // forward the notification
1146 0 : if (mEventSink)
1147 : {
1148 0 : mEventSink->OnStatus(request, ctxt, status, statusArg);
1149 : }
1150 :
1151 0 : return NS_OK;
1152 : }
1153 :
1154 :
1155 : //*****************************************************************************
1156 : // nsWebBrowserPersist private methods
1157 : //*****************************************************************************
1158 :
1159 : // Convert error info into proper message text and send OnStatusChange notification
1160 : // to the web progress listener.
1161 0 : nsresult nsWebBrowserPersist::SendErrorStatusChange(
1162 : bool aIsReadError, nsresult aResult, nsIRequest *aRequest, nsIURI *aURI)
1163 : {
1164 0 : NS_ENSURE_ARG_POINTER(aURI);
1165 :
1166 0 : if (!mProgressListener)
1167 : {
1168 : // Do nothing
1169 0 : return NS_OK;
1170 : }
1171 :
1172 : // Get the file path or spec from the supplied URI
1173 0 : nsCOMPtr<nsIFile> file;
1174 0 : GetLocalFileFromURI(aURI, getter_AddRefs(file));
1175 0 : nsAutoString path;
1176 : nsresult rv;
1177 0 : if (file)
1178 : {
1179 0 : file->GetPath(path);
1180 : }
1181 : else
1182 : {
1183 0 : nsAutoCString fileurl;
1184 0 : rv = aURI->GetSpec(fileurl);
1185 0 : NS_ENSURE_SUCCESS(rv, rv);
1186 0 : AppendUTF8toUTF16(fileurl, path);
1187 : }
1188 :
1189 0 : nsAutoString msgId;
1190 0 : switch(aResult)
1191 : {
1192 : case NS_ERROR_FILE_NAME_TOO_LONG:
1193 : // File name too long.
1194 0 : msgId.AssignLiteral("fileNameTooLongError");
1195 0 : break;
1196 : case NS_ERROR_FILE_ALREADY_EXISTS:
1197 : // File exists with same name as directory.
1198 0 : msgId.AssignLiteral("fileAlreadyExistsError");
1199 0 : break;
1200 : case NS_ERROR_FILE_DISK_FULL:
1201 : case NS_ERROR_FILE_NO_DEVICE_SPACE:
1202 : // Out of space on target volume.
1203 0 : msgId.AssignLiteral("diskFull");
1204 0 : break;
1205 :
1206 : case NS_ERROR_FILE_READ_ONLY:
1207 : // Attempt to write to read/only file.
1208 0 : msgId.AssignLiteral("readOnly");
1209 0 : break;
1210 :
1211 : case NS_ERROR_FILE_ACCESS_DENIED:
1212 : // Attempt to write without sufficient permissions.
1213 0 : msgId.AssignLiteral("accessError");
1214 0 : break;
1215 :
1216 : default:
1217 : // Generic read/write error message.
1218 0 : if (aIsReadError)
1219 0 : msgId.AssignLiteral("readError");
1220 : else
1221 0 : msgId.AssignLiteral("writeError");
1222 0 : break;
1223 : }
1224 : // Get properties file bundle and extract status string.
1225 0 : nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
1226 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
1227 :
1228 0 : nsCOMPtr<nsIStringBundle> bundle;
1229 0 : rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
1230 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
1231 :
1232 0 : nsXPIDLString msgText;
1233 : const char16_t *strings[1];
1234 0 : strings[0] = path.get();
1235 0 : rv = bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText));
1236 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1237 :
1238 0 : mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText);
1239 :
1240 0 : return NS_OK;
1241 : }
1242 :
1243 0 : nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const
1244 : {
1245 0 : NS_ENSURE_ARG_POINTER(aObject);
1246 0 : NS_ENSURE_ARG_POINTER(aURI);
1247 :
1248 0 : nsCOMPtr<nsIFile> objAsFile = do_QueryInterface(aObject);
1249 0 : if (objAsFile)
1250 : {
1251 0 : return NS_NewFileURI(aURI, objAsFile);
1252 : }
1253 0 : nsCOMPtr<nsIURI> objAsURI = do_QueryInterface(aObject);
1254 0 : if (objAsURI)
1255 : {
1256 0 : *aURI = objAsURI;
1257 0 : NS_ADDREF(*aURI);
1258 0 : return NS_OK;
1259 : }
1260 :
1261 0 : return NS_ERROR_FAILURE;
1262 : }
1263 :
1264 : /* static */ nsresult
1265 0 : nsWebBrowserPersist::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile)
1266 : {
1267 : nsresult rv;
1268 :
1269 0 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
1270 0 : if (NS_FAILED(rv))
1271 0 : return rv;
1272 :
1273 0 : nsCOMPtr<nsIFile> file;
1274 0 : rv = fileURL->GetFile(getter_AddRefs(file));
1275 0 : if (NS_FAILED(rv)) {
1276 0 : return rv;
1277 : }
1278 :
1279 0 : file.forget(aLocalFile);
1280 0 : return NS_OK;
1281 : }
1282 :
1283 : /* static */ nsresult
1284 0 : nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath)
1285 : {
1286 0 : NS_ENSURE_ARG_POINTER(aURI);
1287 :
1288 0 : nsAutoCString newPath;
1289 0 : nsresult rv = aURI->GetPath(newPath);
1290 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1291 :
1292 : // Append a forward slash if necessary
1293 0 : int32_t len = newPath.Length();
1294 0 : if (len > 0 && newPath.CharAt(len - 1) != '/')
1295 : {
1296 0 : newPath.Append('/');
1297 : }
1298 :
1299 : // Store the path back on the URI
1300 0 : AppendUTF16toUTF8(aPath, newPath);
1301 0 : aURI->SetPath(newPath);
1302 :
1303 0 : return NS_OK;
1304 : }
1305 :
1306 0 : nsresult nsWebBrowserPersist::SaveURIInternal(
1307 : nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
1308 : uint32_t aReferrerPolicy, nsIInputStream *aPostData,
1309 : const char *aExtraHeaders, nsIURI *aFile,
1310 : bool aCalcFileExt, bool aIsPrivate)
1311 : {
1312 0 : NS_ENSURE_ARG_POINTER(aURI);
1313 0 : NS_ENSURE_ARG_POINTER(aFile);
1314 :
1315 0 : nsresult rv = NS_OK;
1316 :
1317 0 : mURI = aURI;
1318 :
1319 0 : nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
1320 0 : if (mPersistFlags & PERSIST_FLAGS_BYPASS_CACHE)
1321 : {
1322 0 : loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
1323 : }
1324 0 : else if (mPersistFlags & PERSIST_FLAGS_FROM_CACHE)
1325 : {
1326 0 : loadFlags |= nsIRequest::LOAD_FROM_CACHE;
1327 : }
1328 :
1329 : // Extract the cache key
1330 0 : nsCOMPtr<nsISupports> cacheKey;
1331 0 : if (aCacheKey)
1332 : {
1333 : // Test if the cache key is actually a web page descriptor (docshell)
1334 : // or session history entry.
1335 0 : nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(aCacheKey);
1336 0 : if (!shEntry)
1337 : {
1338 : nsCOMPtr<nsIWebPageDescriptor> webPageDescriptor =
1339 0 : do_QueryInterface(aCacheKey);
1340 0 : if (webPageDescriptor)
1341 : {
1342 0 : nsCOMPtr<nsISupports> currentDescriptor;
1343 0 : webPageDescriptor->GetCurrentDescriptor(getter_AddRefs(currentDescriptor));
1344 0 : shEntry = do_QueryInterface(currentDescriptor);
1345 : }
1346 : }
1347 :
1348 0 : if (shEntry)
1349 : {
1350 0 : shEntry->GetCacheKey(getter_AddRefs(cacheKey));
1351 : }
1352 : else
1353 : {
1354 : // Assume a plain cache key
1355 0 : cacheKey = aCacheKey;
1356 : }
1357 : }
1358 :
1359 : // Open a channel to the URI
1360 0 : nsCOMPtr<nsIChannel> inputChannel;
1361 0 : rv = NS_NewChannel(getter_AddRefs(inputChannel),
1362 : aURI,
1363 : nsContentUtils::GetSystemPrincipal(),
1364 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1365 : nsIContentPolicy::TYPE_OTHER,
1366 : nullptr, // aLoadGroup
1367 : static_cast<nsIInterfaceRequestor*>(this),
1368 0 : loadFlags);
1369 :
1370 0 : nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
1371 0 : if (pbChannel)
1372 : {
1373 0 : pbChannel->SetPrivate(aIsPrivate);
1374 : }
1375 :
1376 0 : if (NS_FAILED(rv) || inputChannel == nullptr)
1377 : {
1378 0 : EndDownload(NS_ERROR_FAILURE);
1379 0 : return NS_ERROR_FAILURE;
1380 : }
1381 :
1382 : // Disable content conversion
1383 0 : if (mPersistFlags & PERSIST_FLAGS_NO_CONVERSION)
1384 : {
1385 0 : nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
1386 0 : if (encodedChannel)
1387 : {
1388 0 : encodedChannel->SetApplyConversion(false);
1389 : }
1390 : }
1391 :
1392 0 : if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
1393 : {
1394 : nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
1395 0 : do_QueryInterface(inputChannel);
1396 0 : if (httpChannelInternal) {
1397 0 : rv = httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
1398 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1399 : }
1400 : }
1401 :
1402 : // Set the referrer, post data and headers if any
1403 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
1404 0 : if (httpChannel)
1405 : {
1406 : // Referrer
1407 0 : if (aReferrer)
1408 : {
1409 0 : rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
1410 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1411 : }
1412 :
1413 : // Post data
1414 0 : if (aPostData)
1415 : {
1416 0 : nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
1417 0 : if (stream)
1418 : {
1419 : // Rewind the postdata stream
1420 0 : stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1421 0 : nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
1422 0 : NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
1423 : // Attach the postdata to the http channel
1424 0 : uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
1425 : }
1426 : }
1427 :
1428 : // Cache key
1429 0 : nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(httpChannel));
1430 0 : if (cacheChannel && cacheKey)
1431 : {
1432 0 : cacheChannel->SetCacheKey(cacheKey);
1433 : }
1434 :
1435 : // Headers
1436 0 : if (aExtraHeaders)
1437 : {
1438 0 : nsAutoCString oneHeader;
1439 0 : nsAutoCString headerName;
1440 0 : nsAutoCString headerValue;
1441 0 : int32_t crlf = 0;
1442 0 : int32_t colon = 0;
1443 0 : const char *kWhitespace = "\b\t\r\n ";
1444 0 : nsAutoCString extraHeaders(aExtraHeaders);
1445 : while (true)
1446 : {
1447 0 : crlf = extraHeaders.Find("\r\n", true);
1448 0 : if (crlf == -1)
1449 0 : break;
1450 0 : extraHeaders.Mid(oneHeader, 0, crlf);
1451 0 : extraHeaders.Cut(0, crlf + 2);
1452 0 : colon = oneHeader.Find(":");
1453 0 : if (colon == -1)
1454 0 : break; // Should have a colon
1455 0 : oneHeader.Left(headerName, colon);
1456 0 : colon++;
1457 0 : oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
1458 0 : headerName.Trim(kWhitespace);
1459 0 : headerValue.Trim(kWhitespace);
1460 : // Add the header (merging if required)
1461 0 : rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
1462 0 : if (NS_FAILED(rv))
1463 : {
1464 0 : EndDownload(NS_ERROR_FAILURE);
1465 0 : return NS_ERROR_FAILURE;
1466 : }
1467 : }
1468 : }
1469 : }
1470 0 : return SaveChannelInternal(inputChannel, aFile, aCalcFileExt);
1471 : }
1472 :
1473 0 : nsresult nsWebBrowserPersist::SaveChannelInternal(
1474 : nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt)
1475 : {
1476 0 : NS_ENSURE_ARG_POINTER(aChannel);
1477 0 : NS_ENSURE_ARG_POINTER(aFile);
1478 :
1479 : // The default behaviour of SaveChannelInternal is to download the source
1480 : // into a storage stream and upload that to the target. MakeOutputStream
1481 : // special-cases a file target and creates a file output stream directly.
1482 : // We want to special-case a file source and create a file input stream,
1483 : // but we don't need to do this in the case of a file target.
1484 0 : nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(aChannel));
1485 0 : nsCOMPtr<nsIFileURL> fu(do_QueryInterface(aFile));
1486 :
1487 0 : if (fc && !fu) {
1488 0 : nsCOMPtr<nsIInputStream> fileInputStream, bufferedInputStream;
1489 0 : nsresult rv = NS_MaybeOpenChannelUsingOpen2(aChannel,
1490 0 : getter_AddRefs(fileInputStream));
1491 0 : NS_ENSURE_SUCCESS(rv, rv);
1492 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
1493 0 : fileInputStream, BUFFERED_OUTPUT_SIZE);
1494 0 : NS_ENSURE_SUCCESS(rv, rv);
1495 0 : nsAutoCString contentType;
1496 0 : aChannel->GetContentType(contentType);
1497 0 : return StartUpload(bufferedInputStream, aFile, contentType);
1498 : }
1499 :
1500 : // Read from the input channel
1501 0 : nsresult rv = NS_MaybeOpenChannelUsingAsyncOpen2(aChannel, this);
1502 0 : if (rv == NS_ERROR_NO_CONTENT)
1503 : {
1504 : // Assume this is a protocol such as mailto: which does not feed out
1505 : // data and just ignore it.
1506 0 : return NS_SUCCESS_DONT_FIXUP;
1507 : }
1508 :
1509 0 : if (NS_FAILED(rv))
1510 : {
1511 : // Opening failed, but do we care?
1512 0 : if (mPersistFlags & PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS)
1513 : {
1514 0 : SendErrorStatusChange(true, rv, aChannel, aFile);
1515 0 : EndDownload(NS_ERROR_FAILURE);
1516 0 : return NS_ERROR_FAILURE;
1517 : }
1518 0 : return NS_SUCCESS_DONT_FIXUP;
1519 : }
1520 :
1521 : // Add the output transport to the output map with the channel as the key
1522 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aChannel);
1523 0 : mOutputMap.Put(keyPtr, new OutputData(aFile, mURI, aCalcFileExt));
1524 :
1525 0 : return NS_OK;
1526 : }
1527 :
1528 : nsresult
1529 0 : nsWebBrowserPersist::GetExtensionForContentType(const char16_t *aContentType, char16_t **aExt)
1530 : {
1531 0 : NS_ENSURE_ARG_POINTER(aContentType);
1532 0 : NS_ENSURE_ARG_POINTER(aExt);
1533 :
1534 0 : *aExt = nullptr;
1535 :
1536 : nsresult rv;
1537 0 : if (!mMIMEService)
1538 : {
1539 0 : mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
1540 0 : NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
1541 : }
1542 :
1543 0 : nsAutoCString contentType;
1544 0 : contentType.AssignWithConversion(aContentType);
1545 0 : nsAutoCString ext;
1546 0 : rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
1547 0 : if (NS_SUCCEEDED(rv))
1548 : {
1549 0 : *aExt = UTF8ToNewUnicode(ext);
1550 0 : NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
1551 0 : return NS_OK;
1552 : }
1553 :
1554 0 : return NS_ERROR_FAILURE;
1555 : }
1556 :
1557 : nsresult
1558 0 : nsWebBrowserPersist::SaveDocumentDeferred(mozilla::UniquePtr<WalkData>&& aData)
1559 : {
1560 : nsresult rv =
1561 0 : SaveDocumentInternal(aData->mDocument, aData->mFile, aData->mDataPath);
1562 0 : if (NS_FAILED(rv)) {
1563 0 : SendErrorStatusChange(true, rv, nullptr, mURI);
1564 0 : EndDownload(rv);
1565 : }
1566 0 : return rv;
1567 : }
1568 :
1569 0 : nsresult nsWebBrowserPersist::SaveDocumentInternal(
1570 : nsIWebBrowserPersistDocument *aDocument, nsIURI *aFile, nsIURI *aDataPath)
1571 : {
1572 0 : mURI = nullptr;
1573 0 : NS_ENSURE_ARG_POINTER(aDocument);
1574 0 : NS_ENSURE_ARG_POINTER(aFile);
1575 :
1576 0 : nsresult rv = aDocument->SetPersistFlags(mPersistFlags);
1577 0 : NS_ENSURE_SUCCESS(rv, rv);
1578 :
1579 0 : rv = aDocument->GetIsPrivate(&mIsPrivate);
1580 0 : NS_ENSURE_SUCCESS(rv, rv);
1581 :
1582 : // See if we can get the local file representation of this URI
1583 0 : nsCOMPtr<nsIFile> localFile;
1584 0 : rv = GetLocalFileFromURI(aFile, getter_AddRefs(localFile));
1585 :
1586 0 : nsCOMPtr<nsIFile> localDataPath;
1587 0 : if (NS_SUCCEEDED(rv) && aDataPath)
1588 : {
1589 : // See if we can get the local file representation of this URI
1590 0 : rv = GetLocalFileFromURI(aDataPath, getter_AddRefs(localDataPath));
1591 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1592 : }
1593 :
1594 : // Persist the main document
1595 0 : rv = aDocument->GetCharacterSet(mCurrentCharset);
1596 0 : NS_ENSURE_SUCCESS(rv, rv);
1597 0 : nsAutoCString uriSpec;
1598 0 : rv = aDocument->GetDocumentURI(uriSpec);
1599 0 : NS_ENSURE_SUCCESS(rv, rv);
1600 0 : rv = NS_NewURI(getter_AddRefs(mURI), uriSpec, mCurrentCharset.get());
1601 0 : NS_ENSURE_SUCCESS(rv, rv);
1602 0 : rv = aDocument->GetBaseURI(uriSpec);
1603 0 : NS_ENSURE_SUCCESS(rv, rv);
1604 0 : rv = NS_NewURI(getter_AddRefs(mCurrentBaseURI), uriSpec,
1605 0 : mCurrentCharset.get());
1606 0 : NS_ENSURE_SUCCESS(rv, rv);
1607 :
1608 : // Does the caller want to fixup the referenced URIs and save those too?
1609 0 : if (aDataPath)
1610 : {
1611 : // Basic steps are these.
1612 : //
1613 : // 1. Iterate through the document (and subdocuments) building a list
1614 : // of unique URIs.
1615 : // 2. For each URI create an OutputData entry and open a channel to save
1616 : // it. As each URI is saved, discover the mime type and fix up the
1617 : // local filename with the correct extension.
1618 : // 3. Store the document in a list and wait for URI persistence to finish
1619 : // 4. After URI persistence completes save the list of documents,
1620 : // fixing it up as it goes out to file.
1621 :
1622 0 : mCurrentDataPathIsRelative = false;
1623 0 : mCurrentDataPath = aDataPath;
1624 0 : mCurrentRelativePathToData = "";
1625 0 : mCurrentThingsToPersist = 0;
1626 0 : mTargetBaseURI = aFile;
1627 :
1628 : // Determine if the specified data path is relative to the
1629 : // specified file, (e.g. c:\docs\htmldata is relative to
1630 : // c:\docs\myfile.htm, but not to d:\foo\data.
1631 :
1632 : // Starting with the data dir work back through its parents
1633 : // checking if one of them matches the base directory.
1634 :
1635 0 : if (localDataPath && localFile)
1636 : {
1637 0 : nsCOMPtr<nsIFile> baseDir;
1638 0 : localFile->GetParent(getter_AddRefs(baseDir));
1639 :
1640 0 : nsAutoCString relativePathToData;
1641 0 : nsCOMPtr<nsIFile> dataDirParent;
1642 0 : dataDirParent = localDataPath;
1643 0 : while (dataDirParent)
1644 : {
1645 0 : bool sameDir = false;
1646 0 : dataDirParent->Equals(baseDir, &sameDir);
1647 0 : if (sameDir)
1648 : {
1649 0 : mCurrentRelativePathToData = relativePathToData;
1650 0 : mCurrentDataPathIsRelative = true;
1651 0 : break;
1652 : }
1653 :
1654 0 : nsAutoString dirName;
1655 0 : dataDirParent->GetLeafName(dirName);
1656 :
1657 0 : nsAutoCString newRelativePathToData;
1658 0 : newRelativePathToData = NS_ConvertUTF16toUTF8(dirName)
1659 0 : + NS_LITERAL_CSTRING("/")
1660 0 : + relativePathToData;
1661 0 : relativePathToData = newRelativePathToData;
1662 :
1663 0 : nsCOMPtr<nsIFile> newDataDirParent;
1664 0 : rv = dataDirParent->GetParent(getter_AddRefs(newDataDirParent));
1665 0 : dataDirParent = newDataDirParent;
1666 : }
1667 : }
1668 : else
1669 : {
1670 : // generate a relative path if possible
1671 0 : nsCOMPtr<nsIURL> pathToBaseURL(do_QueryInterface(aFile));
1672 0 : if (pathToBaseURL)
1673 : {
1674 0 : nsAutoCString relativePath; // nsACString
1675 0 : if (NS_SUCCEEDED(pathToBaseURL->GetRelativeSpec(aDataPath, relativePath)))
1676 : {
1677 0 : mCurrentDataPathIsRelative = true;
1678 0 : mCurrentRelativePathToData = relativePath;
1679 : }
1680 : }
1681 : }
1682 :
1683 : // Store the document in a list so when URI persistence is done and the
1684 : // filenames of saved URIs are known, the documents can be fixed up and
1685 : // saved
1686 :
1687 0 : auto *docData = new DocData;
1688 0 : docData->mBaseURI = mCurrentBaseURI;
1689 0 : docData->mCharset = mCurrentCharset;
1690 0 : docData->mDocument = aDocument;
1691 0 : docData->mFile = aFile;
1692 0 : mDocList.AppendElement(docData);
1693 :
1694 : // Walk the DOM gathering a list of externally referenced URIs in the uri map
1695 : nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visit =
1696 0 : new OnWalk(this, aFile, localDataPath);
1697 0 : return aDocument->ReadResources(visit);
1698 : }
1699 : else
1700 : {
1701 0 : auto *docData = new DocData;
1702 0 : docData->mBaseURI = mCurrentBaseURI;
1703 0 : docData->mCharset = mCurrentCharset;
1704 0 : docData->mDocument = aDocument;
1705 0 : docData->mFile = aFile;
1706 0 : mDocList.AppendElement(docData);
1707 :
1708 : // Not walking DOMs, so go directly to serialization.
1709 0 : SerializeNextFile();
1710 0 : return NS_OK;
1711 : }
1712 : }
1713 :
1714 : NS_IMETHODIMP
1715 0 : nsWebBrowserPersist::OnWalk::VisitResource(nsIWebBrowserPersistDocument* aDoc,
1716 : const nsACString& aURI)
1717 : {
1718 0 : return mParent->StoreURI(nsAutoCString(aURI).get());
1719 : }
1720 :
1721 : NS_IMETHODIMP
1722 0 : nsWebBrowserPersist::OnWalk::VisitDocument(nsIWebBrowserPersistDocument* aDoc,
1723 : nsIWebBrowserPersistDocument* aSubDoc)
1724 : {
1725 0 : URIData* data = nullptr;
1726 0 : nsAutoCString uriSpec;
1727 0 : nsresult rv = aSubDoc->GetDocumentURI(uriSpec);
1728 0 : NS_ENSURE_SUCCESS(rv, rv);
1729 0 : rv = mParent->StoreURI(uriSpec.get(), false, &data);
1730 0 : NS_ENSURE_SUCCESS(rv, rv);
1731 0 : if (!data) {
1732 : // If the URI scheme isn't persistable, then don't persist.
1733 0 : return NS_OK;
1734 : }
1735 0 : data->mIsSubFrame = true;
1736 0 : return mParent->SaveSubframeContent(aSubDoc, uriSpec, data);
1737 : }
1738 :
1739 :
1740 : NS_IMETHODIMP
1741 0 : nsWebBrowserPersist::OnWalk::EndVisit(nsIWebBrowserPersistDocument* aDoc,
1742 : nsresult aStatus)
1743 : {
1744 0 : if (NS_FAILED(aStatus)) {
1745 0 : mParent->SendErrorStatusChange(true, aStatus, nullptr, mFile);
1746 0 : mParent->EndDownload(aStatus);
1747 0 : return aStatus;
1748 : }
1749 0 : mParent->FinishSaveDocumentInternal(mFile, mDataPath);
1750 0 : return NS_OK;
1751 : }
1752 :
1753 : void
1754 0 : nsWebBrowserPersist::FinishSaveDocumentInternal(nsIURI* aFile,
1755 : nsIFile* aDataPath)
1756 : {
1757 : // If there are things to persist, create a directory to hold them
1758 0 : if (mCurrentThingsToPersist > 0) {
1759 0 : if (aDataPath) {
1760 0 : bool exists = false;
1761 0 : bool haveDir = false;
1762 :
1763 0 : aDataPath->Exists(&exists);
1764 0 : if (exists) {
1765 0 : aDataPath->IsDirectory(&haveDir);
1766 : }
1767 0 : if (!haveDir) {
1768 : nsresult rv =
1769 0 : aDataPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
1770 0 : if (NS_SUCCEEDED(rv)) {
1771 0 : haveDir = true;
1772 : } else {
1773 0 : SendErrorStatusChange(false, rv, nullptr, aFile);
1774 : }
1775 : }
1776 0 : if (!haveDir) {
1777 0 : EndDownload(NS_ERROR_FAILURE);
1778 0 : return;
1779 : }
1780 0 : if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE) {
1781 : // Add to list of things to delete later if all goes wrong
1782 0 : auto *cleanupData = new CleanupData;
1783 0 : cleanupData->mFile = aDataPath;
1784 0 : cleanupData->mIsDirectory = true;
1785 0 : mCleanupList.AppendElement(cleanupData);
1786 : }
1787 : }
1788 : }
1789 :
1790 0 : if (mWalkStack.Length() > 0) {
1791 0 : mozilla::UniquePtr<WalkData> toWalk;
1792 0 : mWalkStack.LastElement().swap(toWalk);
1793 0 : mWalkStack.TruncateLength(mWalkStack.Length() - 1);
1794 : // Bounce this off the event loop to avoid stack overflow.
1795 : typedef StoreCopyPassByRRef<decltype(toWalk)> WalkStorage;
1796 0 : auto saveMethod = &nsWebBrowserPersist::SaveDocumentDeferred;
1797 0 : nsCOMPtr<nsIRunnable> saveLater = NewRunnableMethod<WalkStorage>(
1798 : "nsWebBrowserPersist::FinishSaveDocumentInternal",
1799 : this,
1800 : saveMethod,
1801 0 : mozilla::Move(toWalk));
1802 0 : NS_DispatchToCurrentThread(saveLater);
1803 : } else {
1804 : // Done walking DOMs; on to the serialization phase.
1805 0 : SerializeNextFile();
1806 : }
1807 : }
1808 :
1809 0 : void nsWebBrowserPersist::Cleanup()
1810 : {
1811 0 : mURIMap.Clear();
1812 0 : for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
1813 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(iter.Key());
1814 0 : if (channel) {
1815 0 : channel->Cancel(NS_BINDING_ABORTED);
1816 : }
1817 : }
1818 0 : mOutputMap.Clear();
1819 :
1820 0 : for (auto iter = mUploadList.Iter(); !iter.Done(); iter.Next()) {
1821 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(iter.Key());
1822 0 : if (channel) {
1823 0 : channel->Cancel(NS_BINDING_ABORTED);
1824 : }
1825 : }
1826 0 : mUploadList.Clear();
1827 :
1828 : uint32_t i;
1829 0 : for (i = 0; i < mDocList.Length(); i++) {
1830 0 : DocData *docData = mDocList.ElementAt(i);
1831 0 : delete docData;
1832 : }
1833 0 : mDocList.Clear();
1834 :
1835 0 : for (i = 0; i < mCleanupList.Length(); i++) {
1836 0 : CleanupData *cleanupData = mCleanupList.ElementAt(i);
1837 0 : delete cleanupData;
1838 : }
1839 0 : mCleanupList.Clear();
1840 :
1841 0 : mFilenameList.Clear();
1842 0 : }
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 : uint32_t i;
1854 0 : for (i = 0; i < mCleanupList.Length(); i++)
1855 : {
1856 0 : CleanupData *cleanupData = mCleanupList.ElementAt(i);
1857 0 : nsCOMPtr<nsIFile> 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 : int32_t 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<nsIFile> 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 0 : 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 : nsAutoCString filename;
1963 0 : rv = url->GetFileName(filename);
1964 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1965 0 : nsAutoCString 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 : int32_t lastDot = filename.RFind(".");
1976 0 : nsAutoCString base;
1977 0 : nsAutoCString 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 : int32_t 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() > (uint32_t) 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() > (uint32_t) 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 : nsAutoCString tmpPath;
2027 0 : nsAutoCString tmpBase;
2028 0 : uint32_t duplicateCounter = 1;
2029 : while (true)
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 : SmprintfPointer tmp = mozilla::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.get());
2048 : }
2049 : else
2050 : {
2051 0 : tmpBase = base;
2052 : }
2053 :
2054 0 : tmpPath.Assign(directory);
2055 0 : tmpPath.Append(tmpBase);
2056 0 : tmpPath.Append(ext);
2057 :
2058 : // Test if the name is a duplicate
2059 0 : if (!mFilenameList.Contains(tmpPath))
2060 : {
2061 0 : if (!base.Equals(tmpBase))
2062 : {
2063 0 : filename.Assign(tmpBase);
2064 0 : filename.Append(ext);
2065 0 : nameHasChanged = true;
2066 : }
2067 0 : break;
2068 : }
2069 0 : duplicateCounter++;
2070 0 : }
2071 : }
2072 :
2073 : // Add name to list of those already used
2074 0 : nsAutoCString newFilepath(directory);
2075 0 : newFilepath.Append(filename);
2076 0 : mFilenameList.AppendElement(newFilepath);
2077 :
2078 : // Update the uri accordingly if the filename actually changed
2079 0 : if (nameHasChanged)
2080 : {
2081 : // Final sanity test
2082 0 : if (filename.Length() > kDefaultMaxFilenameLength)
2083 : {
2084 0 : NS_WARNING("Filename wasn't truncated less than the max file length - how can that be?");
2085 0 : return NS_ERROR_FAILURE;
2086 : }
2087 :
2088 0 : nsCOMPtr<nsIFile> localFile;
2089 0 : GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
2090 :
2091 0 : if (localFile)
2092 : {
2093 0 : nsAutoString filenameAsUnichar;
2094 0 : filenameAsUnichar.AssignWithConversion(filename.get());
2095 0 : localFile->SetLeafName(filenameAsUnichar);
2096 :
2097 : // Resync the URI with the file after the extension has been appended
2098 : nsresult rv;
2099 0 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
2100 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2101 0 : fileURL->SetFile(localFile); // this should recalculate uri
2102 : }
2103 : else
2104 : {
2105 0 : url->SetFileName(filename);
2106 : }
2107 : }
2108 :
2109 0 : return NS_OK;
2110 : }
2111 :
2112 :
2113 : nsresult
2114 0 : nsWebBrowserPersist::MakeFilenameFromURI(nsIURI *aURI, nsString &aFilename)
2115 : {
2116 : // Try to get filename from the URI.
2117 0 : nsAutoString fileName;
2118 :
2119 : // Get a suggested file name from the URL but strip it of characters
2120 : // likely to cause the name to be illegal.
2121 :
2122 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
2123 0 : if (url)
2124 : {
2125 0 : nsAutoCString nameFromURL;
2126 0 : url->GetFileName(nameFromURL);
2127 0 : if (mPersistFlags & PERSIST_FLAGS_DONT_CHANGE_FILENAMES)
2128 : {
2129 0 : fileName.AssignWithConversion(NS_UnescapeURL(nameFromURL).BeginReading());
2130 0 : aFilename = fileName;
2131 0 : return NS_OK;
2132 : }
2133 0 : if (!nameFromURL.IsEmpty())
2134 : {
2135 : // Unescape the file name (GetFileName escapes it)
2136 0 : NS_UnescapeURL(nameFromURL);
2137 0 : uint32_t 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 0 : || *p == '.' || *p == '-' || *p == '_' || (*p == ' '))
2144 : {
2145 0 : fileName.Append(char16_t(*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 : // nsIFile (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(char16_t('a')); // 'a' is for arbitrary
2169 : }
2170 :
2171 0 : aFilename = fileName;
2172 0 : return NS_OK;
2173 : }
2174 :
2175 :
2176 : nsresult
2177 0 : nsWebBrowserPersist::CalculateAndAppendFileExt(nsIURI *aURI, nsIChannel *aChannel, nsIURI *aOriginalURIWithExtension)
2178 : {
2179 : nsresult rv;
2180 :
2181 0 : if (!mMIMEService)
2182 : {
2183 0 : mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
2184 0 : NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
2185 : }
2186 :
2187 0 : nsAutoCString contentType;
2188 :
2189 : // Get the content type from the channel
2190 0 : aChannel->GetContentType(contentType);
2191 :
2192 : // Get the content type from the MIME service
2193 0 : if (contentType.IsEmpty())
2194 : {
2195 0 : nsCOMPtr<nsIURI> uri;
2196 0 : aChannel->GetOriginalURI(getter_AddRefs(uri));
2197 0 : mMIMEService->GetTypeFromURI(uri, contentType);
2198 : }
2199 :
2200 : // Append the extension onto the file
2201 0 : if (!contentType.IsEmpty())
2202 : {
2203 0 : nsCOMPtr<nsIMIMEInfo> mimeInfo;
2204 0 : mMIMEService->GetFromTypeAndExtension(
2205 0 : contentType, EmptyCString(), getter_AddRefs(mimeInfo));
2206 :
2207 0 : nsCOMPtr<nsIFile> localFile;
2208 0 : GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
2209 :
2210 0 : if (mimeInfo)
2211 : {
2212 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
2213 0 : NS_ENSURE_TRUE(url, NS_ERROR_FAILURE);
2214 :
2215 0 : nsAutoCString newFileName;
2216 0 : url->GetFileName(newFileName);
2217 :
2218 : // Test if the current extension is current for the mime type
2219 0 : bool hasExtension = false;
2220 0 : int32_t ext = newFileName.RFind(".");
2221 0 : if (ext != -1)
2222 : {
2223 0 : mimeInfo->ExtensionExists(Substring(newFileName, ext + 1), &hasExtension);
2224 : }
2225 :
2226 : // Append the mime file extension
2227 0 : nsAutoCString fileExt;
2228 0 : if (!hasExtension)
2229 : {
2230 : // Test if previous extension is acceptable
2231 0 : nsCOMPtr<nsIURL> oldurl(do_QueryInterface(aOriginalURIWithExtension));
2232 0 : NS_ENSURE_TRUE(oldurl, NS_ERROR_FAILURE);
2233 0 : oldurl->GetFileExtension(fileExt);
2234 0 : bool useOldExt = false;
2235 0 : if (!fileExt.IsEmpty())
2236 : {
2237 0 : mimeInfo->ExtensionExists(fileExt, &useOldExt);
2238 : }
2239 :
2240 : // can't use old extension so use primary extension
2241 0 : if (!useOldExt)
2242 : {
2243 0 : mimeInfo->GetPrimaryExtension(fileExt);
2244 : }
2245 :
2246 0 : if (!fileExt.IsEmpty())
2247 : {
2248 0 : uint32_t newLength = newFileName.Length() + fileExt.Length() + 1;
2249 0 : if (newLength > kDefaultMaxFilenameLength)
2250 : {
2251 0 : if (fileExt.Length() > kDefaultMaxFilenameLength/2)
2252 0 : fileExt.Truncate(kDefaultMaxFilenameLength/2);
2253 :
2254 : uint32_t diff = kDefaultMaxFilenameLength - 1 -
2255 0 : fileExt.Length();
2256 0 : if (newFileName.Length() > diff)
2257 0 : newFileName.Truncate(diff);
2258 : }
2259 0 : newFileName.Append('.');
2260 0 : newFileName.Append(fileExt);
2261 : }
2262 :
2263 0 : if (localFile)
2264 : {
2265 0 : localFile->SetLeafName(NS_ConvertUTF8toUTF16(newFileName));
2266 :
2267 : // Resync the URI with the file after the extension has been appended
2268 0 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
2269 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2270 0 : fileURL->SetFile(localFile); // this should recalculate uri
2271 : }
2272 : else
2273 : {
2274 0 : url->SetFileName(newFileName);
2275 : }
2276 : }
2277 :
2278 : }
2279 : }
2280 :
2281 0 : return NS_OK;
2282 : }
2283 :
2284 : nsresult
2285 0 : nsWebBrowserPersist::MakeOutputStream(
2286 : nsIURI *aURI, nsIOutputStream **aOutputStream)
2287 : {
2288 : nsresult rv;
2289 :
2290 0 : nsCOMPtr<nsIFile> localFile;
2291 0 : GetLocalFileFromURI(aURI, getter_AddRefs(localFile));
2292 0 : if (localFile)
2293 0 : rv = MakeOutputStreamFromFile(localFile, aOutputStream);
2294 : else
2295 0 : rv = MakeOutputStreamFromURI(aURI, aOutputStream);
2296 :
2297 0 : return rv;
2298 : }
2299 :
2300 : nsresult
2301 0 : nsWebBrowserPersist::MakeOutputStreamFromFile(
2302 : nsIFile *aFile, nsIOutputStream **aOutputStream)
2303 : {
2304 0 : nsresult rv = NS_OK;
2305 :
2306 : nsCOMPtr<nsIFileOutputStream> fileOutputStream =
2307 0 : do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
2308 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2309 :
2310 : // XXX brade: get the right flags here!
2311 0 : int32_t ioFlags = -1;
2312 0 : if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE)
2313 0 : ioFlags = PR_APPEND | PR_CREATE_FILE | PR_WRONLY;
2314 0 : rv = fileOutputStream->Init(aFile, ioFlags, -1, 0);
2315 0 : NS_ENSURE_SUCCESS(rv, rv);
2316 :
2317 0 : *aOutputStream = NS_BufferOutputStream(fileOutputStream,
2318 0 : BUFFERED_OUTPUT_SIZE).take();
2319 :
2320 0 : if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
2321 : {
2322 : // Add to cleanup list in event of failure
2323 0 : auto *cleanupData = new CleanupData;
2324 0 : if (!cleanupData) {
2325 0 : NS_RELEASE(*aOutputStream);
2326 0 : return NS_ERROR_OUT_OF_MEMORY;
2327 : }
2328 0 : cleanupData->mFile = aFile;
2329 0 : cleanupData->mIsDirectory = false;
2330 0 : mCleanupList.AppendElement(cleanupData);
2331 : }
2332 :
2333 0 : return NS_OK;
2334 : }
2335 :
2336 : nsresult
2337 0 : nsWebBrowserPersist::MakeOutputStreamFromURI(
2338 : nsIURI *aURI, nsIOutputStream **aOutputStream)
2339 : {
2340 0 : uint32_t segsize = 8192;
2341 0 : uint32_t maxsize = uint32_t(-1);
2342 0 : nsCOMPtr<nsIStorageStream> storStream;
2343 0 : nsresult rv = NS_NewStorageStream(segsize, maxsize, getter_AddRefs(storStream));
2344 0 : NS_ENSURE_SUCCESS(rv, rv);
2345 :
2346 0 : NS_ENSURE_SUCCESS(CallQueryInterface(storStream, aOutputStream), NS_ERROR_FAILURE);
2347 0 : return NS_OK;
2348 : }
2349 :
2350 : void
2351 0 : nsWebBrowserPersist::FinishDownload()
2352 : {
2353 0 : EndDownload(NS_OK);
2354 0 : }
2355 :
2356 : void
2357 0 : nsWebBrowserPersist::EndDownload(nsresult aResult)
2358 : {
2359 : // Store the error code in the result if it is an error
2360 0 : if (NS_SUCCEEDED(mPersistResult) && NS_FAILED(aResult))
2361 : {
2362 0 : mPersistResult = aResult;
2363 : }
2364 :
2365 : // mCompleted needs to be set before issuing the stop notification.
2366 : // (Bug 1224437)
2367 0 : mCompleted = true;
2368 : // State stop notification
2369 0 : if (mProgressListener) {
2370 0 : mProgressListener->OnStateChange(nullptr, nullptr,
2371 : nsIWebProgressListener::STATE_STOP
2372 0 : | nsIWebProgressListener::STATE_IS_NETWORK, mPersistResult);
2373 : }
2374 :
2375 : // Do file cleanup if required
2376 0 : if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
2377 : {
2378 0 : CleanupLocalFiles();
2379 : }
2380 :
2381 : // Cleanup the channels
2382 0 : Cleanup();
2383 :
2384 0 : mProgressListener = nullptr;
2385 0 : mProgressListener2 = nullptr;
2386 0 : mEventSink = nullptr;
2387 0 : }
2388 :
2389 : nsresult
2390 0 : nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
2391 : {
2392 0 : NS_ENSURE_ARG_POINTER(aNewChannel);
2393 :
2394 : // Iterate through existing open channels looking for one with a URI
2395 : // matching the one specified.
2396 0 : nsCOMPtr<nsIURI> originalURI;
2397 0 : aNewChannel->GetOriginalURI(getter_AddRefs(originalURI));
2398 0 : nsISupports* matchingKey = nullptr;
2399 0 : for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
2400 0 : nsISupports* key = iter.Key();
2401 0 : nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(key);
2402 0 : nsCOMPtr<nsIURI> thisURI;
2403 :
2404 0 : thisChannel->GetOriginalURI(getter_AddRefs(thisURI));
2405 :
2406 : // Compare this channel's URI to the one passed in.
2407 0 : bool matchingURI = false;
2408 0 : thisURI->Equals(originalURI, &matchingURI);
2409 0 : if (matchingURI) {
2410 0 : matchingKey = key;
2411 0 : break;
2412 : }
2413 : }
2414 :
2415 0 : if (matchingKey) {
2416 : // If a match was found, remove the data entry with the old channel
2417 : // key and re-add it with the new channel key.
2418 0 : nsAutoPtr<OutputData> outputData;
2419 0 : mOutputMap.Remove(matchingKey, &outputData);
2420 0 : NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
2421 :
2422 : // Store data again with new channel unless told to ignore redirects.
2423 0 : if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA)) {
2424 0 : nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
2425 0 : mOutputMap.Put(keyPtr, outputData.forget());
2426 : }
2427 : }
2428 :
2429 0 : return NS_OK;
2430 : }
2431 :
2432 : void
2433 0 : nsWebBrowserPersist::CalcTotalProgress()
2434 : {
2435 0 : mTotalCurrentProgress = 0;
2436 0 : mTotalMaxProgress = 0;
2437 :
2438 0 : if (mOutputMap.Count() > 0) {
2439 : // Total up the progress of each output stream
2440 0 : for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
2441 : // Only count toward total progress if destination file is local.
2442 0 : OutputData* data = iter.UserData();
2443 0 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(data->mFile);
2444 0 : if (fileURL) {
2445 0 : mTotalCurrentProgress += data->mSelfProgress;
2446 0 : mTotalMaxProgress += data->mSelfProgressMax;
2447 : }
2448 : }
2449 : }
2450 :
2451 0 : if (mUploadList.Count() > 0) {
2452 : // Total up the progress of each upload
2453 0 : for (auto iter = mUploadList.Iter(); !iter.Done(); iter.Next()) {
2454 0 : UploadData* data = iter.UserData();
2455 0 : if (data) {
2456 0 : mTotalCurrentProgress += data->mSelfProgress;
2457 0 : mTotalMaxProgress += data->mSelfProgressMax;
2458 : }
2459 : }
2460 : }
2461 :
2462 : // XXX this code seems pretty bogus and pointless
2463 0 : if (mTotalCurrentProgress == 0 && mTotalMaxProgress == 0)
2464 : {
2465 : // No output streams so we must be complete
2466 0 : mTotalCurrentProgress = 10000;
2467 0 : mTotalMaxProgress = 10000;
2468 : }
2469 0 : }
2470 :
2471 : nsresult
2472 0 : nsWebBrowserPersist::StoreURI(
2473 : const char *aURI, bool aNeedsPersisting, URIData **aData)
2474 : {
2475 0 : NS_ENSURE_ARG_POINTER(aURI);
2476 :
2477 0 : nsCOMPtr<nsIURI> uri;
2478 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri),
2479 0 : nsDependentCString(aURI),
2480 : mCurrentCharset.get(),
2481 0 : mCurrentBaseURI);
2482 0 : NS_ENSURE_SUCCESS(rv, rv);
2483 :
2484 0 : return StoreURI(uri, aNeedsPersisting, aData);
2485 : }
2486 :
2487 : nsresult
2488 0 : nsWebBrowserPersist::StoreURI(
2489 : nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
2490 : {
2491 0 : NS_ENSURE_ARG_POINTER(aURI);
2492 0 : if (aData)
2493 : {
2494 0 : *aData = nullptr;
2495 : }
2496 :
2497 : // Test if this URI should be persisted. By default
2498 : // we should assume the URI is persistable.
2499 : bool doNotPersistURI;
2500 : nsresult rv = NS_URIChainHasFlags(aURI,
2501 : nsIProtocolHandler::URI_NON_PERSISTABLE,
2502 0 : &doNotPersistURI);
2503 0 : if (NS_FAILED(rv))
2504 : {
2505 0 : doNotPersistURI = false;
2506 : }
2507 :
2508 0 : if (doNotPersistURI)
2509 : {
2510 0 : return NS_OK;
2511 : }
2512 :
2513 0 : URIData *data = nullptr;
2514 0 : MakeAndStoreLocalFilenameInURIMap(aURI, aNeedsPersisting, &data);
2515 0 : if (aData)
2516 : {
2517 0 : *aData = data;
2518 : }
2519 :
2520 0 : return NS_OK;
2521 : }
2522 :
2523 : nsresult
2524 0 : nsWebBrowserPersist::URIData::GetLocalURI(nsIURI *targetBaseURI, nsCString& aSpecOut)
2525 : {
2526 0 : aSpecOut.SetIsVoid(true);
2527 0 : if (!mNeedsFixup) {
2528 0 : return NS_OK;
2529 : }
2530 : nsresult rv;
2531 0 : nsCOMPtr<nsIURI> fileAsURI;
2532 0 : if (mFile) {
2533 0 : rv = mFile->Clone(getter_AddRefs(fileAsURI));
2534 0 : NS_ENSURE_SUCCESS(rv, rv);
2535 : } else {
2536 0 : rv = mDataPath->Clone(getter_AddRefs(fileAsURI));
2537 0 : NS_ENSURE_SUCCESS(rv, rv);
2538 0 : rv = AppendPathToURI(fileAsURI, mFilename);
2539 0 : NS_ENSURE_SUCCESS(rv, rv);
2540 : }
2541 :
2542 : // remove username/password if present
2543 0 : fileAsURI->SetUserPass(EmptyCString());
2544 :
2545 : // reset node attribute
2546 : // Use relative or absolute links
2547 0 : if (mDataPathIsRelative) {
2548 0 : bool isEqual = false;
2549 0 : if (NS_SUCCEEDED(mRelativeDocumentURI->Equals(targetBaseURI, &isEqual)) && isEqual) {
2550 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(fileAsURI));
2551 0 : if (!url) {
2552 0 : return NS_ERROR_FAILURE;
2553 : }
2554 :
2555 0 : nsAutoCString filename;
2556 0 : url->GetFileName(filename);
2557 :
2558 0 : nsAutoCString rawPathURL(mRelativePathToData);
2559 0 : rawPathURL.Append(filename);
2560 :
2561 0 : rv = NS_EscapeURL(rawPathURL, esc_FilePath, aSpecOut, fallible);
2562 0 : NS_ENSURE_SUCCESS(rv, rv);
2563 : } else {
2564 0 : nsAutoCString rawPathURL;
2565 :
2566 0 : nsCOMPtr<nsIFile> dataFile;
2567 0 : rv = GetLocalFileFromURI(mFile, getter_AddRefs(dataFile));
2568 0 : NS_ENSURE_SUCCESS(rv, rv);
2569 :
2570 0 : nsCOMPtr<nsIFile> docFile;
2571 0 : rv = GetLocalFileFromURI(targetBaseURI, getter_AddRefs(docFile));
2572 0 : NS_ENSURE_SUCCESS(rv, rv);
2573 :
2574 0 : nsCOMPtr<nsIFile> parentDir;
2575 0 : rv = docFile->GetParent(getter_AddRefs(parentDir));
2576 0 : NS_ENSURE_SUCCESS(rv, rv);
2577 :
2578 0 : rv = dataFile->GetRelativePath(parentDir, rawPathURL);
2579 0 : NS_ENSURE_SUCCESS(rv, rv);
2580 :
2581 0 : rv = NS_EscapeURL(rawPathURL, esc_FilePath, aSpecOut, fallible);
2582 0 : NS_ENSURE_SUCCESS(rv, rv);
2583 : }
2584 : } else {
2585 0 : fileAsURI->GetSpec(aSpecOut);
2586 : }
2587 0 : if (mIsSubFrame) {
2588 0 : AppendUTF16toUTF8(mSubFrameExt, aSpecOut);
2589 : }
2590 :
2591 0 : return NS_OK;
2592 : }
2593 :
2594 : bool
2595 0 : nsWebBrowserPersist::DocumentEncoderExists(const char *aContentType)
2596 : {
2597 : // Check if there is an encoder for the desired content type.
2598 0 : nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
2599 0 : contractID.Append(aContentType);
2600 :
2601 0 : nsCOMPtr<nsIComponentRegistrar> registrar;
2602 0 : NS_GetComponentRegistrar(getter_AddRefs(registrar));
2603 0 : if (registrar)
2604 : {
2605 : bool result;
2606 0 : nsresult rv = registrar->IsContractIDRegistered(contractID.get(),
2607 0 : &result);
2608 0 : if (NS_SUCCEEDED(rv) && result)
2609 : {
2610 0 : return true;
2611 : }
2612 : }
2613 0 : return false;
2614 : }
2615 :
2616 : nsresult
2617 0 : nsWebBrowserPersist::SaveSubframeContent(
2618 : nsIWebBrowserPersistDocument *aFrameContent,
2619 : const nsCString& aURISpec,
2620 : URIData *aData)
2621 : {
2622 0 : NS_ENSURE_ARG_POINTER(aData);
2623 :
2624 : // Extract the content type for the frame's contents.
2625 0 : nsAutoCString contentType;
2626 0 : nsresult rv = aFrameContent->GetContentType(contentType);
2627 0 : NS_ENSURE_SUCCESS(rv, rv);
2628 :
2629 0 : nsXPIDLString ext;
2630 0 : GetExtensionForContentType(NS_ConvertASCIItoUTF16(contentType).get(),
2631 0 : getter_Copies(ext));
2632 :
2633 : // We must always have an extension so we will try to re-assign
2634 : // the original extension if GetExtensionForContentType fails.
2635 0 : if (ext.IsEmpty()) {
2636 0 : nsCOMPtr<nsIURI> docURI;
2637 0 : rv = NS_NewURI(getter_AddRefs(docURI), aURISpec, mCurrentCharset.get());
2638 0 : NS_ENSURE_SUCCESS(rv, rv);
2639 :
2640 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(docURI, &rv));
2641 0 : nsAutoCString extension;
2642 0 : if (NS_SUCCEEDED(rv)) {
2643 0 : url->GetFileExtension(extension);
2644 : } else {
2645 0 : extension.AssignLiteral("htm");
2646 : }
2647 0 : aData->mSubFrameExt.Assign(char16_t('.'));
2648 0 : AppendUTF8toUTF16(extension, aData->mSubFrameExt);
2649 : } else {
2650 0 : aData->mSubFrameExt.Assign(char16_t('.'));
2651 0 : aData->mSubFrameExt.Append(ext);
2652 : }
2653 :
2654 0 : nsString filenameWithExt = aData->mFilename;
2655 0 : filenameWithExt.Append(aData->mSubFrameExt);
2656 :
2657 : // Work out the path for the subframe
2658 0 : nsCOMPtr<nsIURI> frameURI;
2659 0 : rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
2660 0 : NS_ENSURE_SUCCESS(rv, rv);
2661 0 : rv = AppendPathToURI(frameURI, filenameWithExt);
2662 0 : NS_ENSURE_SUCCESS(rv, rv);
2663 :
2664 : // Work out the path for the subframe data
2665 0 : nsCOMPtr<nsIURI> frameDataURI;
2666 0 : rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
2667 0 : NS_ENSURE_SUCCESS(rv, rv);
2668 0 : nsAutoString newFrameDataPath(aData->mFilename);
2669 :
2670 : // Append _data
2671 0 : newFrameDataPath.AppendLiteral("_data");
2672 0 : rv = AppendPathToURI(frameDataURI, newFrameDataPath);
2673 0 : NS_ENSURE_SUCCESS(rv, rv);
2674 :
2675 : // Make frame document & data path conformant and unique
2676 0 : rv = CalculateUniqueFilename(frameURI);
2677 0 : NS_ENSURE_SUCCESS(rv, rv);
2678 0 : rv = CalculateUniqueFilename(frameDataURI);
2679 0 : NS_ENSURE_SUCCESS(rv, rv);
2680 :
2681 0 : mCurrentThingsToPersist++;
2682 :
2683 : // We shouldn't use SaveDocumentInternal for the contents
2684 : // of frames that are not documents, e.g. images.
2685 0 : if (DocumentEncoderExists(contentType.get())) {
2686 0 : auto toWalk = mozilla::MakeUnique<WalkData>();
2687 0 : toWalk->mDocument = aFrameContent;
2688 0 : toWalk->mFile = frameURI;
2689 0 : toWalk->mDataPath = frameDataURI;
2690 0 : mWalkStack.AppendElement(mozilla::Move(toWalk));
2691 : } else {
2692 0 : rv = StoreURI(aURISpec.get());
2693 : }
2694 0 : NS_ENSURE_SUCCESS(rv, rv);
2695 :
2696 : // Store the updated uri to the frame
2697 0 : aData->mFile = frameURI;
2698 0 : aData->mSubFrameExt.Truncate(); // we already put this in frameURI
2699 :
2700 0 : return NS_OK;
2701 : }
2702 :
2703 : nsresult
2704 0 : nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
2705 : {
2706 0 : nsresult rv = NS_OK;
2707 0 : *aChannel = nullptr;
2708 :
2709 0 : rv = NS_NewChannel(aChannel,
2710 : aURI,
2711 : nsContentUtils::GetSystemPrincipal(),
2712 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
2713 0 : nsIContentPolicy::TYPE_OTHER);
2714 0 : NS_ENSURE_SUCCESS(rv, rv);
2715 0 : NS_ENSURE_ARG_POINTER(*aChannel);
2716 :
2717 0 : rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
2718 0 : NS_ENSURE_SUCCESS(rv, rv);
2719 0 : return NS_OK;
2720 : }
2721 :
2722 :
2723 : // we store the current location as the key (absolutized version of domnode's attribute's value)
2724 : nsresult
2725 0 : nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
2726 : nsIURI *aURI, bool aNeedsPersisting, URIData **aData)
2727 : {
2728 0 : NS_ENSURE_ARG_POINTER(aURI);
2729 :
2730 0 : nsAutoCString spec;
2731 0 : nsresult rv = aURI->GetSpec(spec);
2732 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2733 :
2734 : // Create a sensibly named filename for the URI and store in the URI map
2735 : URIData *data;
2736 0 : if (mURIMap.Get(spec, &data))
2737 : {
2738 0 : if (aNeedsPersisting)
2739 : {
2740 0 : data->mNeedsPersisting = true;
2741 : }
2742 0 : if (aData)
2743 : {
2744 0 : *aData = data;
2745 : }
2746 0 : return NS_OK;
2747 : }
2748 :
2749 : // Create a unique file name for the uri
2750 0 : nsString filename;
2751 0 : rv = MakeFilenameFromURI(aURI, filename);
2752 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
2753 :
2754 : // Store the file name
2755 0 : data = new URIData;
2756 0 : NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
2757 :
2758 0 : data->mNeedsPersisting = aNeedsPersisting;
2759 0 : data->mNeedsFixup = true;
2760 0 : data->mFilename = filename;
2761 0 : data->mSaved = false;
2762 0 : data->mIsSubFrame = false;
2763 0 : data->mDataPath = mCurrentDataPath;
2764 0 : data->mDataPathIsRelative = mCurrentDataPathIsRelative;
2765 0 : data->mRelativePathToData = mCurrentRelativePathToData;
2766 0 : data->mRelativeDocumentURI = mTargetBaseURI;
2767 0 : data->mCharset = mCurrentCharset;
2768 :
2769 0 : if (aNeedsPersisting)
2770 0 : mCurrentThingsToPersist++;
2771 :
2772 0 : mURIMap.Put(spec, data);
2773 0 : if (aData)
2774 : {
2775 0 : *aData = data;
2776 : }
2777 :
2778 0 : return NS_OK;
2779 : }
2780 :
2781 : // Decide if we need to apply conversion to the passed channel.
2782 0 : void nsWebBrowserPersist::SetApplyConversionIfNeeded(nsIChannel *aChannel)
2783 : {
2784 0 : nsresult rv = NS_OK;
2785 0 : nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aChannel, &rv);
2786 0 : if (NS_FAILED(rv))
2787 0 : return;
2788 :
2789 : // Set the default conversion preference:
2790 0 : encChannel->SetApplyConversion(false);
2791 :
2792 0 : nsCOMPtr<nsIURI> thisURI;
2793 0 : aChannel->GetURI(getter_AddRefs(thisURI));
2794 0 : nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(thisURI));
2795 0 : if (!sourceURL)
2796 0 : return;
2797 0 : nsAutoCString extension;
2798 0 : sourceURL->GetFileExtension(extension);
2799 :
2800 0 : nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
2801 0 : encChannel->GetContentEncodings(getter_AddRefs(encEnum));
2802 0 : if (!encEnum)
2803 0 : return;
2804 : nsCOMPtr<nsIExternalHelperAppService> helperAppService =
2805 0 : do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
2806 0 : if (NS_FAILED(rv))
2807 0 : return;
2808 : bool hasMore;
2809 0 : rv = encEnum->HasMore(&hasMore);
2810 0 : if (NS_SUCCEEDED(rv) && hasMore)
2811 : {
2812 0 : nsAutoCString encType;
2813 0 : rv = encEnum->GetNext(encType);
2814 0 : if (NS_SUCCEEDED(rv))
2815 : {
2816 0 : bool applyConversion = false;
2817 0 : rv = helperAppService->ApplyDecodingForExtension(extension, encType,
2818 0 : &applyConversion);
2819 0 : if (NS_SUCCEEDED(rv))
2820 0 : encChannel->SetApplyConversion(applyConversion);
2821 : }
2822 : }
2823 : }
|