1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsDOMSerializer.h"
8 
9 #include "mozilla/Encoding.h"
10 #include "mozilla/dom/Document.h"
11 #include "nsIDocumentEncoder.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsContentCID.h"
14 #include "nsContentUtils.h"
15 #include "nsError.h"
16 #include "nsINode.h"
17 
18 using namespace mozilla;
19 
20 nsDOMSerializer::nsDOMSerializer() = default;
21 
SetUpEncoder(nsINode & aRoot,const nsAString & aCharset,ErrorResult & aRv)22 static already_AddRefed<nsIDocumentEncoder> SetUpEncoder(
23     nsINode& aRoot, const nsAString& aCharset, ErrorResult& aRv) {
24   nsCOMPtr<nsIDocumentEncoder> encoder =
25       do_createDocumentEncoder("application/xhtml+xml");
26   if (!encoder) {
27     aRv.Throw(NS_ERROR_FAILURE);
28     return nullptr;
29   }
30 
31   dom::Document* doc = aRoot.OwnerDoc();
32   bool entireDocument = (doc == &aRoot);
33 
34   // This method will fail if no document
35   nsresult rv = encoder->NativeInit(
36       doc, u"application/xhtml+xml"_ns,
37       nsIDocumentEncoder::OutputRaw |
38           nsIDocumentEncoder::OutputDontRewriteEncodingDeclaration);
39 
40   if (NS_FAILED(rv)) {
41     aRv.Throw(rv);
42     return nullptr;
43   }
44 
45   NS_ConvertUTF16toUTF8 charset(aCharset);
46   if (charset.IsEmpty()) {
47     doc->GetDocumentCharacterSet()->Name(charset);
48   }
49   rv = encoder->SetCharset(charset);
50   if (NS_FAILED(rv)) {
51     aRv.Throw(rv);
52     return nullptr;
53   }
54 
55   // If we are working on the entire document we do not need to
56   // specify which part to serialize
57   if (!entireDocument) {
58     rv = encoder->SetNode(&aRoot);
59   }
60 
61   if (NS_FAILED(rv)) {
62     aRv.Throw(rv);
63     return nullptr;
64   }
65 
66   return encoder.forget();
67 }
68 
SerializeToString(nsINode & aRoot,nsAString & aStr,ErrorResult & aRv)69 void nsDOMSerializer::SerializeToString(nsINode& aRoot, nsAString& aStr,
70                                         ErrorResult& aRv) {
71   aStr.Truncate();
72 
73   if (!nsContentUtils::CanCallerAccess(&aRoot)) {
74     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
75     return;
76   }
77 
78   nsCOMPtr<nsIDocumentEncoder> encoder = SetUpEncoder(aRoot, u""_ns, aRv);
79   if (aRv.Failed()) {
80     return;
81   }
82 
83   nsresult rv = encoder->EncodeToString(aStr);
84   if (NS_FAILED(rv)) {
85     aRv.Throw(rv);
86   }
87 }
88 
SerializeToStream(nsINode & aRoot,nsIOutputStream * aStream,const nsAString & aCharset,ErrorResult & aRv)89 void nsDOMSerializer::SerializeToStream(nsINode& aRoot,
90                                         nsIOutputStream* aStream,
91                                         const nsAString& aCharset,
92                                         ErrorResult& aRv) {
93   if (NS_WARN_IF(!aStream)) {
94     aRv.Throw(NS_ERROR_INVALID_ARG);
95     return;
96   }
97 
98   // The charset arg can be empty, in which case we get the document's
99   // charset and use that when serializing.
100 
101   // No point doing a CanCallerAccess check, because we can only be
102   // called by system JS or C++.
103   nsCOMPtr<nsIDocumentEncoder> encoder = SetUpEncoder(aRoot, aCharset, aRv);
104   if (aRv.Failed()) {
105     return;
106   }
107 
108   nsresult rv = encoder->EncodeToStream(aStream);
109   if (NS_FAILED(rv)) {
110     aRv.Throw(rv);
111   }
112 }
113