Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/DOMParser.cpp
Line
Count
Source (jump to first uncovered line)
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 "mozilla/dom/DOMParser.h"
8
9
#include "nsNetUtil.h"
10
#include "nsDOMString.h"
11
#include "MainThreadUtils.h"
12
#include "nsIStreamListener.h"
13
#include "nsStringStream.h"
14
#include "nsIScriptError.h"
15
#include "nsIScriptSecurityManager.h"
16
#include "nsCRT.h"
17
#include "nsStreamUtils.h"
18
#include "nsContentUtils.h"
19
#include "nsDOMJSUtils.h"
20
#include "nsError.h"
21
#include "nsPIDOMWindow.h"
22
#include "mozilla/LoadInfo.h"
23
#include "mozilla/NullPrincipal.h"
24
#include "mozilla/dom/BindingUtils.h"
25
#include "mozilla/dom/ScriptSettings.h"
26
27
using namespace mozilla;
28
using namespace mozilla::dom;
29
30
DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
31
                     nsIURI* aDocumentURI, nsIURI* aBaseURI)
32
  : mOwner(aOwner)
33
  , mPrincipal(aDocPrincipal)
34
  , mDocumentURI(aDocumentURI)
35
  , mBaseURI(aBaseURI)
36
  , mForceEnableXULXBL(false)
37
0
{
38
0
  MOZ_ASSERT(aDocPrincipal);
39
0
  MOZ_ASSERT(aDocumentURI);
40
0
}
41
42
DOMParser::~DOMParser()
43
0
{
44
0
}
45
46
// QueryInterface implementation for DOMParser
47
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
48
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
49
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
50
0
NS_INTERFACE_MAP_END
51
52
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner)
53
54
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
55
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
56
57
static const char*
58
StringFromSupportedType(SupportedType aType)
59
0
{
60
0
  return SupportedTypeValues::strings[static_cast<int>(aType)].value;
61
0
}
62
63
already_AddRefed<nsIDocument>
64
DOMParser::ParseFromString(const nsAString& aStr, SupportedType aType,
65
                           ErrorResult& aRv)
66
0
{
67
0
  if (aType == SupportedType::Text_html) {
68
0
    nsCOMPtr<nsIDocument> document = SetUpDocument(DocumentFlavorHTML, aRv);
69
0
    if (NS_WARN_IF(aRv.Failed())) {
70
0
      return nullptr;
71
0
    }
72
0
73
0
    // Keep the XULXBL state in sync with the XML case.
74
0
    if (mForceEnableXULXBL) {
75
0
      document->ForceEnableXULXBL();
76
0
    }
77
0
78
0
    nsresult rv = nsContentUtils::ParseDocumentHTML(aStr, document, false);
79
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
80
0
      aRv.Throw(rv);
81
0
      return nullptr;
82
0
    }
83
0
84
0
    return document.forget();
85
0
  }
86
0
87
0
  nsAutoCString utf8str;
88
0
  // Convert from UTF16 to UTF8 using fallible allocations
89
0
  if (!AppendUTF16toUTF8(aStr, utf8str, mozilla::fallible)) {
90
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
91
0
    return nullptr;
92
0
  }
93
0
94
0
  // The new stream holds a reference to the buffer
95
0
  nsCOMPtr<nsIInputStream> stream;
96
0
  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
97
0
                                      utf8str.get(), utf8str.Length(),
98
0
                                      NS_ASSIGNMENT_DEPEND);
99
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
100
0
    aRv.Throw(rv);
101
0
    return nullptr;
102
0
  }
103
0
104
0
  return ParseFromStream(stream,  NS_LITERAL_STRING("UTF-8"),
105
0
                         utf8str.Length(), aType, aRv);
106
0
}
107
108
already_AddRefed<nsIDocument>
109
DOMParser::ParseFromBuffer(const Uint8Array& aBuf, SupportedType aType,
110
                           ErrorResult& aRv)
111
0
{
112
0
  aBuf.ComputeLengthAndData();
113
0
  return ParseFromBuffer(MakeSpan(aBuf.Data(), aBuf.Length()), aType, aRv);
114
0
}
115
116
already_AddRefed<nsIDocument>
117
DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf, SupportedType aType,
118
                           ErrorResult& aRv)
119
0
{
120
0
  // The new stream holds a reference to the buffer
121
0
  nsCOMPtr<nsIInputStream> stream;
122
0
  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
123
0
                                      reinterpret_cast<const char *>(aBuf.Elements()),
124
0
                                      aBuf.Length(), NS_ASSIGNMENT_DEPEND);
125
0
  if (NS_FAILED(rv)) {
126
0
    aRv.Throw(rv);
127
0
    return nullptr;
128
0
  }
129
0
130
0
  return ParseFromStream(stream, VoidString(), aBuf.Length(), aType, aRv);
131
0
}
132
133
134
already_AddRefed<nsIDocument>
135
DOMParser::ParseFromStream(nsIInputStream* aStream,
136
                           const nsAString& aCharset,
137
                           int32_t aContentLength,
138
                           SupportedType aType,
139
                           ErrorResult& aRv)
140
0
{
141
0
  bool svg = (aType == SupportedType::Image_svg_xml);
142
0
143
0
  // For now, we can only create XML documents.
144
0
  //XXXsmaug Should we create an HTMLDocument (in XHTML mode)
145
0
  //         for "application/xhtml+xml"?
146
0
  if (aType != SupportedType::Text_xml &&
147
0
      aType != SupportedType::Application_xml &&
148
0
      aType != SupportedType::Application_xhtml_xml &&
149
0
      !svg) {
150
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
151
0
    return nullptr;
152
0
  }
153
0
154
0
  // Put the nsCOMPtr out here so we hold a ref to the stream as needed
155
0
  nsCOMPtr<nsIInputStream> stream = aStream;
156
0
  if (!NS_InputStreamIsBuffered(stream)) {
157
0
    nsCOMPtr<nsIInputStream> bufferedStream;
158
0
    nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
159
0
                                   stream.forget(), 4096);
160
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
161
0
      aRv.Throw(rv);
162
0
      return nullptr;
163
0
    }
164
0
165
0
    stream = bufferedStream;
166
0
  }
167
0
168
0
  nsCOMPtr<nsIDocument> document =
169
0
    SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess, aRv);
170
0
  if (NS_WARN_IF(aRv.Failed())) {
171
0
    return nullptr;
172
0
  }
173
0
174
0
  // Create a fake channel
175
0
  nsCOMPtr<nsIChannel> parserChannel;
176
0
  NS_NewInputStreamChannel(getter_AddRefs(parserChannel),
177
0
                           mDocumentURI,
178
0
                           nullptr, // aStream
179
0
                           mPrincipal,
180
0
                           nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
181
0
                           nsIContentPolicy::TYPE_OTHER,
182
0
                           nsDependentCString(StringFromSupportedType(aType)));
183
0
  if (NS_WARN_IF(!parserChannel)) {
184
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
185
0
    return nullptr;
186
0
  }
187
0
188
0
  if (!DOMStringIsNull(aCharset)) {
189
0
    parserChannel->SetContentCharset(NS_ConvertUTF16toUTF8(aCharset));
190
0
  }
191
0
192
0
  // Tell the document to start loading
193
0
  nsCOMPtr<nsIStreamListener> listener;
194
0
195
0
  // Keep the XULXBL state in sync with the HTML case
196
0
  if (mForceEnableXULXBL) {
197
0
    document->ForceEnableXULXBL();
198
0
  }
199
0
200
0
  // Have to pass false for reset here, else the reset will remove
201
0
  // our event listener.  Should that listener addition move to later
202
0
  // than this call?
203
0
  nsresult rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
204
0
                                            nullptr, nullptr,
205
0
                                            getter_AddRefs(listener),
206
0
                                            false);
207
0
208
0
  if (NS_FAILED(rv) || !listener) {
209
0
    aRv.Throw(NS_ERROR_FAILURE);
210
0
    return nullptr;
211
0
  }
212
0
213
0
  // Now start pumping data to the listener
214
0
  nsresult status;
215
0
216
0
  rv = listener->OnStartRequest(parserChannel, nullptr);
217
0
  if (NS_FAILED(rv))
218
0
    parserChannel->Cancel(rv);
219
0
  parserChannel->GetStatus(&status);
220
0
221
0
  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
222
0
    rv = listener->OnDataAvailable(parserChannel, nullptr, stream, 0,
223
0
                                   aContentLength);
224
0
    if (NS_FAILED(rv))
225
0
      parserChannel->Cancel(rv);
226
0
    parserChannel->GetStatus(&status);
227
0
  }
228
0
229
0
  rv = listener->OnStopRequest(parserChannel, nullptr, status);
230
0
  // Failure returned from OnStopRequest does not affect the final status of
231
0
  // the channel, so we do not need to call Cancel(rv) as we do above.
232
0
233
0
  if (NS_FAILED(rv)) {
234
0
    aRv.Throw(NS_ERROR_FAILURE);
235
0
    return nullptr;
236
0
  }
237
0
238
0
  return document.forget();
239
0
}
240
241
/*static */already_AddRefed<DOMParser>
242
DOMParser::Constructor(const GlobalObject& aOwner,
243
                       ErrorResult& rv)
244
0
{
245
0
  MOZ_ASSERT(NS_IsMainThread());
246
0
  nsCOMPtr<nsIPrincipal> docPrincipal = aOwner.GetSubjectPrincipal();
247
0
  nsCOMPtr<nsIURI> documentURI;
248
0
  nsIURI* baseURI = nullptr;
249
0
  if (nsContentUtils::IsSystemPrincipal(docPrincipal)) {
250
0
    docPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
251
0
    docPrincipal->GetURI(getter_AddRefs(documentURI));
252
0
  } else {
253
0
    // Grab document and base URIs off the window our constructor was
254
0
    // called on. Error out if anything untoward happens.
255
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aOwner.GetAsSupports());
256
0
    if (!window) {
257
0
      rv.Throw(NS_ERROR_UNEXPECTED);
258
0
      return nullptr;
259
0
    }
260
0
261
0
    baseURI = window->GetDocBaseURI();
262
0
    documentURI = window->GetDocumentURI();
263
0
  }
264
0
265
0
  if (!documentURI) {
266
0
    rv.Throw(NS_ERROR_UNEXPECTED);
267
0
    return nullptr;
268
0
  }
269
0
270
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner.GetAsSupports());
271
0
  MOZ_ASSERT(global);
272
0
  RefPtr<DOMParser> domParser = new DOMParser(global, docPrincipal,
273
0
                                              documentURI, baseURI);
274
0
  return domParser.forget();
275
0
}
276
277
// static
278
already_AddRefed<DOMParser>
279
DOMParser::CreateWithoutGlobal(ErrorResult& aRv)
280
0
{
281
0
  nsCOMPtr<nsIPrincipal> docPrincipal =
282
0
    NullPrincipal::CreateWithoutOriginAttributes();
283
0
  nsCOMPtr<nsIURI> documentURI;
284
0
  docPrincipal->GetURI(getter_AddRefs(documentURI));
285
0
286
0
  if (!documentURI) {
287
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
288
0
    return nullptr;
289
0
  }
290
0
291
0
  RefPtr<DOMParser> domParser = new DOMParser(nullptr, docPrincipal,
292
0
                                              documentURI, nullptr);
293
0
  return domParser.forget();
294
0
}
295
296
already_AddRefed<nsIDocument>
297
DOMParser::SetUpDocument(DocumentFlavor aFlavor, ErrorResult& aRv)
298
0
{
299
0
  // We should really just use mOwner here, but nsDocument gets confused
300
0
  // if we pass it a scriptHandlingObject that doesn't QI to
301
0
  // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
302
0
  // a window global) breaks. The correct solution is just to wean nsDocument
303
0
  // off of nsIScriptGlobalObject, but that's a yak to shave another day.
304
0
  nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
305
0
    do_QueryInterface(mOwner);
306
0
307
0
  // Try to inherit a style backend.
308
0
  NS_ASSERTION(mPrincipal, "Must have principal by now");
309
0
  NS_ASSERTION(mDocumentURI, "Must have document URI by now");
310
0
311
0
  nsCOMPtr<nsIDocument> doc;
312
0
  nsresult rv = NS_NewDOMDocument(getter_AddRefs(doc), EmptyString(), EmptyString(),
313
0
                                  nullptr, mDocumentURI, mBaseURI, mPrincipal,
314
0
                                  true, scriptHandlingObject, aFlavor);
315
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
316
0
    aRv.Throw(rv);
317
0
    return nullptr;
318
0
  }
319
0
320
0
  return doc.forget();
321
0
}