Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/webbrowserpersist/WebBrowserPersistLocalDocument.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 "WebBrowserPersistLocalDocument.h"
7
#include "WebBrowserPersistDocumentParent.h"
8
9
#include "mozilla/dom/Attr.h"
10
#include "mozilla/dom/Comment.h"
11
#include "mozilla/dom/Element.h"
12
#include "mozilla/dom/HTMLAnchorElement.h"
13
#include "mozilla/dom/HTMLAreaElement.h"
14
#include "mozilla/dom/HTMLInputElement.h"
15
#include "mozilla/dom/HTMLLinkElement.h"
16
#include "mozilla/dom/HTMLObjectElement.h"
17
#include "mozilla/dom/HTMLOptionElement.h"
18
#include "mozilla/dom/HTMLSharedElement.h"
19
#include "mozilla/dom/HTMLTextAreaElement.h"
20
#include "mozilla/dom/NodeFilterBinding.h"
21
#include "mozilla/dom/ProcessingInstruction.h"
22
#include "mozilla/dom/TabParent.h"
23
#include "mozilla/dom/TreeWalker.h"
24
#include "mozilla/Unused.h"
25
#include "nsComponentManagerUtils.h"
26
#include "nsContentUtils.h"
27
#include "nsContentCID.h"
28
#include "nsCycleCollectionParticipant.h"
29
#include "nsDOMAttributeMap.h"
30
#include "nsFrameLoader.h"
31
#include "nsGlobalWindowOuter.h"
32
#include "nsIComponentRegistrar.h"
33
#include "nsIContent.h"
34
#include "nsIDOMWindowUtils.h"
35
#include "nsIDocShell.h"
36
#include "nsIDocument.h"
37
#include "nsIDocumentEncoder.h"
38
#include "nsILoadContext.h"
39
#include "nsIProtocolHandler.h"
40
#include "nsISHEntry.h"
41
#include "nsISupportsPrimitives.h"
42
#include "nsITabParent.h"
43
#include "nsIURIMutator.h"
44
#include "nsIWebBrowserPersist.h"
45
#include "nsIWebNavigation.h"
46
#include "nsIWebPageDescriptor.h"
47
#include "nsNetUtil.h"
48
49
namespace mozilla {
50
51
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument)
52
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument)
53
54
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument)
55
0
  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument)
56
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
57
0
NS_INTERFACE_MAP_END
58
59
NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument, mDocument)
60
61
62
WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument(nsIDocument* aDocument)
63
: mDocument(aDocument)
64
, mPersistFlags(0)
65
0
{
66
0
    MOZ_ASSERT(mDocument);
67
0
}
68
69
0
WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument() = default;
70
71
NS_IMETHODIMP
72
WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags)
73
0
{
74
0
    mPersistFlags = aFlags;
75
0
    return NS_OK;
76
0
}
77
78
NS_IMETHODIMP
79
WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags)
80
0
{
81
0
    *aFlags = mPersistFlags;
82
0
    return NS_OK;
83
0
}
84
85
NS_IMETHODIMP
86
WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate)
87
0
{
88
0
    nsCOMPtr<nsILoadContext> privacyContext = mDocument->GetLoadContext();
89
0
    *aIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
90
0
    return NS_OK;
91
0
}
92
93
NS_IMETHODIMP
94
WebBrowserPersistLocalDocument::GetDocumentURI(nsACString& aURISpec)
95
0
{
96
0
    nsCOMPtr<nsIURI> uri = mDocument->GetDocumentURI();
97
0
    if (!uri) {
98
0
        return NS_ERROR_UNEXPECTED;
99
0
    }
100
0
    return uri->GetSpec(aURISpec);
101
0
}
102
103
NS_IMETHODIMP
104
WebBrowserPersistLocalDocument::GetBaseURI(nsACString& aURISpec)
105
0
{
106
0
    nsCOMPtr<nsIURI> uri = GetBaseURI();
107
0
    if (!uri) {
108
0
        return NS_ERROR_UNEXPECTED;
109
0
    }
110
0
    return uri->GetSpec(aURISpec);
111
0
}
112
113
NS_IMETHODIMP
114
WebBrowserPersistLocalDocument::GetContentType(nsACString& aContentType)
115
0
{
116
0
    nsAutoString utf16Type;
117
0
    mDocument->GetContentType(utf16Type);
118
0
    CopyUTF16toUTF8(utf16Type, aContentType);
119
0
    return NS_OK;
120
0
}
121
122
NS_IMETHODIMP
123
WebBrowserPersistLocalDocument::GetCharacterSet(nsACString& aCharSet)
124
0
{
125
0
    GetCharacterSet()->Name(aCharSet);
126
0
    return NS_OK;
127
0
}
128
129
NS_IMETHODIMP
130
WebBrowserPersistLocalDocument::GetTitle(nsAString& aTitle)
131
0
{
132
0
    nsAutoString titleBuffer;
133
0
    mDocument->GetTitle(titleBuffer);
134
0
    aTitle = titleBuffer;
135
0
    return NS_OK;
136
0
}
137
138
NS_IMETHODIMP
139
WebBrowserPersistLocalDocument::GetReferrer(nsAString& aReferrer)
140
0
{
141
0
    mDocument->GetReferrer(aReferrer);
142
0
    return NS_OK;
143
0
}
144
145
NS_IMETHODIMP
146
WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD)
147
0
{
148
0
    nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
149
0
    if (NS_WARN_IF(!window)) {
150
0
        aCD.SetIsVoid(true);
151
0
        return NS_OK;
152
0
    }
153
0
    nsCOMPtr<nsIDOMWindowUtils> utils =
154
0
        nsGlobalWindowOuter::Cast(window)->WindowUtils();
155
0
    nsresult rv = utils->GetDocumentMetadata(
156
0
        NS_LITERAL_STRING("content-disposition"), aCD);
157
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
158
0
        aCD.SetIsVoid(true);
159
0
    }
160
0
    return NS_OK;
161
0
}
162
163
NS_IMETHODIMP
164
WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
165
0
{
166
0
    *aKey = 0;
167
0
    nsCOMPtr<nsISHEntry> history = GetHistory();
168
0
    if (history) {
169
0
        history->GetCacheKey(aKey);
170
0
    }
171
0
    return NS_OK;
172
0
}
173
174
NS_IMETHODIMP
175
WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream)
176
0
{
177
0
    nsCOMPtr<nsISHEntry> history = GetHistory();
178
0
    if (!history) {
179
0
        *aStream = nullptr;
180
0
        return NS_OK;
181
0
    }
182
0
    return history->GetPostData(aStream);
183
0
}
184
185
NS_IMETHODIMP
186
WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal** aPrincipal)
187
0
{
188
0
  nsCOMPtr<nsIPrincipal> nodePrincipal = mDocument->NodePrincipal();
189
0
  nodePrincipal.forget(aPrincipal);
190
0
  return NS_OK;
191
0
}
192
193
already_AddRefed<nsISHEntry>
194
WebBrowserPersistLocalDocument::GetHistory()
195
0
{
196
0
    nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
197
0
    if (NS_WARN_IF(!window)) {
198
0
        return nullptr;
199
0
    }
200
0
    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
201
0
    if (NS_WARN_IF(!webNav)) {
202
0
        return nullptr;
203
0
    }
204
0
    nsCOMPtr<nsIWebPageDescriptor> desc = do_QueryInterface(webNav);
205
0
    if (NS_WARN_IF(!desc)) {
206
0
        return nullptr;
207
0
    }
208
0
    nsCOMPtr<nsISupports> curDesc;
209
0
    nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc));
210
0
    // This can fail if, e.g., the document is a Print Preview.
211
0
    if (NS_FAILED(rv) || NS_WARN_IF(!curDesc)) {
212
0
        return nullptr;
213
0
    }
214
0
    nsCOMPtr<nsISHEntry> history = do_QueryInterface(curDesc);
215
0
    return history.forget();
216
0
}
217
218
NotNull<const Encoding*>
219
WebBrowserPersistLocalDocument::GetCharacterSet() const
220
0
{
221
0
    return mDocument->GetDocumentCharacterSet();
222
0
}
223
224
uint32_t
225
WebBrowserPersistLocalDocument::GetPersistFlags() const
226
0
{
227
0
    return mPersistFlags;
228
0
}
229
230
231
already_AddRefed<nsIURI>
232
WebBrowserPersistLocalDocument::GetBaseURI() const
233
0
{
234
0
    return mDocument->GetBaseURI();
235
0
}
236
237
namespace {
238
239
// Helper class for ReadResources().
240
class ResourceReader final : public nsIWebBrowserPersistDocumentReceiver {
241
public:
242
    ResourceReader(WebBrowserPersistLocalDocument* aParent,
243
                   nsIWebBrowserPersistResourceVisitor* aVisitor);
244
    nsresult OnWalkDOMNode(nsINode* aNode);
245
246
    // This is called both to indicate the end of the document walk
247
    // and when a subdocument is (maybe asynchronously) sent to the
248
    // visitor.  The call to EndVisit needs to happen after both of
249
    // those have finished.
250
    void DocumentDone(nsresult aStatus);
251
252
    NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
253
    NS_DECL_ISUPPORTS
254
255
private:
256
    RefPtr<WebBrowserPersistLocalDocument> mParent;
257
    nsCOMPtr<nsIWebBrowserPersistResourceVisitor> mVisitor;
258
    nsCOMPtr<nsIURI> mCurrentBaseURI;
259
    uint32_t mPersistFlags;
260
261
    // The number of DocumentDone calls after which EndVisit will be
262
    // called on the visitor.  Counts the main document if it's still
263
    // being walked and any outstanding asynchronous subdocument
264
    // StartPersistence calls.
265
    size_t mOutstandingDocuments;
266
    // Collects the status parameters to DocumentDone calls.
267
    nsresult mEndStatus;
268
269
    nsresult OnWalkURI(const nsACString& aURISpec);
270
    nsresult OnWalkURI(nsIURI* aURI);
271
    nsresult OnWalkAttribute(Element* aElement,
272
                             const char* aAttribute,
273
                             const char* aNamespaceURI = "");
274
    nsresult OnWalkSubframe(nsINode* aNode);
275
276
    ~ResourceReader();
277
278
    using IWBP = nsIWebBrowserPersist;
279
};
280
281
NS_IMPL_ISUPPORTS(ResourceReader, nsIWebBrowserPersistDocumentReceiver)
282
283
ResourceReader::ResourceReader(WebBrowserPersistLocalDocument* aParent,
284
                               nsIWebBrowserPersistResourceVisitor* aVisitor)
285
: mParent(aParent)
286
, mVisitor(aVisitor)
287
, mCurrentBaseURI(aParent->GetBaseURI())
288
, mPersistFlags(aParent->GetPersistFlags())
289
, mOutstandingDocuments(1)
290
, mEndStatus(NS_OK)
291
0
{
292
0
    MOZ_ASSERT(mCurrentBaseURI);
293
0
}
294
295
ResourceReader::~ResourceReader()
296
0
{
297
0
    MOZ_ASSERT(mOutstandingDocuments == 0);
298
0
}
299
300
void
301
ResourceReader::DocumentDone(nsresult aStatus)
302
0
{
303
0
    MOZ_ASSERT(mOutstandingDocuments > 0);
304
0
    if (NS_SUCCEEDED(mEndStatus)) {
305
0
        mEndStatus = aStatus;
306
0
    }
307
0
    if (--mOutstandingDocuments == 0) {
308
0
        mVisitor->EndVisit(mParent, mEndStatus);
309
0
    }
310
0
}
311
312
nsresult
313
ResourceReader::OnWalkSubframe(nsINode* aNode)
314
0
{
315
0
    nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aNode);
316
0
    NS_ENSURE_STATE(loaderOwner);
317
0
    RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
318
0
    NS_ENSURE_STATE(loader);
319
0
320
0
    ++mOutstandingDocuments;
321
0
    // Pass in 0 as the outer window ID so that we start
322
0
    // persisting the root of this subframe, and not some other
323
0
    // subframe child of this subframe.
324
0
    ErrorResult err;
325
0
    loader->StartPersistence(0, this, err);
326
0
    nsresult rv = err.StealNSResult();
327
0
    if (NS_FAILED(rv)) {
328
0
        if (rv == NS_ERROR_NO_CONTENT) {
329
0
            // Just ignore frames with no content document.
330
0
            rv = NS_OK;
331
0
        }
332
0
        // StartPersistence won't eventually call this if it failed,
333
0
        // so this does so (to keep mOutstandingDocuments correct).
334
0
        DocumentDone(rv);
335
0
    }
336
0
    return rv;
337
0
}
338
339
NS_IMETHODIMP
340
ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument* aDocument)
341
0
{
342
0
    mVisitor->VisitDocument(mParent, aDocument);
343
0
    DocumentDone(NS_OK);
344
0
    return NS_OK;
345
0
}
346
347
NS_IMETHODIMP
348
ResourceReader::OnError(nsresult aFailure)
349
0
{
350
0
    DocumentDone(aFailure);
351
0
    return NS_OK;
352
0
}
353
354
nsresult
355
ResourceReader::OnWalkURI(nsIURI* aURI)
356
0
{
357
0
    // Test if this URI should be persisted. By default
358
0
    // we should assume the URI  is persistable.
359
0
    bool doNotPersistURI;
360
0
    nsresult rv = NS_URIChainHasFlags(aURI,
361
0
                                      nsIProtocolHandler::URI_NON_PERSISTABLE,
362
0
                                      &doNotPersistURI);
363
0
    if (NS_SUCCEEDED(rv) && doNotPersistURI) {
364
0
        return NS_OK;
365
0
    }
366
0
367
0
    nsAutoCString stringURI;
368
0
    rv = aURI->GetSpec(stringURI);
369
0
    NS_ENSURE_SUCCESS(rv, rv);
370
0
    return mVisitor->VisitResource(mParent, stringURI);
371
0
}
372
373
nsresult
374
ResourceReader::OnWalkURI(const nsACString& aURISpec)
375
0
{
376
0
    nsresult rv;
377
0
    nsCOMPtr<nsIURI> uri;
378
0
379
0
    rv = NS_NewURI(getter_AddRefs(uri),
380
0
                   aURISpec,
381
0
                   mParent->GetCharacterSet(),
382
0
                   mCurrentBaseURI);
383
0
    NS_ENSURE_SUCCESS(rv, rv);
384
0
    return OnWalkURI(uri);
385
0
}
386
387
static void
388
ExtractAttribute(Element* aElement,
389
                 const char* aAttribute,
390
                 const char* aNamespaceURI,
391
                 nsCString&  aValue)
392
0
{
393
0
    // Find the named URI attribute on the (element) node and store
394
0
    // a reference to the URI that maps onto a local file name
395
0
396
0
    RefPtr<nsDOMAttributeMap> attrMap = aElement->Attributes();
397
0
398
0
    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
399
0
    NS_ConvertASCIItoUTF16 attribute(aAttribute);
400
0
    RefPtr<dom::Attr> attr = attrMap->GetNamedItemNS(namespaceURI, attribute);
401
0
    if (attr) {
402
0
        nsAutoString value;
403
0
        attr->GetValue(value);
404
0
        CopyUTF16toUTF8(value, aValue);
405
0
    } else {
406
0
        aValue.Truncate();
407
0
    }
408
0
}
409
410
nsresult
411
ResourceReader::OnWalkAttribute(Element* aElement,
412
                                const char* aAttribute,
413
                                const char* aNamespaceURI)
414
0
{
415
0
    nsAutoCString uriSpec;
416
0
    ExtractAttribute(aElement, aAttribute, aNamespaceURI, uriSpec);
417
0
    if (uriSpec.IsEmpty()) {
418
0
        return NS_OK;
419
0
    }
420
0
    return OnWalkURI(uriSpec);
421
0
}
422
423
static nsresult
424
GetXMLStyleSheetLink(dom::ProcessingInstruction *aPI, nsAString &aHref)
425
0
{
426
0
    nsAutoString data;
427
0
    aPI->GetData(data);
428
0
429
0
    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
430
0
    return NS_OK;
431
0
}
432
433
nsresult
434
ResourceReader::OnWalkDOMNode(nsINode* aNode)
435
0
{
436
0
    // Fixup xml-stylesheet processing instructions
437
0
    if (auto nodeAsPI = dom::ProcessingInstruction::FromNode(aNode)) {
438
0
        nsAutoString target;
439
0
        nodeAsPI->GetTarget(target);
440
0
        if (target.EqualsLiteral("xml-stylesheet")) {
441
0
            nsAutoString href;
442
0
            GetXMLStyleSheetLink(nodeAsPI, href);
443
0
            if (!href.IsEmpty()) {
444
0
                return OnWalkURI(NS_ConvertUTF16toUTF8(href));
445
0
            }
446
0
        }
447
0
        return NS_OK;
448
0
    }
449
0
450
0
    // Test the node to see if it's an image, frame, iframe, css, js
451
0
    if (aNode->IsHTMLElement(nsGkAtoms::img)) {
452
0
        return OnWalkAttribute(aNode->AsElement(), "src");
453
0
    }
454
0
455
0
    if (aNode->IsSVGElement(nsGkAtoms::img)) {
456
0
        return OnWalkAttribute(aNode->AsElement(), "href",
457
0
                               "http://www.w3.org/1999/xlink");
458
0
    }
459
0
460
0
    if (aNode->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
461
0
        return OnWalkAttribute(aNode->AsElement(), "src");
462
0
    }
463
0
464
0
    if (aNode->IsHTMLElement(nsGkAtoms::source)) {
465
0
        return OnWalkAttribute(aNode->AsElement(), "src");
466
0
    }
467
0
468
0
    if (aNode->IsHTMLElement(nsGkAtoms::body)) {
469
0
        return OnWalkAttribute(aNode->AsElement(), "background");
470
0
    }
471
0
472
0
    if (aNode->IsHTMLElement(nsGkAtoms::table)) {
473
0
        return OnWalkAttribute(aNode->AsElement(), "background");
474
0
    }
475
0
476
0
    if (aNode->IsHTMLElement(nsGkAtoms::tr)) {
477
0
        return OnWalkAttribute(aNode->AsElement(), "background");
478
0
    }
479
0
480
0
    if (aNode->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
481
0
        return OnWalkAttribute(aNode->AsElement(), "background");
482
0
    }
483
0
484
0
    if (aNode->IsHTMLElement(nsGkAtoms::script)) {
485
0
        return OnWalkAttribute(aNode->AsElement(), "src");
486
0
    }
487
0
488
0
    if (aNode->IsSVGElement(nsGkAtoms::script)) {
489
0
        return OnWalkAttribute(aNode->AsElement(), "href",
490
0
                               "http://www.w3.org/1999/xlink");
491
0
    }
492
0
493
0
    if (aNode->IsHTMLElement(nsGkAtoms::embed)) {
494
0
        return OnWalkAttribute(aNode->AsElement(), "src");
495
0
    }
496
0
497
0
    if (aNode->IsHTMLElement(nsGkAtoms::object)) {
498
0
        return OnWalkAttribute(aNode->AsElement(), "data");
499
0
    }
500
0
501
0
    if (auto nodeAsLink = dom::HTMLLinkElement::FromNode(aNode)) {
502
0
        // Test if the link has a rel value indicating it to be a stylesheet
503
0
        nsAutoString linkRel;
504
0
        nodeAsLink->GetRel(linkRel);
505
0
        if (!linkRel.IsEmpty()) {
506
0
            nsReadingIterator<char16_t> start;
507
0
            nsReadingIterator<char16_t> end;
508
0
            nsReadingIterator<char16_t> current;
509
0
510
0
            linkRel.BeginReading(start);
511
0
            linkRel.EndReading(end);
512
0
513
0
            // Walk through space delimited string looking for "stylesheet"
514
0
            for (current = start; current != end; ++current) {
515
0
                // Ignore whitespace
516
0
                if (nsCRT::IsAsciiSpace(*current)) {
517
0
                    continue;
518
0
                }
519
0
520
0
                // Grab the next space delimited word
521
0
                nsReadingIterator<char16_t> startWord = current;
522
0
                do {
523
0
                    ++current;
524
0
                } while (current != end && !nsCRT::IsAsciiSpace(*current));
525
0
526
0
                // Store the link for fix up if it says "stylesheet"
527
0
                if (Substring(startWord, current)
528
0
                        .LowerCaseEqualsLiteral("stylesheet")) {
529
0
                    OnWalkAttribute(aNode->AsElement(), "href");
530
0
                    return NS_OK;
531
0
                }
532
0
                if (current == end) {
533
0
                    break;
534
0
                }
535
0
            }
536
0
        }
537
0
        return NS_OK;
538
0
    }
539
0
540
0
    if (aNode->IsHTMLElement(nsGkAtoms::frame)) {
541
0
        return OnWalkSubframe(aNode);
542
0
    }
543
0
544
0
    if (aNode->IsHTMLElement(nsGkAtoms::iframe) &&
545
0
        !(mPersistFlags & IWBP::PERSIST_FLAGS_IGNORE_IFRAMES)) {
546
0
        return OnWalkSubframe(aNode);
547
0
    }
548
0
549
0
    auto nodeAsInput = dom::HTMLInputElement::FromNode(aNode);
550
0
    if (nodeAsInput) {
551
0
        return OnWalkAttribute(aNode->AsElement(), "src");
552
0
    }
553
0
554
0
    return NS_OK;
555
0
}
556
557
// Helper class for node rewriting in writeContent().
558
class PersistNodeFixup final : public nsIDocumentEncoderNodeFixup {
559
public:
560
    PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
561
                     nsIWebBrowserPersistURIMap* aMap,
562
                     nsIURI* aTargetURI);
563
564
    NS_DECL_ISUPPORTS
565
    NS_DECL_NSIDOCUMENTENCODERNODEFIXUP
566
private:
567
0
    virtual ~PersistNodeFixup() = default;
568
    RefPtr<WebBrowserPersistLocalDocument> mParent;
569
    nsClassHashtable<nsCStringHashKey, nsCString> mMap;
570
    nsCOMPtr<nsIURI> mCurrentBaseURI;
571
    nsCOMPtr<nsIURI> mTargetBaseURI;
572
573
0
    bool IsFlagSet(uint32_t aFlag) const {
574
0
        return mParent->GetPersistFlags() & aFlag;
575
0
    }
576
577
    nsresult GetNodeToFixup(nsINode* aNodeIn, nsINode** aNodeOut);
578
    nsresult FixupURI(nsAString& aURI);
579
    nsresult FixupAttribute(nsINode* aNode,
580
                            const char* aAttribute,
581
                            const char* aNamespaceURI = "");
582
    nsresult FixupAnchor(nsINode* aNode);
583
    nsresult FixupXMLStyleSheetLink(dom::ProcessingInstruction* aPI,
584
                                    const nsAString& aHref);
585
586
    using IWBP = nsIWebBrowserPersist;
587
};
588
589
NS_IMPL_ISUPPORTS(PersistNodeFixup, nsIDocumentEncoderNodeFixup)
590
591
PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
592
                                   nsIWebBrowserPersistURIMap* aMap,
593
                                   nsIURI* aTargetURI)
594
: mParent(aParent)
595
, mCurrentBaseURI(aParent->GetBaseURI())
596
, mTargetBaseURI(aTargetURI)
597
0
{
598
0
    if (aMap) {
599
0
        uint32_t mapSize;
600
0
        nsresult rv = aMap->GetNumMappedURIs(&mapSize);
601
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
602
0
        NS_ENSURE_SUCCESS_VOID(rv);
603
0
        for (uint32_t i = 0; i < mapSize; ++i) {
604
0
            nsAutoCString urlFrom;
605
0
            auto* urlTo = new nsCString();
606
0
607
0
            rv = aMap->GetURIMapping(i, urlFrom, *urlTo);
608
0
            MOZ_ASSERT(NS_SUCCEEDED(rv));
609
0
            if (NS_SUCCEEDED(rv)) {
610
0
                mMap.Put(urlFrom, urlTo);
611
0
            }
612
0
        }
613
0
    }
614
0
}
615
616
nsresult
617
PersistNodeFixup::GetNodeToFixup(nsINode *aNodeIn, nsINode **aNodeOut)
618
0
{
619
0
    // Avoid mixups in FixupNode that could leak objects; this goes
620
0
    // against the usual out parameter convention, but it's a private
621
0
    // method so shouldn't be a problem.
622
0
    MOZ_ASSERT(!*aNodeOut);
623
0
624
0
    if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM)) {
625
0
        ErrorResult rv;
626
0
        *aNodeOut = aNodeIn->CloneNode(false, rv).take();
627
0
        return rv.StealNSResult();
628
0
    }
629
0
630
0
    NS_ADDREF(*aNodeOut = aNodeIn);
631
0
    return NS_OK;
632
0
}
633
634
nsresult
635
PersistNodeFixup::FixupURI(nsAString &aURI)
636
0
{
637
0
    // get the current location of the file (absolutized)
638
0
    nsCOMPtr<nsIURI> uri;
639
0
    nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
640
0
                            mParent->GetCharacterSet(), mCurrentBaseURI);
641
0
    NS_ENSURE_SUCCESS(rv, rv);
642
0
    nsAutoCString spec;
643
0
    rv = uri->GetSpec(spec);
644
0
    NS_ENSURE_SUCCESS(rv, rv);
645
0
646
0
    const nsCString* replacement = mMap.Get(spec);
647
0
    if (!replacement) {
648
0
        // Note that most callers ignore this "failure".
649
0
        return NS_ERROR_FAILURE;
650
0
    }
651
0
    if (!replacement->IsEmpty()) {
652
0
        aURI = NS_ConvertUTF8toUTF16(*replacement);
653
0
    }
654
0
    return NS_OK;
655
0
}
656
657
nsresult
658
PersistNodeFixup::FixupAttribute(nsINode* aNode,
659
                                 const char* aAttribute,
660
                                 const char* aNamespaceURI)
661
0
{
662
0
    MOZ_ASSERT(aNode->IsElement());
663
0
    dom::Element* element = aNode->AsElement();
664
0
665
0
    RefPtr<nsDOMAttributeMap> attrMap = element->Attributes();
666
0
667
0
    NS_ConvertASCIItoUTF16 attribute(aAttribute);
668
0
    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
669
0
    RefPtr<dom::Attr> attr = attrMap->GetNamedItemNS(namespaceURI, attribute);
670
0
    nsresult rv = NS_OK;
671
0
    if (attr) {
672
0
        nsString uri;
673
0
        attr->GetValue(uri);
674
0
        rv = FixupURI(uri);
675
0
        if (NS_SUCCEEDED(rv)) {
676
0
            attr->SetValue(uri, IgnoreErrors());
677
0
        }
678
0
    }
679
0
680
0
    return rv;
681
0
}
682
683
nsresult
684
PersistNodeFixup::FixupAnchor(nsINode *aNode)
685
0
{
686
0
    if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS)) {
687
0
        return NS_OK;
688
0
    }
689
0
690
0
    MOZ_ASSERT(aNode->IsElement());
691
0
    dom::Element* element = aNode->AsElement();
692
0
693
0
    RefPtr<nsDOMAttributeMap> attrMap = element->Attributes();
694
0
695
0
    // Make all anchor links absolute so they point off onto the Internet
696
0
    nsString attribute(NS_LITERAL_STRING("href"));
697
0
    RefPtr<dom::Attr> attr = attrMap->GetNamedItem(attribute);
698
0
    if (attr) {
699
0
        nsString oldValue;
700
0
        attr->GetValue(oldValue);
701
0
        NS_ConvertUTF16toUTF8 oldCValue(oldValue);
702
0
703
0
        // Skip empty values and self-referencing bookmarks
704
0
        if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#') {
705
0
            return NS_OK;
706
0
        }
707
0
708
0
        // if saving file to same location, we don't need to do any fixup
709
0
        bool isEqual;
710
0
        if (mTargetBaseURI &&
711
0
            NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual)) &&
712
0
            isEqual) {
713
0
            return NS_OK;
714
0
        }
715
0
716
0
        nsCOMPtr<nsIURI> relativeURI;
717
0
        relativeURI = IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
718
0
                      ? mTargetBaseURI : mCurrentBaseURI;
719
0
        // Make a new URI to replace the current one
720
0
        nsCOMPtr<nsIURI> newURI;
721
0
        nsresult rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
722
0
                                mParent->GetCharacterSet(), relativeURI);
723
0
        if (NS_SUCCEEDED(rv) && newURI) {
724
0
            Unused << NS_MutateURI(newURI)
725
0
                        .SetUserPass(EmptyCString())
726
0
                        .Finalize(newURI);
727
0
            nsAutoCString uriSpec;
728
0
            rv = newURI->GetSpec(uriSpec);
729
0
            NS_ENSURE_SUCCESS(rv, rv);
730
0
            attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec), IgnoreErrors());
731
0
        }
732
0
    }
733
0
734
0
    return NS_OK;
735
0
}
736
737
static void
738
AppendXMLAttr(const nsAString& key, const nsAString& aValue, nsAString& aBuffer)
739
0
{
740
0
    if (!aBuffer.IsEmpty()) {
741
0
        aBuffer.Append(' ');
742
0
    }
743
0
    aBuffer.Append(key);
744
0
    aBuffer.AppendLiteral(R"(=")");
745
0
    for (size_t i = 0; i < aValue.Length(); ++i) {
746
0
        switch (aValue[i]) {
747
0
            case '&':
748
0
                aBuffer.AppendLiteral("&amp;");
749
0
                break;
750
0
            case '<':
751
0
                aBuffer.AppendLiteral("&lt;");
752
0
                break;
753
0
            case '>':
754
0
                aBuffer.AppendLiteral("&gt;");
755
0
                break;
756
0
            case '"':
757
0
                aBuffer.AppendLiteral("&quot;");
758
0
                break;
759
0
            default:
760
0
                aBuffer.Append(aValue[i]);
761
0
                break;
762
0
        }
763
0
    }
764
0
    aBuffer.Append('"');
765
0
}
766
767
nsresult
768
PersistNodeFixup::FixupXMLStyleSheetLink(dom::ProcessingInstruction* aPI,
769
                                         const nsAString& aHref)
770
0
{
771
0
    NS_ENSURE_ARG_POINTER(aPI);
772
0
773
0
    nsAutoString data;
774
0
    aPI->GetData(data);
775
0
776
0
    nsAutoString href;
777
0
    nsContentUtils::GetPseudoAttributeValue(data,
778
0
                                            nsGkAtoms::href,
779
0
                                            href);
780
0
781
0
    // Construct and set a new data value for the xml-stylesheet
782
0
    if (!aHref.IsEmpty() && !href.IsEmpty())
783
0
    {
784
0
        nsAutoString alternate;
785
0
        nsAutoString charset;
786
0
        nsAutoString title;
787
0
        nsAutoString type;
788
0
        nsAutoString media;
789
0
790
0
        nsContentUtils::GetPseudoAttributeValue(data,
791
0
                                                nsGkAtoms::alternate,
792
0
                                                alternate);
793
0
        nsContentUtils::GetPseudoAttributeValue(data,
794
0
                                                nsGkAtoms::charset,
795
0
                                                charset);
796
0
        nsContentUtils::GetPseudoAttributeValue(data,
797
0
                                                nsGkAtoms::title,
798
0
                                                title);
799
0
        nsContentUtils::GetPseudoAttributeValue(data,
800
0
                                                nsGkAtoms::type,
801
0
                                                type);
802
0
        nsContentUtils::GetPseudoAttributeValue(data,
803
0
                                                nsGkAtoms::media,
804
0
                                                media);
805
0
806
0
        nsAutoString newData;
807
0
        AppendXMLAttr(NS_LITERAL_STRING("href"), aHref, newData);
808
0
        if (!title.IsEmpty()) {
809
0
            AppendXMLAttr(NS_LITERAL_STRING("title"), title, newData);
810
0
        }
811
0
        if (!media.IsEmpty()) {
812
0
            AppendXMLAttr(NS_LITERAL_STRING("media"), media, newData);
813
0
        }
814
0
        if (!type.IsEmpty()) {
815
0
            AppendXMLAttr(NS_LITERAL_STRING("type"), type, newData);
816
0
        }
817
0
        if (!charset.IsEmpty()) {
818
0
            AppendXMLAttr(NS_LITERAL_STRING("charset"), charset, newData);
819
0
        }
820
0
        if (!alternate.IsEmpty()) {
821
0
            AppendXMLAttr(NS_LITERAL_STRING("alternate"), alternate, newData);
822
0
        }
823
0
        aPI->SetData(newData, IgnoreErrors());
824
0
    }
825
0
826
0
    return NS_OK;
827
0
}
828
829
NS_IMETHODIMP
830
PersistNodeFixup::FixupNode(nsINode* aNodeIn,
831
                            bool* aSerializeCloneKids,
832
                            nsINode** aNodeOut)
833
0
{
834
0
    *aNodeOut = nullptr;
835
0
    *aSerializeCloneKids = false;
836
0
837
0
    uint16_t type = aNodeIn->NodeType();
838
0
    if (type != nsINode::ELEMENT_NODE &&
839
0
        type != nsINode::PROCESSING_INSTRUCTION_NODE) {
840
0
        return NS_OK;
841
0
    }
842
0
843
0
    MOZ_ASSERT(aNodeIn->IsContent());
844
0
845
0
    // Fixup xml-stylesheet processing instructions
846
0
    if (auto nodeAsPI =
847
0
          dom::ProcessingInstruction::FromNode(aNodeIn)) {
848
0
        nsAutoString target;
849
0
        nodeAsPI->GetTarget(target);
850
0
        if (target.EqualsLiteral("xml-stylesheet"))
851
0
        {
852
0
            nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
853
0
            if (NS_SUCCEEDED(rv) && *aNodeOut) {
854
0
                MOZ_ASSERT((*aNodeOut)->IsProcessingInstruction());
855
0
                auto nodeAsPI =
856
0
                  static_cast<dom::ProcessingInstruction*>(*aNodeOut);
857
0
                nsAutoString href;
858
0
                GetXMLStyleSheetLink(nodeAsPI, href);
859
0
                if (!href.IsEmpty()) {
860
0
                    FixupURI(href);
861
0
                    FixupXMLStyleSheetLink(nodeAsPI, href);
862
0
                }
863
0
            }
864
0
        }
865
0
        return NS_OK;
866
0
    }
867
0
868
0
    nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
869
0
    if (!content) {
870
0
        return NS_OK;
871
0
    }
872
0
873
0
    // BASE elements are replaced by a comment so relative links are not hosed.
874
0
    if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS) &&
875
0
        content->IsHTMLElement(nsGkAtoms::base)) {
876
0
        // Base uses HTMLSharedElement, which would be awkward to implement
877
0
        // FromContent on, since it represents multiple elements. Since we've
878
0
        // already checked IsHTMLElement here, just cast as we were doing.
879
0
        auto* base = static_cast<dom::HTMLSharedElement*>(content.get());
880
0
        nsIDocument* ownerDoc = base->OwnerDoc();
881
0
882
0
        nsAutoString href;
883
0
        base->GetHref(href); // Doesn't matter if this fails
884
0
        nsAutoString commentText;
885
0
        commentText.AssignLiteral(" base ");
886
0
        if (!href.IsEmpty()) {
887
0
            commentText += NS_LITERAL_STRING("href=\"") + href
888
0
                + NS_LITERAL_STRING("\" ");
889
0
        }
890
0
        *aNodeOut = ownerDoc->CreateComment(commentText).take();
891
0
        return NS_OK;
892
0
    }
893
0
894
0
    // Fix up href and file links in the elements
895
0
    RefPtr<dom::HTMLAnchorElement> nodeAsAnchor = dom::HTMLAnchorElement::FromNode(content);
896
0
    if (nodeAsAnchor) {
897
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
898
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
899
0
            FixupAnchor(*aNodeOut);
900
0
        }
901
0
        return rv;
902
0
    }
903
0
904
0
    RefPtr<dom::HTMLAreaElement> nodeAsArea = dom::HTMLAreaElement::FromNode(content);
905
0
    if (nodeAsArea) {
906
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
907
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
908
0
            FixupAnchor(*aNodeOut);
909
0
        }
910
0
        return rv;
911
0
    }
912
0
913
0
    if (content->IsHTMLElement(nsGkAtoms::body)) {
914
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
915
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
916
0
            FixupAttribute(*aNodeOut, "background");
917
0
        }
918
0
        return rv;
919
0
    }
920
0
921
0
    if (content->IsHTMLElement(nsGkAtoms::table)) {
922
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
923
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
924
0
            FixupAttribute(*aNodeOut, "background");
925
0
        }
926
0
        return rv;
927
0
    }
928
0
929
0
    if (content->IsHTMLElement(nsGkAtoms::tr)) {
930
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
931
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
932
0
            FixupAttribute(*aNodeOut, "background");
933
0
        }
934
0
        return rv;
935
0
    }
936
0
937
0
    if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
938
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
939
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
940
0
            FixupAttribute(*aNodeOut, "background");
941
0
        }
942
0
        return rv;
943
0
    }
944
0
945
0
    if (content->IsHTMLElement(nsGkAtoms::img)) {
946
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
947
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
948
0
            // Disable image loads
949
0
            nsCOMPtr<nsIImageLoadingContent> imgCon =
950
0
                do_QueryInterface(*aNodeOut);
951
0
            if (imgCon) {
952
0
                imgCon->SetLoadingEnabled(false);
953
0
            }
954
0
            FixupAnchor(*aNodeOut);
955
0
            FixupAttribute(*aNodeOut, "src");
956
0
        }
957
0
        return rv;
958
0
    }
959
0
960
0
    if (content->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
961
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
962
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
963
0
            FixupAttribute(*aNodeOut, "src");
964
0
        }
965
0
        return rv;
966
0
    }
967
0
968
0
    if (content->IsHTMLElement(nsGkAtoms::source)) {
969
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
970
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
971
0
            FixupAttribute(*aNodeOut, "src");
972
0
        }
973
0
        return rv;
974
0
    }
975
0
976
0
    if (content->IsSVGElement(nsGkAtoms::img)) {
977
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
978
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
979
0
            // Disable image loads
980
0
            nsCOMPtr<nsIImageLoadingContent> imgCon =
981
0
                do_QueryInterface(*aNodeOut);
982
0
            if (imgCon)
983
0
                imgCon->SetLoadingEnabled(false);
984
0
985
0
            // FixupAnchor(*aNodeOut);  // XXXjwatt: is this line needed?
986
0
            FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink");
987
0
        }
988
0
        return rv;
989
0
    }
990
0
991
0
    if (content->IsHTMLElement(nsGkAtoms::script)) {
992
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
993
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
994
0
            FixupAttribute(*aNodeOut, "src");
995
0
        }
996
0
        return rv;
997
0
    }
998
0
999
0
    if (content->IsSVGElement(nsGkAtoms::script)) {
1000
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1001
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1002
0
            FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink");
1003
0
        }
1004
0
        return rv;
1005
0
    }
1006
0
1007
0
    if (content->IsHTMLElement(nsGkAtoms::embed)) {
1008
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1009
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1010
0
            FixupAttribute(*aNodeOut, "src");
1011
0
        }
1012
0
        return rv;
1013
0
    }
1014
0
1015
0
    if (content->IsHTMLElement(nsGkAtoms::object)) {
1016
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1017
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1018
0
            FixupAttribute(*aNodeOut, "data");
1019
0
        }
1020
0
        return rv;
1021
0
    }
1022
0
1023
0
    if (content->IsHTMLElement(nsGkAtoms::link)) {
1024
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1025
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1026
0
            // First see if the link represents linked content
1027
0
            rv = FixupAttribute(*aNodeOut, "href");
1028
0
            if (NS_FAILED(rv)) {
1029
0
                // Perhaps this link is actually an anchor to related content
1030
0
                FixupAnchor(*aNodeOut);
1031
0
            }
1032
0
            // TODO if "type" attribute == "text/css"
1033
0
            //        fixup stylesheet
1034
0
        }
1035
0
        return rv;
1036
0
    }
1037
0
1038
0
    if (content->IsHTMLElement(nsGkAtoms::frame)) {
1039
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1040
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1041
0
            FixupAttribute(*aNodeOut, "src");
1042
0
        }
1043
0
        return rv;
1044
0
    }
1045
0
1046
0
    if (content->IsHTMLElement(nsGkAtoms::iframe)) {
1047
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1048
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1049
0
            FixupAttribute(*aNodeOut, "src");
1050
0
        }
1051
0
        return rv;
1052
0
    }
1053
0
1054
0
    RefPtr<dom::HTMLInputElement> nodeAsInput =
1055
0
        dom::HTMLInputElement::FromNodeOrNull(content);
1056
0
    if (nodeAsInput) {
1057
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1058
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1059
0
            // Disable image loads
1060
0
            nsCOMPtr<nsIImageLoadingContent> imgCon =
1061
0
                do_QueryInterface(*aNodeOut);
1062
0
            if (imgCon) {
1063
0
                imgCon->SetLoadingEnabled(false);
1064
0
            }
1065
0
1066
0
            FixupAttribute(*aNodeOut, "src");
1067
0
1068
0
            nsAutoString valueStr;
1069
0
            NS_NAMED_LITERAL_STRING(valueAttr, "value");
1070
0
            // Update element node attributes with user-entered form state
1071
0
            RefPtr<dom::HTMLInputElement> outElt =
1072
0
                dom::HTMLInputElement::FromNode((*aNodeOut)->AsContent());
1073
0
            nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
1074
0
            switch (formControl->ControlType()) {
1075
0
                case NS_FORM_INPUT_EMAIL:
1076
0
                case NS_FORM_INPUT_SEARCH:
1077
0
                case NS_FORM_INPUT_TEXT:
1078
0
                case NS_FORM_INPUT_TEL:
1079
0
                case NS_FORM_INPUT_URL:
1080
0
                case NS_FORM_INPUT_NUMBER:
1081
0
                case NS_FORM_INPUT_RANGE:
1082
0
                case NS_FORM_INPUT_DATE:
1083
0
                case NS_FORM_INPUT_TIME:
1084
0
                case NS_FORM_INPUT_COLOR:
1085
0
                    nodeAsInput->GetValue(valueStr, dom::CallerType::System);
1086
0
                    // Avoid superfluous value="" serialization
1087
0
                    if (valueStr.IsEmpty()) {
1088
0
                      outElt->RemoveAttribute(valueAttr, IgnoreErrors());
1089
0
                    } else {
1090
0
                      outElt->SetAttribute(valueAttr, valueStr, IgnoreErrors());
1091
0
                    }
1092
0
                    break;
1093
0
                case NS_FORM_INPUT_CHECKBOX:
1094
0
                case NS_FORM_INPUT_RADIO:
1095
0
                    {
1096
0
                        bool checked = nodeAsInput->Checked();
1097
0
                        outElt->SetDefaultChecked(checked, IgnoreErrors());
1098
0
                    }
1099
0
                    break;
1100
0
                default:
1101
0
                    break;
1102
0
            }
1103
0
        }
1104
0
        return rv;
1105
0
    }
1106
0
1107
0
    dom::HTMLTextAreaElement* nodeAsTextArea = dom::HTMLTextAreaElement::FromNode(content);
1108
0
    if (nodeAsTextArea) {
1109
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1110
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1111
0
            // Tell the document encoder to serialize the text child we create below
1112
0
            *aSerializeCloneKids = true;
1113
0
1114
0
            nsAutoString valueStr;
1115
0
            nodeAsTextArea->GetValue(valueStr);
1116
0
1117
0
            (*aNodeOut)->SetTextContent(valueStr, IgnoreErrors());
1118
0
        }
1119
0
        return rv;
1120
0
    }
1121
0
1122
0
    dom::HTMLOptionElement* nodeAsOption = dom::HTMLOptionElement::FromNode(content);
1123
0
    if (nodeAsOption) {
1124
0
        nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1125
0
        if (NS_SUCCEEDED(rv) && *aNodeOut) {
1126
0
            dom::HTMLOptionElement* outElt =
1127
0
                dom::HTMLOptionElement::FromNode((*aNodeOut)->AsContent());
1128
0
            bool selected = nodeAsOption->Selected();
1129
0
            outElt->SetDefaultSelected(selected, IgnoreErrors());
1130
0
        }
1131
0
        return rv;
1132
0
    }
1133
0
1134
0
    return NS_OK;
1135
0
}
1136
1137
} // unnamed namespace
1138
1139
NS_IMETHODIMP
1140
WebBrowserPersistLocalDocument::ReadResources(nsIWebBrowserPersistResourceVisitor* aVisitor)
1141
0
{
1142
0
    nsresult rv = NS_OK;
1143
0
    nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visitor = aVisitor;
1144
0
1145
0
    NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
1146
0
1147
0
    ErrorResult err;
1148
0
    RefPtr<dom::TreeWalker> walker =
1149
0
        mDocument->CreateTreeWalker(*mDocument,
1150
0
            dom::NodeFilter_Binding::SHOW_ELEMENT |
1151
0
            dom::NodeFilter_Binding::SHOW_DOCUMENT |
1152
0
            dom::NodeFilter_Binding::SHOW_PROCESSING_INSTRUCTION,
1153
0
            nullptr, err);
1154
0
1155
0
    if (NS_WARN_IF(err.Failed())) {
1156
0
        return err.StealNSResult();
1157
0
    }
1158
0
    MOZ_ASSERT(walker);
1159
0
1160
0
    RefPtr<ResourceReader> reader = new ResourceReader(this, aVisitor);
1161
0
    nsCOMPtr<nsINode> currentNode = walker->CurrentNode();
1162
0
    do {
1163
0
        rv = reader->OnWalkDOMNode(currentNode);
1164
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
1165
0
            break;
1166
0
        }
1167
0
1168
0
        ErrorResult err;
1169
0
        currentNode = walker->NextNode(err);
1170
0
        if (NS_WARN_IF(err.Failed())) {
1171
0
            err.SuppressException();
1172
0
            break;
1173
0
        }
1174
0
    } while (currentNode);
1175
0
    reader->DocumentDone(rv);
1176
0
    // If NS_FAILED(rv), it was / will be reported by an EndVisit call
1177
0
    // via DocumentDone.  This method must return a failure if and
1178
0
    // only if visitor won't be invoked.
1179
0
    return NS_OK;
1180
0
}
1181
1182
static uint32_t
1183
ConvertEncoderFlags(uint32_t aEncoderFlags)
1184
0
{
1185
0
    uint32_t encoderFlags = 0;
1186
0
1187
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_SELECTION_ONLY)
1188
0
        encoderFlags |= nsIDocumentEncoder::OutputSelectionOnly;
1189
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED)
1190
0
        encoderFlags |= nsIDocumentEncoder::OutputFormatted;
1191
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_RAW)
1192
0
        encoderFlags |= nsIDocumentEncoder::OutputRaw;
1193
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_BODY_ONLY)
1194
0
        encoderFlags |= nsIDocumentEncoder::OutputBodyOnly;
1195
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_PREFORMATTED)
1196
0
        encoderFlags |= nsIDocumentEncoder::OutputPreformatted;
1197
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_WRAP)
1198
0
        encoderFlags |= nsIDocumentEncoder::OutputWrap;
1199
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMAT_FLOWED)
1200
0
        encoderFlags |= nsIDocumentEncoder::OutputFormatFlowed;
1201
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS)
1202
0
        encoderFlags |= nsIDocumentEncoder::OutputAbsoluteLinks;
1203
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_BASIC_ENTITIES)
1204
0
        encoderFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities;
1205
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_CR_LINEBREAKS)
1206
0
        encoderFlags |= nsIDocumentEncoder::OutputCRLineBreak;
1207
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_LF_LINEBREAKS)
1208
0
        encoderFlags |= nsIDocumentEncoder::OutputLFLineBreak;
1209
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOSCRIPT_CONTENT)
1210
0
        encoderFlags |= nsIDocumentEncoder::OutputNoScriptContent;
1211
0
    if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT)
1212
0
        encoderFlags |= nsIDocumentEncoder::OutputNoFramesContent;
1213
0
1214
0
    return encoderFlags;
1215
0
}
1216
1217
static bool
1218
ContentTypeEncoderExists(const nsACString& aType)
1219
0
{
1220
0
    nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
1221
0
    contractID.Append(aType);
1222
0
1223
0
    nsCOMPtr<nsIComponentRegistrar> registrar;
1224
0
    nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
1225
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
1226
0
    if (NS_SUCCEEDED(rv) && registrar) {
1227
0
        bool result;
1228
0
        rv = registrar->IsContractIDRegistered(contractID.get(), &result);
1229
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
1230
0
        return NS_SUCCEEDED(rv) && result;
1231
0
    }
1232
0
    return false;
1233
0
}
1234
1235
void
1236
WebBrowserPersistLocalDocument::DecideContentType(nsACString& aContentType)
1237
0
{
1238
0
    if (aContentType.IsEmpty()) {
1239
0
        if (NS_WARN_IF(NS_FAILED(GetContentType(aContentType)))) {
1240
0
            aContentType.Truncate();
1241
0
        }
1242
0
    }
1243
0
    if (!aContentType.IsEmpty() &&
1244
0
        !ContentTypeEncoderExists(aContentType)) {
1245
0
        aContentType.Truncate();
1246
0
    }
1247
0
    if (aContentType.IsEmpty()) {
1248
0
        aContentType.AssignLiteral("text/html");
1249
0
    }
1250
0
}
1251
1252
nsresult
1253
WebBrowserPersistLocalDocument::GetDocEncoder(const nsACString& aContentType,
1254
                                              uint32_t aEncoderFlags,
1255
                                              nsIDocumentEncoder** aEncoder)
1256
0
{
1257
0
    nsresult rv;
1258
0
    nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
1259
0
    contractID.Append(aContentType);
1260
0
    nsCOMPtr<nsIDocumentEncoder> encoder =
1261
0
        do_CreateInstance(contractID.get(), &rv);
1262
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1263
0
1264
0
    rv = encoder->NativeInit(mDocument,
1265
0
                             NS_ConvertASCIItoUTF16(aContentType),
1266
0
                             ConvertEncoderFlags(aEncoderFlags));
1267
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1268
0
1269
0
    nsAutoCString charSet;
1270
0
    rv = GetCharacterSet(charSet);
1271
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1272
0
    rv = encoder->SetCharset(charSet);
1273
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1274
0
1275
0
    encoder.forget(aEncoder);
1276
0
    return NS_OK;
1277
0
}
1278
1279
1280
NS_IMETHODIMP
1281
WebBrowserPersistLocalDocument::WriteContent(
1282
    nsIOutputStream* aStream,
1283
    nsIWebBrowserPersistURIMap* aMap,
1284
    const nsACString& aRequestedContentType,
1285
    uint32_t aEncoderFlags,
1286
    uint32_t aWrapColumn,
1287
    nsIWebBrowserPersistWriteCompletion* aCompletion)
1288
0
{
1289
0
    NS_ENSURE_ARG_POINTER(aStream);
1290
0
    NS_ENSURE_ARG_POINTER(aCompletion);
1291
0
    nsAutoCString contentType(aRequestedContentType);
1292
0
    DecideContentType(contentType);
1293
0
1294
0
    nsCOMPtr<nsIDocumentEncoder> encoder;
1295
0
    nsresult rv = GetDocEncoder(contentType, aEncoderFlags,
1296
0
                                getter_AddRefs(encoder));
1297
0
    NS_ENSURE_SUCCESS(rv, rv);
1298
0
1299
0
    if (aWrapColumn != 0 && (aEncoderFlags
1300
0
                             & nsIWebBrowserPersist::ENCODE_FLAGS_WRAP)) {
1301
0
        encoder->SetWrapColumn(aWrapColumn);
1302
0
    }
1303
0
1304
0
    nsCOMPtr<nsIURI> targetURI;
1305
0
    if (aMap) {
1306
0
        nsAutoCString targetURISpec;
1307
0
        rv = aMap->GetTargetBaseURI(targetURISpec);
1308
0
        if (NS_SUCCEEDED(rv) && !targetURISpec.IsEmpty()) {
1309
0
            rv = NS_NewURI(getter_AddRefs(targetURI), targetURISpec,
1310
0
                           /* charset: */ nullptr, /* base: */ nullptr);
1311
0
            NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1312
0
        } else if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION) {
1313
0
            return NS_ERROR_UNEXPECTED;
1314
0
        }
1315
0
    }
1316
0
    rv = encoder->SetNodeFixup(new PersistNodeFixup(this, aMap, targetURI));
1317
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1318
0
1319
0
    rv = encoder->EncodeToStream(aStream);
1320
0
    aCompletion->OnFinish(this, aStream, contentType, rv);
1321
0
    return NS_OK;
1322
0
}
1323
1324
} // namespace mozilla