Coverage Report

Created: 2018-09-25 14:53

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