Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xml/nsXMLContentSink.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 "nsCOMPtr.h"
8
#include "nsXMLContentSink.h"
9
#include "nsIParser.h"
10
#include "nsIDocument.h"
11
#include "nsIContent.h"
12
#include "nsIURI.h"
13
#include "nsNetUtil.h"
14
#include "nsIDocShell.h"
15
#include "nsIStyleSheetLinkingElement.h"
16
#include "nsHTMLParts.h"
17
#include "nsCRT.h"
18
#include "mozilla/StyleSheetInlines.h"
19
#include "mozilla/css/Loader.h"
20
#include "nsGkAtoms.h"
21
#include "nsContentUtils.h"
22
#include "nsDocElementCreatedNotificationRunner.h"
23
#include "nsIScriptContext.h"
24
#include "nsNameSpaceManager.h"
25
#include "nsIServiceManager.h"
26
#include "nsIScriptSecurityManager.h"
27
#include "nsIContentViewer.h"
28
#include "prtime.h"
29
#include "mozilla/Logging.h"
30
#include "nsRect.h"
31
#include "nsIWebNavigation.h"
32
#include "nsIScriptElement.h"
33
#include "nsStyleLinkElement.h"
34
#include "nsReadableUtils.h"
35
#include "nsUnicharUtils.h"
36
#include "nsICookieService.h"
37
#include "nsIPrompt.h"
38
#include "nsIChannel.h"
39
#include "nsIPrincipal.h"
40
#include "nsXMLPrettyPrinter.h"
41
#include "nsNodeInfoManager.h"
42
#include "nsContentCreatorFunctions.h"
43
#include "nsIContentPolicy.h"
44
#include "nsContentPolicyUtils.h"
45
#include "nsError.h"
46
#include "nsNodeUtils.h"
47
#include "nsIScriptGlobalObject.h"
48
#include "nsIHTMLDocument.h"
49
#include "mozAutoDocUpdate.h"
50
#include "nsMimeTypes.h"
51
#include "nsHtml5SVGLoadDispatcher.h"
52
#include "nsTextNode.h"
53
#include "mozilla/dom/CDATASection.h"
54
#include "mozilla/dom/Comment.h"
55
#include "mozilla/dom/DocumentType.h"
56
#include "mozilla/dom/Element.h"
57
#include "mozilla/dom/HTMLTemplateElement.h"
58
#include "mozilla/dom/ProcessingInstruction.h"
59
#include "mozilla/dom/ScriptLoader.h"
60
#include "mozilla/dom/txMozillaXSLTProcessor.h"
61
#include "mozilla/LoadInfo.h"
62
63
using namespace mozilla;
64
using namespace mozilla::dom;
65
66
// XXX Open Issues:
67
// 1) what's not allowed - We need to figure out which HTML tags
68
//    (prefixed with a HTML namespace qualifier) are explicitly not
69
//    allowed (if any).
70
// 2) factoring code with nsHTMLContentSink - There's some amount of
71
//    common code between this and the HTML content sink. This will
72
//    increase as we support more and more HTML elements. How can code
73
//    from the code be factored?
74
75
nsresult
76
NS_NewXMLContentSink(nsIXMLContentSink** aResult,
77
                     nsIDocument* aDoc,
78
                     nsIURI* aURI,
79
                     nsISupports* aContainer,
80
                     nsIChannel* aChannel)
81
0
{
82
0
  MOZ_ASSERT(nullptr != aResult, "null ptr");
83
0
  if (nullptr == aResult) {
84
0
    return NS_ERROR_NULL_POINTER;
85
0
  }
86
0
  RefPtr<nsXMLContentSink> it = new nsXMLContentSink();
87
0
88
0
  nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
89
0
  NS_ENSURE_SUCCESS(rv, rv);
90
0
91
0
  it.forget(aResult);
92
0
  return NS_OK;
93
0
}
94
95
nsXMLContentSink::nsXMLContentSink()
96
  : mState(eXMLContentSinkState_InProlog)
97
  , mTextLength(0)
98
  , mNotifyLevel(0)
99
  , mPrettyPrintXML(true)
100
  , mPrettyPrintHasSpecialRoot(0)
101
  , mPrettyPrintHasFactoredElements(0)
102
  , mPrettyPrinting(0)
103
  , mPreventScriptExecution(0)
104
0
{
105
0
  PodArrayZero(mText);
106
0
}
107
108
nsXMLContentSink::~nsXMLContentSink()
109
0
{
110
0
}
111
112
nsresult
113
nsXMLContentSink::Init(nsIDocument* aDoc,
114
                       nsIURI* aURI,
115
                       nsISupports* aContainer,
116
                       nsIChannel* aChannel)
117
0
{
118
0
  nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
119
0
  NS_ENSURE_SUCCESS(rv, rv);
120
0
121
0
  aDoc->AddObserver(this);
122
0
  mIsDocumentObserver = true;
123
0
124
0
  if (!mDocShell) {
125
0
    mPrettyPrintXML = false;
126
0
  }
127
0
128
0
  mState = eXMLContentSinkState_InProlog;
129
0
  mDocElement = nullptr;
130
0
131
0
  return NS_OK;
132
0
}
133
134
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLContentSink)
135
0
  NS_INTERFACE_MAP_ENTRY(nsIContentSink)
136
0
  NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
137
0
  NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
138
0
  NS_INTERFACE_MAP_ENTRY(nsITransformObserver)
139
0
NS_INTERFACE_MAP_END_INHERITING(nsContentSink)
140
141
NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink)
142
NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink)
143
144
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLContentSink)
145
146
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLContentSink,
147
0
                                                  nsContentSink)
148
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentHead)
149
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocElement)
150
0
  for (uint32_t i = 0, count = tmp->mContentStack.Length(); i < count; i++) {
151
0
    const StackNode& node = tmp->mContentStack.ElementAt(i);
152
0
    cb.NoteXPCOMChild(node.mContent);
153
0
  }
154
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentChildren)
155
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
156
157
// nsIContentSink
158
NS_IMETHODIMP
159
nsXMLContentSink::WillParse(void)
160
0
{
161
0
  return WillParseImpl();
162
0
}
163
164
NS_IMETHODIMP
165
nsXMLContentSink::WillBuildModel(nsDTDMode aDTDMode)
166
0
{
167
0
  WillBuildModelImpl();
168
0
169
0
  // Notify document that the load is beginning
170
0
  mDocument->BeginLoad();
171
0
172
0
  // Check for correct load-command for maybe prettyprinting
173
0
  if (mPrettyPrintXML) {
174
0
    nsAutoCString command;
175
0
    GetParser()->GetCommand(command);
176
0
    if (!command.EqualsLiteral("view")) {
177
0
      mPrettyPrintXML = false;
178
0
    }
179
0
  }
180
0
181
0
  return NS_OK;
182
0
}
183
184
bool
185
nsXMLContentSink::CanStillPrettyPrint()
186
0
{
187
0
  return mPrettyPrintXML &&
188
0
         (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot);
189
0
}
190
191
nsresult
192
nsXMLContentSink::MaybePrettyPrint()
193
0
{
194
0
  if (!CanStillPrettyPrint()) {
195
0
    mPrettyPrintXML = false;
196
0
197
0
    return NS_OK;
198
0
  }
199
0
200
0
  // stop observing in order to avoid crashing when replacing content
201
0
  mDocument->RemoveObserver(this);
202
0
  mIsDocumentObserver = false;
203
0
204
0
  // Reenable the CSSLoader so that the prettyprinting stylesheets can load
205
0
  if (mCSSLoader) {
206
0
    mCSSLoader->SetEnabled(true);
207
0
  }
208
0
209
0
  RefPtr<nsXMLPrettyPrinter> printer;
210
0
  nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer));
211
0
  NS_ENSURE_SUCCESS(rv, rv);
212
0
213
0
  bool isPrettyPrinting;
214
0
  rv = printer->PrettyPrint(mDocument, &isPrettyPrinting);
215
0
  NS_ENSURE_SUCCESS(rv, rv);
216
0
217
0
  mPrettyPrinting = isPrettyPrinting;
218
0
  return NS_OK;
219
0
}
220
221
static void
222
CheckXSLTParamPI(ProcessingInstruction* aPi,
223
                 nsIDocumentTransformer* aProcessor,
224
                 nsINode* aSource)
225
0
{
226
0
  nsAutoString target, data;
227
0
  aPi->GetTarget(target);
228
0
229
0
  // Check for namespace declarations
230
0
  if (target.EqualsLiteral("xslt-param-namespace")) {
231
0
    aPi->GetData(data);
232
0
    nsAutoString prefix, namespaceAttr;
233
0
    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix,
234
0
                                            prefix);
235
0
    if (!prefix.IsEmpty() &&
236
0
        nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace,
237
0
                                                namespaceAttr)) {
238
0
      aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr);
239
0
    }
240
0
  }
241
0
242
0
  // Check for actual parameters
243
0
  else if (target.EqualsLiteral("xslt-param")) {
244
0
    aPi->GetData(data);
245
0
    nsAutoString name, namespaceAttr, select, value;
246
0
    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name,
247
0
                                            name);
248
0
    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace,
249
0
                                            namespaceAttr);
250
0
    if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select, select)) {
251
0
      select.SetIsVoid(true);
252
0
    }
253
0
    if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value, value)) {
254
0
      value.SetIsVoid(true);
255
0
    }
256
0
    if (!name.IsEmpty()) {
257
0
      aProcessor->AddXSLTParam(name, namespaceAttr, select, value, aSource);
258
0
    }
259
0
  }
260
0
}
261
262
NS_IMETHODIMP
263
nsXMLContentSink::DidBuildModel(bool aTerminated)
264
0
{
265
0
  if (!mParser) {
266
0
    // If mParser is null, this parse has already been terminated and must
267
0
    // not been terminated again. However, nsDocument may still think that
268
0
    // the parse has not been terminated and call back into here in the case
269
0
    // where the XML parser has finished but the XSLT transform associated
270
0
    // with the document has not.
271
0
    return NS_OK;
272
0
  }
273
0
274
0
  DidBuildModelImpl(aTerminated);
275
0
276
0
  if (mXSLTProcessor) {
277
0
    // stop observing in order to avoid crashing when replacing content
278
0
    mDocument->RemoveObserver(this);
279
0
    mIsDocumentObserver = false;
280
0
281
0
    ErrorResult rv;
282
0
    RefPtr<DocumentFragment> source = mDocument->CreateDocumentFragment();
283
0
    for (nsIContent* child : mDocumentChildren) {
284
0
        // XPath data model doesn't have DocumentType nodes.
285
0
        if (child->NodeType() != nsINode::DOCUMENT_TYPE_NODE) {
286
0
            source->AppendChild(*child, rv);
287
0
            if (rv.Failed()) {
288
0
                return rv.StealNSResult();
289
0
            }
290
0
        }
291
0
    }
292
0
293
0
    // Check for xslt-param and xslt-param-namespace PIs
294
0
    for (nsIContent* child : mDocumentChildren) {
295
0
      if (auto pi = ProcessingInstruction::FromNode(child)) {
296
0
        CheckXSLTParamPI(pi, mXSLTProcessor, source);
297
0
      }
298
0
      else if (child->IsElement()) {
299
0
        // Only honor PIs in the prolog
300
0
        break;
301
0
      }
302
0
    }
303
0
304
0
    mXSLTProcessor->SetSourceContentModel(source);
305
0
    // Since the processor now holds a reference to us we drop our reference
306
0
    // to it to avoid owning cycles
307
0
    mXSLTProcessor = nullptr;
308
0
  }
309
0
  else {
310
0
    // Kick off layout for non-XSLT transformed documents.
311
0
312
0
    // Check if we want to prettyprint
313
0
    MaybePrettyPrint();
314
0
315
0
    bool startLayout = true;
316
0
317
0
    if (mPrettyPrinting) {
318
0
      NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!");
319
0
320
0
      // We're pretty-printing now.  See whether we should wait up on
321
0
      // stylesheet loads
322
0
      if (mDocument->CSSLoader()->HasPendingLoads() &&
323
0
          NS_SUCCEEDED(mDocument->CSSLoader()->AddObserver(this))) {
324
0
        // wait for those sheets to load
325
0
        startLayout = false;
326
0
      }
327
0
    }
328
0
329
0
    if (startLayout) {
330
0
      StartLayout(false);
331
0
332
0
      ScrollToRef();
333
0
    }
334
0
335
0
    mDocument->RemoveObserver(this);
336
0
    mIsDocumentObserver = false;
337
0
338
0
    mDocument->EndLoad();
339
0
340
0
    DropParserAndPerfHint();
341
0
  }
342
0
343
0
  return NS_OK;
344
0
}
345
346
NS_IMETHODIMP
347
nsXMLContentSink::OnDocumentCreated(nsIDocument* aResultDocument)
348
0
{
349
0
  NS_ENSURE_ARG(aResultDocument);
350
0
351
0
  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aResultDocument);
352
0
  if (htmlDoc) {
353
0
    htmlDoc->SetDocWriteDisabled(true);
354
0
  }
355
0
356
0
  nsCOMPtr<nsIContentViewer> contentViewer;
357
0
  mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
358
0
  if (contentViewer) {
359
0
    return contentViewer->SetDocumentInternal(aResultDocument, true);
360
0
  }
361
0
  return NS_OK;
362
0
}
363
364
NS_IMETHODIMP
365
nsXMLContentSink::OnTransformDone(nsresult aResult,
366
                                  nsIDocument* aResultDocument)
367
0
{
368
0
  MOZ_ASSERT(aResultDocument, "Don't notify about transform end without a document.");
369
0
370
0
  mDocumentChildren.Clear();
371
0
372
0
  nsCOMPtr<nsIContentViewer> contentViewer;
373
0
  mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
374
0
375
0
  if (NS_FAILED(aResult) && contentViewer) {
376
0
    // Transform failed.
377
0
    aResultDocument->SetMayStartLayout(false);
378
0
    // We have an error document.
379
0
    contentViewer->SetDocument(aResultDocument);
380
0
  }
381
0
382
0
  nsCOMPtr<nsIDocument> originalDocument = mDocument;
383
0
  bool blockingOnload = mIsBlockingOnload;
384
0
  if (!mRunsToCompletion) {
385
0
    // This BlockOnload call corresponds to the UnblockOnload call in
386
0
    // nsContentSink::DropParserAndPerfHint.
387
0
    aResultDocument->BlockOnload();
388
0
    mIsBlockingOnload = true;
389
0
  }
390
0
  // Transform succeeded, or it failed and we have an error document to display.
391
0
  mDocument = aResultDocument;
392
0
  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
393
0
  if (htmlDoc) {
394
0
    htmlDoc->SetDocWriteDisabled(false);
395
0
  }
396
0
397
0
  // Notify document observers that all the content has been stuck
398
0
  // into the document.
399
0
  // XXX do we need to notify for things like PIs?  Or just the
400
0
  // documentElement?
401
0
  nsIContent *rootElement = mDocument->GetRootElement();
402
0
  if (rootElement) {
403
0
    NS_ASSERTION(mDocument->ComputeIndexOf(rootElement) != -1,
404
0
                 "rootElement not in doc?");
405
0
    mDocument->BeginUpdate();
406
0
    nsNodeUtils::ContentInserted(mDocument, rootElement);
407
0
    mDocument->EndUpdate();
408
0
  }
409
0
410
0
  // Start the layout process
411
0
  StartLayout(false);
412
0
413
0
  ScrollToRef();
414
0
415
0
  originalDocument->EndLoad();
416
0
  if (blockingOnload) {
417
0
    // This UnblockOnload call corresponds to the BlockOnload call in
418
0
    // nsContentSink::WillBuildModelImpl.
419
0
    originalDocument->UnblockOnload(true);
420
0
  }
421
0
422
0
  DropParserAndPerfHint();
423
0
424
0
  return NS_OK;
425
0
}
426
427
NS_IMETHODIMP
428
nsXMLContentSink::StyleSheetLoaded(StyleSheet* aSheet,
429
                                   bool aWasDeferred,
430
                                   nsresult aStatus)
431
0
{
432
0
  if (!mPrettyPrinting) {
433
0
    return nsContentSink::StyleSheetLoaded(aSheet, aWasDeferred, aStatus);
434
0
  }
435
0
436
0
  if (!mDocument->CSSLoader()->HasPendingLoads()) {
437
0
    mDocument->CSSLoader()->RemoveObserver(this);
438
0
    StartLayout(false);
439
0
    ScrollToRef();
440
0
  }
441
0
442
0
  return NS_OK;
443
0
}
444
445
NS_IMETHODIMP
446
nsXMLContentSink::WillInterrupt(void)
447
0
{
448
0
  return WillInterruptImpl();
449
0
}
450
451
NS_IMETHODIMP
452
nsXMLContentSink::WillResume(void)
453
0
{
454
0
  return WillResumeImpl();
455
0
}
456
457
NS_IMETHODIMP
458
nsXMLContentSink::SetParser(nsParserBase* aParser)
459
0
{
460
0
  MOZ_ASSERT(aParser, "Should have a parser here!");
461
0
  mParser = aParser;
462
0
  return NS_OK;
463
0
}
464
465
static bool
466
FindIsAttrValue(const char16_t** aAtts, const char16_t** aResult)
467
0
{
468
0
  RefPtr<nsAtom> prefix, localName;
469
0
  for (; *aAtts; aAtts += 2) {
470
0
    int32_t nameSpaceID;
471
0
    nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
472
0
                                   getter_AddRefs(localName), &nameSpaceID);
473
0
    if (nameSpaceID == kNameSpaceID_None && localName == nsGkAtoms::is) {
474
0
      *aResult = aAtts[1];
475
0
476
0
      return true;
477
0
    }
478
0
  }
479
0
480
0
  return false;
481
0
}
482
483
nsresult
484
nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
485
                                mozilla::dom::NodeInfo* aNodeInfo,
486
                                uint32_t aLineNumber, uint32_t aColumnNumber,
487
                                nsIContent** aResult, bool* aAppendContent,
488
                                FromParser aFromParser)
489
0
{
490
0
  NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo");
491
0
492
0
  *aResult = nullptr;
493
0
  *aAppendContent = true;
494
0
  nsresult rv = NS_OK;
495
0
496
0
  RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
497
0
  RefPtr<Element> content;
498
0
499
0
  const char16_t* is = nullptr;
500
0
  if ((aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
501
0
       aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) &&
502
0
      FindIsAttrValue(aAtts, &is)) {
503
0
    const nsDependentString isStr(is);
504
0
    rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser, &isStr);
505
0
  } else {
506
0
    rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser);
507
0
  }
508
0
509
0
  NS_ENSURE_SUCCESS(rv, rv);
510
0
511
0
  if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML)
512
0
      || aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)
513
0
    ) {
514
0
    nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
515
0
    if (sele) {
516
0
      sele->SetScriptLineNumber(aLineNumber);
517
0
      sele->SetScriptColumnNumber(aColumnNumber);
518
0
      sele->SetCreatorParser(GetParser());
519
0
    } else {
520
0
      MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, "Node didn't QI to script, but SVG wasn't disabled.");
521
0
    }
522
0
  }
523
0
524
0
  // XHTML needs some special attention
525
0
  if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
526
0
    mPrettyPrintHasFactoredElements = true;
527
0
  }
528
0
  else {
529
0
    // If we care, find out if we just used a special factory.
530
0
    if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot &&
531
0
        mPrettyPrintXML) {
532
0
      mPrettyPrintHasFactoredElements =
533
0
        nsContentUtils::NameSpaceManager()->
534
0
          HasElementCreator(aNodeInfo->NamespaceID());
535
0
    }
536
0
537
0
    if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
538
0
      content.forget(aResult);
539
0
540
0
      return NS_OK;
541
0
    }
542
0
  }
543
0
544
0
  if (aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
545
0
      aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
546
0
      aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
547
0
    nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(content));
548
0
    if (ssle) {
549
0
      ssle->InitStyleLinkElement(false);
550
0
      if (aFromParser) {
551
0
        ssle->SetEnableUpdates(false);
552
0
      }
553
0
      if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
554
0
        ssle->SetLineNumber(aFromParser ? aLineNumber : 0);
555
0
        ssle->SetColumnNumber(aFromParser ? aColumnNumber : 0);
556
0
      }
557
0
    }
558
0
  }
559
0
560
0
  content.forget(aResult);
561
0
562
0
  return NS_OK;
563
0
}
564
565
566
nsresult
567
nsXMLContentSink::CloseElement(nsIContent* aContent)
568
0
{
569
0
  NS_ASSERTION(aContent, "missing element to close");
570
0
571
0
  mozilla::dom::NodeInfo *nodeInfo = aContent->NodeInfo();
572
0
573
0
  // Some HTML nodes need DoneAddingChildren() called to initialize
574
0
  // properly (eg form state restoration).
575
0
  if ((nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
576
0
       (nodeInfo->NameAtom() == nsGkAtoms::select ||
577
0
        nodeInfo->NameAtom() == nsGkAtoms::textarea ||
578
0
        nodeInfo->NameAtom() == nsGkAtoms::video ||
579
0
        nodeInfo->NameAtom() == nsGkAtoms::audio ||
580
0
        nodeInfo->NameAtom() == nsGkAtoms::head ||
581
0
        nodeInfo->NameAtom() == nsGkAtoms::object))
582
0
      || nodeInfo->NameAtom() == nsGkAtoms::title
583
0
      ) {
584
0
    aContent->DoneAddingChildren(HaveNotifiedForCurrentContent());
585
0
  }
586
0
587
0
  if (IsMonolithicContainer(nodeInfo)) {
588
0
    mInMonolithicContainer--;
589
0
  }
590
0
591
0
  if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
592
0
      !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
593
0
    return NS_OK;
594
0
  }
595
0
596
0
  if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML)
597
0
      || nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)
598
0
    ) {
599
0
    nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent);
600
0
    if (!sele) {
601
0
      MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, "Node didn't QI to script, but SVG wasn't disabled.");
602
0
      return NS_OK;
603
0
    }
604
0
605
0
    if (mPreventScriptExecution) {
606
0
      sele->PreventExecution();
607
0
      return NS_OK;
608
0
    }
609
0
610
0
    // Always check the clock in nsContentSink right after a script
611
0
    StopDeflecting();
612
0
613
0
    // Now tell the script that it's ready to go. This may execute the script
614
0
    // or return true, or neither if the script doesn't need executing.
615
0
    bool block = sele->AttemptToExecute();
616
0
617
0
    // If the parser got blocked, make sure to return the appropriate rv.
618
0
    // I'm not sure if this is actually needed or not.
619
0
    if (mParser && !mParser->IsParserEnabled()) {
620
0
      block = true;
621
0
    }
622
0
623
0
    return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK;
624
0
  }
625
0
626
0
  nsresult rv = NS_OK;
627
0
  if (nodeInfo->Equals(nsGkAtoms::meta, kNameSpaceID_XHTML) &&
628
0
           // Need to check here to make sure this meta tag does not set
629
0
           // mPrettyPrintXML to false when we have a special root!
630
0
           (!mPrettyPrintXML || !mPrettyPrintHasSpecialRoot)) {
631
0
    rv = ProcessMETATag(aContent);
632
0
  }
633
0
  else if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
634
0
           nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
635
0
           nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
636
0
    nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aContent));
637
0
    if (ssle) {
638
0
      ssle->SetEnableUpdates(true);
639
0
      auto updateOrError =
640
0
        ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
641
0
      if (updateOrError.isErr()) {
642
0
        rv = updateOrError.unwrapErr();
643
0
      } else if (updateOrError.unwrap().ShouldBlock() && !mRunsToCompletion) {
644
0
        ++mPendingSheetCount;
645
0
        mScriptLoader->AddParserBlockingScriptExecutionBlocker();
646
0
      }
647
0
    }
648
0
  }
649
0
650
0
  return rv;
651
0
}
652
653
nsresult
654
nsXMLContentSink::AddContentAsLeaf(nsIContent *aContent)
655
0
{
656
0
  nsresult result = NS_OK;
657
0
658
0
  if (mState == eXMLContentSinkState_InProlog) {
659
0
    NS_ASSERTION(mDocument, "Fragments have no prolog");
660
0
    mDocumentChildren.AppendElement(aContent);
661
0
  } else if (mState == eXMLContentSinkState_InEpilog) {
662
0
    NS_ASSERTION(mDocument, "Fragments have no epilog");
663
0
    if (mXSLTProcessor) {
664
0
      mDocumentChildren.AppendElement(aContent);
665
0
    } else {
666
0
      mDocument->AppendChildTo(aContent, false);
667
0
    }
668
0
  } else {
669
0
    nsCOMPtr<nsIContent> parent = GetCurrentContent();
670
0
671
0
    if (parent) {
672
0
      result = parent->AppendChildTo(aContent, false);
673
0
    }
674
0
  }
675
0
  return result;
676
0
}
677
678
// Create an XML parser and an XSL content sink and start parsing
679
// the XSL stylesheet located at the given URI.
680
nsresult
681
nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl)
682
0
{
683
0
  nsCOMPtr<nsIDocumentTransformer> processor = new txMozillaXSLTProcessor();
684
0
685
0
  processor->SetTransformObserver(this);
686
0
687
0
  if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, mDocument))) {
688
0
    mXSLTProcessor.swap(processor);
689
0
  }
690
0
691
0
  // Intentionally ignore errors here, we should continue loading the
692
0
  // XML document whether we're able to load the XSLT stylesheet or
693
0
  // not.
694
0
695
0
  return NS_OK;
696
0
}
697
698
nsresult
699
nsXMLContentSink::ProcessStyleLinkFromHeader(const nsAString& aHref,
700
                                             bool aAlternate,
701
                                             const nsAString& aTitle,
702
                                             const nsAString& aType,
703
                                             const nsAString& aMedia,
704
                                             const nsAString& aReferrerPolicy)
705
0
{
706
0
  mPrettyPrintXML = false;
707
0
708
0
  nsAutoCString cmd;
709
0
  if (mParser)
710
0
    GetParser()->GetCommand(cmd);
711
0
  if (cmd.EqualsASCII(kLoadAsData))
712
0
    return NS_OK; // Do not load stylesheets when loading as data
713
0
714
0
  bool wasXSLT;
715
0
  nsresult rv = MaybeProcessXSLTLink(nullptr, aHref, aAlternate, aType, aType,
716
0
                                     aMedia, aReferrerPolicy, &wasXSLT);
717
0
  NS_ENSURE_SUCCESS(rv, rv);
718
0
  if (wasXSLT) {
719
0
    // We're done here.
720
0
    return NS_OK;
721
0
  }
722
0
723
0
  // Otherwise fall through to nsContentSink to handle CSS Link headers.
724
0
  return nsContentSink::ProcessStyleLinkFromHeader(aHref, aAlternate,
725
0
                                                   aTitle, aType, aMedia,
726
0
                                                   aReferrerPolicy);
727
0
}
728
729
nsresult
730
nsXMLContentSink::MaybeProcessXSLTLink(
731
  ProcessingInstruction* aProcessingInstruction,
732
  const nsAString& aHref,
733
  bool aAlternate,
734
  const nsAString& aTitle,
735
  const nsAString& aType,
736
  const nsAString& aMedia,
737
  const nsAString& aReferrerPolicy,
738
  bool* aWasXSLT)
739
0
{
740
0
  bool wasXSLT =
741
0
    aType.LowerCaseEqualsLiteral(TEXT_XSL) ||
742
0
    aType.LowerCaseEqualsLiteral(APPLICATION_XSLT_XML) ||
743
0
    aType.LowerCaseEqualsLiteral(TEXT_XML) ||
744
0
    aType.LowerCaseEqualsLiteral(APPLICATION_XML);
745
0
746
0
  if (aWasXSLT) {
747
0
    *aWasXSLT = wasXSLT;
748
0
  }
749
0
750
0
  if (!wasXSLT) {
751
0
    return NS_OK;
752
0
  }
753
0
754
0
  if (aAlternate) {
755
0
    // don't load alternate XSLT
756
0
    return NS_OK;
757
0
  }
758
0
  // LoadXSLStyleSheet needs a mDocShell.
759
0
  if (!mDocShell) {
760
0
    return NS_OK;
761
0
  }
762
0
763
0
  nsCOMPtr<nsIURI> url;
764
0
  nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
765
0
                 mDocument->GetDocBaseURI());
766
0
  NS_ENSURE_SUCCESS(rv, rv);
767
0
768
0
  // Do security check
769
0
  nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
770
0
  rv = secMan->
771
0
    CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url,
772
0
                              nsIScriptSecurityManager::ALLOW_CHROME);
773
0
  NS_ENSURE_SUCCESS(rv, NS_OK);
774
0
775
0
  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
776
0
    new net::LoadInfo(mDocument->NodePrincipal(), // loading principal
777
0
                      mDocument->NodePrincipal(), // triggering principal
778
0
                      aProcessingInstruction,
779
0
                      nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
780
0
                      nsIContentPolicy::TYPE_XSLT);
781
0
782
0
  // Do content policy check
783
0
  int16_t decision = nsIContentPolicy::ACCEPT;
784
0
  rv = NS_CheckContentLoadPolicy(url,
785
0
                                 secCheckLoadInfo,
786
0
                                 NS_ConvertUTF16toUTF8(aType),
787
0
                                 &decision,
788
0
                                 nsContentUtils::GetContentPolicy());
789
0
790
0
  NS_ENSURE_SUCCESS(rv, rv);
791
0
792
0
  if (NS_CP_REJECTED(decision)) {
793
0
    return NS_OK;
794
0
  }
795
0
796
0
  return LoadXSLStyleSheet(url);
797
0
}
798
799
void
800
nsXMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding)
801
0
{
802
0
  if (mDocument) {
803
0
    mDocument->SetDocumentCharacterSet(aEncoding);
804
0
  }
805
0
}
806
807
nsISupports *
808
nsXMLContentSink::GetTarget()
809
0
{
810
0
  return mDocument;
811
0
}
812
813
bool
814
nsXMLContentSink::IsScriptExecuting()
815
0
{
816
0
  return IsScriptExecutingImpl();
817
0
}
818
819
nsresult
820
nsXMLContentSink::FlushText(bool aReleaseTextNode)
821
0
{
822
0
  nsresult rv = NS_OK;
823
0
824
0
  if (mTextLength != 0) {
825
0
    if (mLastTextNode) {
826
0
      bool notify = HaveNotifiedForCurrentContent();
827
0
      // We could probably always increase mInNotification here since
828
0
      // if AppendText doesn't notify it shouldn't trigger evil code.
829
0
      // But just in case it does, we don't want to mask any notifications.
830
0
      if (notify) {
831
0
        ++mInNotification;
832
0
      }
833
0
      rv = mLastTextNode->AppendText(mText, mTextLength, notify);
834
0
      if (notify) {
835
0
        --mInNotification;
836
0
      }
837
0
838
0
      mTextLength = 0;
839
0
    } else {
840
0
      RefPtr<nsTextNode> textContent = new nsTextNode(mNodeInfoManager);
841
0
842
0
      mLastTextNode = textContent;
843
0
844
0
      // Set the text in the text node
845
0
      textContent->SetText(mText, mTextLength, false);
846
0
      mTextLength = 0;
847
0
848
0
      // Add text to its parent
849
0
      rv = AddContentAsLeaf(textContent);
850
0
    }
851
0
  }
852
0
853
0
  if (aReleaseTextNode) {
854
0
    mLastTextNode = nullptr;
855
0
  }
856
0
857
0
  return rv;
858
0
}
859
860
nsIContent*
861
nsXMLContentSink::GetCurrentContent()
862
0
{
863
0
  if (mContentStack.Length() == 0) {
864
0
    return nullptr;
865
0
  }
866
0
  return GetCurrentStackNode()->mContent;
867
0
}
868
869
StackNode*
870
nsXMLContentSink::GetCurrentStackNode()
871
0
{
872
0
  int32_t count = mContentStack.Length();
873
0
  return count != 0 ? &mContentStack[count-1] : nullptr;
874
0
}
875
876
877
nsresult
878
nsXMLContentSink::PushContent(nsIContent *aContent)
879
0
{
880
0
  MOZ_ASSERT(aContent, "Null content being pushed!");
881
0
  StackNode *sn = mContentStack.AppendElement();
882
0
  NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY);
883
0
884
0
  nsIContent* contentToPush = aContent;
885
0
886
0
  // When an XML parser would append a node to a template element, it
887
0
  // must instead append it to the template element's template contents.
888
0
  if (contentToPush->IsHTMLElement(nsGkAtoms::_template)) {
889
0
    HTMLTemplateElement* templateElement =
890
0
      static_cast<HTMLTemplateElement*>(contentToPush);
891
0
    contentToPush = templateElement->Content();
892
0
  }
893
0
894
0
  sn->mContent = contentToPush;
895
0
  sn->mNumFlushed = 0;
896
0
  return NS_OK;
897
0
}
898
899
void
900
nsXMLContentSink::PopContent()
901
0
{
902
0
  int32_t count = mContentStack.Length();
903
0
904
0
  if (count == 0) {
905
0
    NS_WARNING("Popping empty stack");
906
0
    return;
907
0
  }
908
0
909
0
  mContentStack.RemoveElementAt(count - 1);
910
0
}
911
912
bool
913
nsXMLContentSink::HaveNotifiedForCurrentContent() const
914
0
{
915
0
  uint32_t stackLength = mContentStack.Length();
916
0
  if (stackLength) {
917
0
    const StackNode& stackNode = mContentStack[stackLength - 1];
918
0
    nsIContent* parent = stackNode.mContent;
919
0
    return stackNode.mNumFlushed == parent->GetChildCount();
920
0
  }
921
0
  return true;
922
0
}
923
924
void
925
nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets)
926
0
{
927
0
  // XXXbz if aIgnorePendingSheets is true, what should we do when
928
0
  // mXSLTProcessor or CanStillPrettyPrint()?
929
0
  if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) {
930
0
    return;
931
0
  }
932
0
  StartLayout(aIgnorePendingSheets);
933
0
}
934
935
////////////////////////////////////////////////////////////////////////
936
937
bool
938
nsXMLContentSink::SetDocElement(int32_t aNameSpaceID,
939
                                nsAtom* aTagName,
940
                                nsIContent *aContent)
941
0
{
942
0
  if (mDocElement)
943
0
    return false;
944
0
945
0
  mDocElement = aContent;
946
0
947
0
  if (mXSLTProcessor) {
948
0
    mDocumentChildren.AppendElement(aContent);
949
0
    return true;
950
0
  }
951
0
952
0
  if (!mDocumentChildren.IsEmpty()) {
953
0
    for (nsIContent* child : mDocumentChildren) {
954
0
      mDocument->AppendChildTo(child, false);
955
0
    }
956
0
    mDocumentChildren.Clear();
957
0
  }
958
0
959
0
  // check for root elements that needs special handling for
960
0
  // prettyprinting
961
0
  if ((aNameSpaceID == kNameSpaceID_XBL &&
962
0
       aTagName == nsGkAtoms::bindings) ||
963
0
      (aNameSpaceID == kNameSpaceID_XSLT &&
964
0
       (aTagName == nsGkAtoms::stylesheet ||
965
0
        aTagName == nsGkAtoms::transform))) {
966
0
    mPrettyPrintHasSpecialRoot = true;
967
0
    if (mPrettyPrintXML) {
968
0
      // In this case, disable script execution, stylesheet
969
0
      // loading, and auto XLinks since we plan to prettyprint.
970
0
      mDocument->ScriptLoader()->SetEnabled(false);
971
0
      if (mCSSLoader) {
972
0
        mCSSLoader->SetEnabled(false);
973
0
      }
974
0
    }
975
0
  }
976
0
977
0
  nsresult rv = mDocument->AppendChildTo(mDocElement, NotifyForDocElement());
978
0
  if (NS_FAILED(rv)) {
979
0
    // If we return false here, the caller will bail out because it won't
980
0
    // find a parent content node to append to, which is fine.
981
0
    return false;
982
0
  }
983
0
984
0
  if (aTagName == nsGkAtoms::html &&
985
0
      aNameSpaceID == kNameSpaceID_XHTML) {
986
0
    ProcessOfflineManifest(aContent);
987
0
  }
988
0
989
0
  return true;
990
0
}
991
992
NS_IMETHODIMP
993
nsXMLContentSink::HandleStartElement(const char16_t *aName,
994
                                     const char16_t **aAtts,
995
                                     uint32_t aAttsCount,
996
                                     uint32_t aLineNumber,
997
                                     uint32_t aColumnNumber)
998
0
{
999
0
  return HandleStartElement(aName, aAtts, aAttsCount, aLineNumber,
1000
0
                            aColumnNumber, true);
1001
0
}
1002
1003
nsresult
1004
nsXMLContentSink::HandleStartElement(const char16_t *aName,
1005
                                     const char16_t **aAtts,
1006
                                     uint32_t aAttsCount,
1007
                                     uint32_t aLineNumber,
1008
                                     uint32_t aColumnNumber,
1009
                                     bool aInterruptable)
1010
0
{
1011
0
  MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
1012
0
  // Adjust aAttsCount so it's the actual number of attributes
1013
0
  aAttsCount /= 2;
1014
0
1015
0
  nsresult result = NS_OK;
1016
0
  bool appendContent = true;
1017
0
  nsCOMPtr<nsIContent> content;
1018
0
1019
0
  // XXX Hopefully the parser will flag this before we get
1020
0
  // here. If we're in the epilog, there should be no
1021
0
  // new elements
1022
0
  MOZ_ASSERT(eXMLContentSinkState_InEpilog != mState);
1023
0
1024
0
  FlushText();
1025
0
  DidAddContent();
1026
0
1027
0
  mState = eXMLContentSinkState_InDocumentElement;
1028
0
1029
0
  int32_t nameSpaceID;
1030
0
  RefPtr<nsAtom> prefix, localName;
1031
0
  nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
1032
0
                                 getter_AddRefs(localName), &nameSpaceID);
1033
0
1034
0
  if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName, aLineNumber)) {
1035
0
    return NS_OK;
1036
0
  }
1037
0
1038
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
1039
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
1040
0
                                           nsINode::ELEMENT_NODE);
1041
0
1042
0
  result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber,
1043
0
                         aColumnNumber, getter_AddRefs(content), &appendContent,
1044
0
                         FROM_PARSER_NETWORK);
1045
0
  NS_ENSURE_SUCCESS(result, result);
1046
0
1047
0
  // Have to do this before we push the new content on the stack... and have to
1048
0
  // do that before we set attributes, call BindToTree, etc.  Ideally we'd push
1049
0
  // on the stack inside CreateElement (which is effectively what the HTML sink
1050
0
  // does), but that's hard with all the subclass overrides going on.
1051
0
  nsCOMPtr<nsIContent> parent = GetCurrentContent();
1052
0
1053
0
  result = PushContent(content);
1054
0
  NS_ENSURE_SUCCESS(result, result);
1055
0
1056
0
  // Set the attributes on the new content element
1057
0
  result = AddAttributes(aAtts, content->AsElement());
1058
0
1059
0
  if (NS_OK == result) {
1060
0
    // Store the element
1061
0
    if (!SetDocElement(nameSpaceID, localName, content) && appendContent) {
1062
0
      NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
1063
0
1064
0
      parent->AppendChildTo(content, false);
1065
0
    }
1066
0
  }
1067
0
1068
0
  // Some HTML nodes need DoneCreatingElement() called to initialize
1069
0
  // properly (eg form state restoration).
1070
0
  if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
1071
0
    if (nodeInfo->NameAtom() == nsGkAtoms::input ||
1072
0
        nodeInfo->NameAtom() == nsGkAtoms::button ||
1073
0
        nodeInfo->NameAtom() == nsGkAtoms::menuitem ||
1074
0
        nodeInfo->NameAtom() == nsGkAtoms::audio ||
1075
0
        nodeInfo->NameAtom() == nsGkAtoms::video) {
1076
0
      content->DoneCreatingElement();
1077
0
    } else if (nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) {
1078
0
      mCurrentHead = content;
1079
0
    }
1080
0
  }
1081
0
1082
0
  if (IsMonolithicContainer(nodeInfo)) {
1083
0
    mInMonolithicContainer++;
1084
0
  }
1085
0
1086
0
  if (!mXSLTProcessor) {
1087
0
    if (content == mDocElement) {
1088
0
      nsContentUtils::AddScriptRunner(
1089
0
          new nsDocElementCreatedNotificationRunner(mDocument));
1090
0
1091
0
      if (aInterruptable && NS_SUCCEEDED(result) && mParser && !mParser->IsParserEnabled()) {
1092
0
        return NS_ERROR_HTMLPARSER_BLOCK;
1093
0
      }
1094
0
    } else if (!mCurrentHead) {
1095
0
      // This isn't the root and we're not inside an XHTML <head>.
1096
0
      // Might need to start layout
1097
0
      MaybeStartLayout(false);
1098
0
    }
1099
0
  }
1100
0
1101
0
  return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() :
1102
0
                                                  result;
1103
0
}
1104
1105
NS_IMETHODIMP
1106
nsXMLContentSink::HandleEndElement(const char16_t *aName)
1107
0
{
1108
0
  return HandleEndElement(aName, true);
1109
0
}
1110
1111
nsresult
1112
nsXMLContentSink::HandleEndElement(const char16_t *aName,
1113
                                   bool aInterruptable)
1114
0
{
1115
0
  nsresult result = NS_OK;
1116
0
1117
0
  // XXX Hopefully the parser will flag this before we get
1118
0
  // here. If we're in the prolog or epilog, there should be
1119
0
  // no close tags for elements.
1120
0
  MOZ_ASSERT(eXMLContentSinkState_InDocumentElement == mState);
1121
0
1122
0
  FlushText();
1123
0
1124
0
  StackNode* sn = GetCurrentStackNode();
1125
0
  if (!sn) {
1126
0
    return NS_ERROR_UNEXPECTED;
1127
0
  }
1128
0
1129
0
  nsCOMPtr<nsIContent> content;
1130
0
  sn->mContent.swap(content);
1131
0
  uint32_t numFlushed = sn->mNumFlushed;
1132
0
1133
0
  PopContent();
1134
0
  NS_ASSERTION(content, "failed to pop content");
1135
#ifdef DEBUG
1136
  // Check that we're closing the right thing
1137
  RefPtr<nsAtom> debugNameSpacePrefix, debugTagAtom;
1138
  int32_t debugNameSpaceID;
1139
  nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix),
1140
                                 getter_AddRefs(debugTagAtom),
1141
                                 &debugNameSpaceID);
1142
  // Check if we are closing a template element because template
1143
  // elements do not get pushed on the stack, the template
1144
  // element content is pushed instead.
1145
  bool isTemplateElement = debugTagAtom == nsGkAtoms::_template &&
1146
                           debugNameSpaceID == kNameSpaceID_XHTML;
1147
  NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) ||
1148
               (debugNameSpaceID == kNameSpaceID_MathML &&
1149
                content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_MathML &&
1150
                content->NodeInfo()->Equals(debugTagAtom)) ||
1151
               (debugNameSpaceID == kNameSpaceID_SVG &&
1152
                content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_SVG &&
1153
                content->NodeInfo()->Equals(debugTagAtom)) ||
1154
               isTemplateElement, "Wrong element being closed");
1155
#endif
1156
1157
0
  // Make sure to notify on our kids before we call out to any other code that
1158
0
  // might reenter us and call FlushTags, in a state in which we've already
1159
0
  // popped "content" from the stack but haven't notified on its kids yet.
1160
0
  int32_t stackLen = mContentStack.Length();
1161
0
  if (mNotifyLevel >= stackLen) {
1162
0
    if (numFlushed < content->GetChildCount()) {
1163
0
      NotifyAppend(content, numFlushed);
1164
0
    }
1165
0
    mNotifyLevel = stackLen - 1;
1166
0
  }
1167
0
1168
0
  result = CloseElement(content);
1169
0
1170
0
  if (mCurrentHead == content) {
1171
0
    mCurrentHead = nullptr;
1172
0
  }
1173
0
1174
0
  if (mDocElement == content) {
1175
0
    // XXXbz for roots that don't want to be appended on open, we
1176
0
    // probably need to deal here.... (and stop appending them on open).
1177
0
    mState = eXMLContentSinkState_InEpilog;
1178
0
1179
0
    // We might have had no occasion to start layout yet.  Do so now.
1180
0
    MaybeStartLayout(false);
1181
0
  }
1182
0
1183
0
  DidAddContent();
1184
0
1185
0
  if (content->IsSVGElement(nsGkAtoms::svg)) {
1186
0
    FlushTags();
1187
0
    nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content);
1188
0
    if (NS_FAILED(content->OwnerDoc()->Dispatch(TaskCategory::Other,
1189
0
                                                event.forget()))) {
1190
0
      NS_WARNING("failed to dispatch svg load dispatcher");
1191
0
    }
1192
0
  }
1193
0
1194
0
  return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() :
1195
0
                                                  result;
1196
0
}
1197
1198
NS_IMETHODIMP
1199
nsXMLContentSink::HandleComment(const char16_t *aName)
1200
0
{
1201
0
  FlushText();
1202
0
1203
0
  RefPtr<Comment> comment = new Comment(mNodeInfoManager);
1204
0
  comment->SetText(nsDependentString(aName), false);
1205
0
  nsresult rv = AddContentAsLeaf(comment);
1206
0
  DidAddContent();
1207
0
1208
0
  return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1209
0
}
1210
1211
NS_IMETHODIMP
1212
nsXMLContentSink::HandleCDataSection(const char16_t *aData,
1213
                                     uint32_t aLength)
1214
0
{
1215
0
  // XSLT doesn't differentiate between text and cdata and wants adjacent
1216
0
  // textnodes merged, so add as text.
1217
0
  if (mXSLTProcessor) {
1218
0
    return AddText(aData, aLength);
1219
0
  }
1220
0
1221
0
  FlushText();
1222
0
1223
0
  RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
1224
0
  cdata->SetText(aData, aLength, false);
1225
0
  nsresult rv = AddContentAsLeaf(cdata);
1226
0
  DidAddContent();
1227
0
1228
0
  return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1229
0
}
1230
1231
NS_IMETHODIMP
1232
nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset,
1233
                                    const nsAString & aName,
1234
                                    const nsAString & aSystemId,
1235
                                    const nsAString & aPublicId,
1236
                                    nsISupports* aCatalogData)
1237
0
{
1238
0
  FlushText();
1239
0
1240
0
  NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment");
1241
0
1242
0
  RefPtr<nsAtom> name = NS_Atomize(aName);
1243
0
  NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
1244
0
1245
0
  // Create a new doctype node
1246
0
  RefPtr<DocumentType> docType = NS_NewDOMDocumentType(mNodeInfoManager,
1247
0
                                                       name, aPublicId,
1248
0
                                                       aSystemId, aSubset);
1249
0
1250
0
  MOZ_ASSERT(!aCatalogData, "Need to add back support for catalog style "
1251
0
                            "sheets");
1252
0
1253
0
  mDocumentChildren.AppendElement(docType);
1254
0
  DidAddContent();
1255
0
  return DidProcessATokenImpl();
1256
0
}
1257
1258
NS_IMETHODIMP
1259
nsXMLContentSink::HandleCharacterData(const char16_t *aData,
1260
                                      uint32_t aLength)
1261
0
{
1262
0
  return HandleCharacterData(aData, aLength, true);
1263
0
}
1264
1265
nsresult
1266
nsXMLContentSink::HandleCharacterData(const char16_t *aData, uint32_t aLength,
1267
                                      bool aInterruptable)
1268
0
{
1269
0
  nsresult rv = NS_OK;
1270
0
  if (aData && mState != eXMLContentSinkState_InProlog &&
1271
0
      mState != eXMLContentSinkState_InEpilog) {
1272
0
    rv = AddText(aData, aLength);
1273
0
  }
1274
0
  return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1275
0
}
1276
1277
NS_IMETHODIMP
1278
nsXMLContentSink::HandleProcessingInstruction(const char16_t *aTarget,
1279
                                              const char16_t *aData)
1280
0
{
1281
0
  FlushText();
1282
0
1283
0
  const nsDependentString target(aTarget);
1284
0
  const nsDependentString data(aData);
1285
0
1286
0
  RefPtr<ProcessingInstruction> node =
1287
0
    NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data);
1288
0
1289
0
  nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
1290
0
    do_QueryInterface(ToSupports(node));
1291
0
  if (ssle) {
1292
0
    ssle->InitStyleLinkElement(false);
1293
0
    ssle->SetEnableUpdates(false);
1294
0
    mPrettyPrintXML = false;
1295
0
  }
1296
0
1297
0
  nsresult rv = AddContentAsLeaf(node);
1298
0
  NS_ENSURE_SUCCESS(rv, rv);
1299
0
  DidAddContent();
1300
0
1301
0
  if (ssle) {
1302
0
    // This is an xml-stylesheet processing instruction... but it might not be
1303
0
    // a CSS one if the type is set to something else.
1304
0
    ssle->SetEnableUpdates(true);
1305
0
    auto updateOrError =
1306
0
      ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
1307
0
    if (updateOrError.isErr()) {
1308
0
      return updateOrError.unwrapErr();
1309
0
    }
1310
0
1311
0
    auto update = updateOrError.unwrap();
1312
0
    if (update.WillNotify()) {
1313
0
      // Successfully started a stylesheet load
1314
0
      if (update.ShouldBlock() && !mRunsToCompletion) {
1315
0
        ++mPendingSheetCount;
1316
0
        mScriptLoader->AddParserBlockingScriptExecutionBlocker();
1317
0
      }
1318
0
      return NS_OK;
1319
0
    }
1320
0
  }
1321
0
1322
0
  // Check whether this is a CSS stylesheet PI.  Make sure the type
1323
0
  // handling here matches
1324
0
  // XMLStylesheetProcessingInstruction::GetStyleSheetInfo.
1325
0
  nsAutoString type;
1326
0
  nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type);
1327
0
  nsAutoString mimeType, notUsed;
1328
0
  nsContentUtils::SplitMimeType(type, mimeType, notUsed);
1329
0
1330
0
  if (mState != eXMLContentSinkState_InProlog ||
1331
0
      !target.EqualsLiteral("xml-stylesheet") ||
1332
0
      mimeType.IsEmpty()                          ||
1333
0
      mimeType.LowerCaseEqualsLiteral("text/css")) {
1334
0
    // Either not a useful stylesheet PI, or a CSS stylesheet PI that
1335
0
    // got handled above by the "ssle" bits.  We're done here.
1336
0
    return DidProcessATokenImpl();
1337
0
  }
1338
0
1339
0
  // If it's not a CSS stylesheet PI...
1340
0
  nsAutoString href, title, media;
1341
0
  bool isAlternate = false;
1342
0
1343
0
  // If there was no href, we can't do anything with this PI
1344
0
  if (!ParsePIData(data, href, title, media, isAlternate)) {
1345
0
      return DidProcessATokenImpl();
1346
0
  }
1347
0
1348
0
  // <?xml-stylesheet?> processing instructions don't have a referrerpolicy
1349
0
  // pseudo-attribute, so we pass in an empty string
1350
0
  rv = MaybeProcessXSLTLink(node, href, isAlternate, title, type, media,
1351
0
                            EmptyString());
1352
0
  return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1353
0
}
1354
1355
/* static */
1356
bool
1357
nsXMLContentSink::ParsePIData(const nsString &aData, nsString &aHref,
1358
                              nsString &aTitle, nsString &aMedia,
1359
                              bool &aIsAlternate)
1360
0
{
1361
0
  // If there was no href, we can't do anything with this PI
1362
0
  if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) {
1363
0
    return false;
1364
0
  }
1365
0
1366
0
  nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle);
1367
0
1368
0
  nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia);
1369
0
1370
0
  nsAutoString alternate;
1371
0
  nsContentUtils::GetPseudoAttributeValue(aData,
1372
0
                                          nsGkAtoms::alternate,
1373
0
                                          alternate);
1374
0
1375
0
  aIsAlternate = alternate.EqualsLiteral("yes");
1376
0
1377
0
  return true;
1378
0
}
1379
1380
NS_IMETHODIMP
1381
nsXMLContentSink::HandleXMLDeclaration(const char16_t *aVersion,
1382
                                       const char16_t *aEncoding,
1383
                                       int32_t aStandalone)
1384
0
{
1385
0
  mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone);
1386
0
1387
0
  return DidProcessATokenImpl();
1388
0
}
1389
1390
NS_IMETHODIMP
1391
nsXMLContentSink::ReportError(const char16_t* aErrorText,
1392
                              const char16_t* aSourceText,
1393
                              nsIScriptError *aError,
1394
                              bool *_retval)
1395
0
{
1396
0
  MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
1397
0
  nsresult rv = NS_OK;
1398
0
1399
0
  // The expat driver should report the error.  We're just cleaning up the mess.
1400
0
  *_retval = true;
1401
0
1402
0
  mPrettyPrintXML = false;
1403
0
1404
0
  mState = eXMLContentSinkState_InProlog;
1405
0
1406
0
  // XXX need to stop scripts here -- hsivonen
1407
0
1408
0
  // stop observing in order to avoid crashing when removing content
1409
0
  mDocument->RemoveObserver(this);
1410
0
  mIsDocumentObserver = false;
1411
0
1412
0
  // Clear the current content
1413
0
  mDocumentChildren.Clear();
1414
0
  while (mDocument->GetLastChild()) {
1415
0
    mDocument->GetLastChild()->Remove();
1416
0
  }
1417
0
  mDocElement = nullptr;
1418
0
1419
0
  // Clear any buffered-up text we have.  It's enough to set the length to 0.
1420
0
  // The buffer itself is allocated when we're created and deleted in our
1421
0
  // destructor, so don't mess with it.
1422
0
  mTextLength = 0;
1423
0
1424
0
  if (mXSLTProcessor) {
1425
0
    // Get rid of the XSLT processor.
1426
0
    mXSLTProcessor->CancelLoads();
1427
0
    mXSLTProcessor = nullptr;
1428
0
  }
1429
0
1430
0
  // release the nodes on stack
1431
0
  mContentStack.Clear();
1432
0
  mNotifyLevel = 0;
1433
0
1434
0
  // return leaving the document empty if we're asked to not add a <parsererror> root node
1435
0
  if (mDocument->SuppressParserErrorElement()) {
1436
0
    return NS_OK;
1437
0
  }
1438
0
1439
0
  // prepare to set <parsererror> as the document root
1440
0
  rv = HandleProcessingInstruction(u"xml-stylesheet",
1441
0
                                   u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");
1442
0
  NS_ENSURE_SUCCESS(rv, rv);
1443
0
1444
0
  const char16_t* noAtts[] = { 0, 0 };
1445
0
1446
0
  NS_NAMED_LITERAL_STRING(errorNs,
1447
0
                          "http://www.mozilla.org/newlayout/xml/parsererror.xml");
1448
0
1449
0
  nsAutoString parsererror(errorNs);
1450
0
  parsererror.Append((char16_t)0xFFFF);
1451
0
  parsererror.AppendLiteral("parsererror");
1452
0
1453
0
  rv = HandleStartElement(parsererror.get(), noAtts, 0, (uint32_t)-1,
1454
0
                          false);
1455
0
  NS_ENSURE_SUCCESS(rv, rv);
1456
0
1457
0
  rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false);
1458
0
  NS_ENSURE_SUCCESS(rv, rv);
1459
0
1460
0
  nsAutoString sourcetext(errorNs);
1461
0
  sourcetext.Append((char16_t)0xFFFF);
1462
0
  sourcetext.AppendLiteral("sourcetext");
1463
0
1464
0
  rv = HandleStartElement(sourcetext.get(), noAtts, 0, (uint32_t)-1,
1465
0
                          false);
1466
0
  NS_ENSURE_SUCCESS(rv, rv);
1467
0
1468
0
  rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false);
1469
0
  NS_ENSURE_SUCCESS(rv, rv);
1470
0
1471
0
  rv = HandleEndElement(sourcetext.get(), false);
1472
0
  NS_ENSURE_SUCCESS(rv, rv);
1473
0
1474
0
  rv = HandleEndElement(parsererror.get(), false);
1475
0
  NS_ENSURE_SUCCESS(rv, rv);
1476
0
1477
0
  FlushTags();
1478
0
1479
0
  return NS_OK;
1480
0
}
1481
1482
nsresult
1483
nsXMLContentSink::AddAttributes(const char16_t** aAtts,
1484
                                Element* aContent)
1485
0
{
1486
0
  // Add tag attributes to the content attributes
1487
0
  RefPtr<nsAtom> prefix, localName;
1488
0
  while (*aAtts) {
1489
0
    int32_t nameSpaceID;
1490
0
    nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
1491
0
                                   getter_AddRefs(localName), &nameSpaceID);
1492
0
1493
0
    // Add attribute to content
1494
0
    aContent->SetAttr(nameSpaceID, localName, prefix,
1495
0
                      nsDependentString(aAtts[1]), false);
1496
0
    aAtts += 2;
1497
0
  }
1498
0
1499
0
  return NS_OK;
1500
0
}
1501
1502
0
#define NS_ACCUMULATION_BUFFER_SIZE 4096
1503
1504
nsresult
1505
nsXMLContentSink::AddText(const char16_t* aText,
1506
                          int32_t aLength)
1507
0
{
1508
0
  // Copy data from string into our buffer; flush buffer when it fills up.
1509
0
  int32_t offset = 0;
1510
0
  while (0 != aLength) {
1511
0
    int32_t amount = NS_ACCUMULATION_BUFFER_SIZE - mTextLength;
1512
0
    if (0 == amount) {
1513
0
      nsresult rv = FlushText(false);
1514
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1515
0
        return rv;
1516
0
      }
1517
0
      MOZ_ASSERT(mTextLength == 0);
1518
0
      amount = NS_ACCUMULATION_BUFFER_SIZE;
1519
0
    }
1520
0
1521
0
    if (amount > aLength) {
1522
0
      amount = aLength;
1523
0
    }
1524
0
    memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount);
1525
0
    mTextLength += amount;
1526
0
    offset += amount;
1527
0
    aLength -= amount;
1528
0
  }
1529
0
1530
0
  return NS_OK;
1531
0
}
1532
1533
void
1534
nsXMLContentSink::FlushPendingNotifications(FlushType aType)
1535
0
{
1536
0
  // Only flush tags if we're not doing the notification ourselves
1537
0
  // (since we aren't reentrant)
1538
0
  if (!mInNotification) {
1539
0
    if (mIsDocumentObserver) {
1540
0
      // Only flush if we're still a document observer (so that our child
1541
0
      // counts should be correct).
1542
0
      if (aType >= FlushType::ContentAndNotify) {
1543
0
        FlushTags();
1544
0
      }
1545
0
      else {
1546
0
        FlushText(false);
1547
0
      }
1548
0
    }
1549
0
    if (aType >= FlushType::EnsurePresShellInitAndFrames) {
1550
0
      // Make sure that layout has started so that the reflow flush
1551
0
      // will actually happen.
1552
0
      MaybeStartLayout(true);
1553
0
    }
1554
0
  }
1555
0
}
1556
1557
/**
1558
 * NOTE!! Forked from SinkContext. Please keep in sync.
1559
 *
1560
 * Flush all elements that have been seen so far such that
1561
 * they are visible in the tree. Specifically, make sure
1562
 * that they are all added to their respective parents.
1563
 * Also, do notification at the top for all content that
1564
 * has been newly added so that the frame tree is complete.
1565
 */
1566
nsresult
1567
nsXMLContentSink::FlushTags()
1568
0
{
1569
0
  mDeferredFlushTags = false;
1570
0
  bool oldBeganUpdate = mBeganUpdate;
1571
0
  uint32_t oldUpdates = mUpdatesInNotification;
1572
0
1573
0
  mUpdatesInNotification = 0;
1574
0
  ++mInNotification;
1575
0
  {
1576
0
    // Scope so we call EndUpdate before we decrease mInNotification
1577
0
    mozAutoDocUpdate updateBatch(mDocument, true);
1578
0
    mBeganUpdate = true;
1579
0
1580
0
    // Don't release last text node in case we need to add to it again
1581
0
    FlushText(false);
1582
0
1583
0
    // Start from the base of the stack (growing downward) and do
1584
0
    // a notification from the node that is closest to the root of
1585
0
    // tree for any content that has been added.
1586
0
1587
0
    int32_t stackPos;
1588
0
    int32_t stackLen = mContentStack.Length();
1589
0
    bool flushed = false;
1590
0
    uint32_t childCount;
1591
0
    nsIContent* content;
1592
0
1593
0
    for (stackPos = 0; stackPos < stackLen; ++stackPos) {
1594
0
      content = mContentStack[stackPos].mContent;
1595
0
      childCount = content->GetChildCount();
1596
0
1597
0
      if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) {
1598
0
        NotifyAppend(content, mContentStack[stackPos].mNumFlushed);
1599
0
        flushed = true;
1600
0
      }
1601
0
1602
0
      mContentStack[stackPos].mNumFlushed = childCount;
1603
0
    }
1604
0
    mNotifyLevel = stackLen - 1;
1605
0
  }
1606
0
  --mInNotification;
1607
0
1608
0
  if (mUpdatesInNotification > 1) {
1609
0
    UpdateChildCounts();
1610
0
  }
1611
0
1612
0
  mUpdatesInNotification = oldUpdates;
1613
0
  mBeganUpdate = oldBeganUpdate;
1614
0
1615
0
  return NS_OK;
1616
0
}
1617
1618
/**
1619
 * NOTE!! Forked from SinkContext. Please keep in sync.
1620
 */
1621
void
1622
nsXMLContentSink::UpdateChildCounts()
1623
0
{
1624
0
  // Start from the top of the stack (growing upwards) and see if any
1625
0
  // new content has been appended. If so, we recognize that reflows
1626
0
  // have been generated for it and we should make sure that no
1627
0
  // further reflows occur.  Note that we have to include stackPos == 0
1628
0
  // to properly notify on kids of <html>.
1629
0
  int32_t stackLen = mContentStack.Length();
1630
0
  int32_t stackPos = stackLen - 1;
1631
0
  while (stackPos >= 0) {
1632
0
    StackNode & node = mContentStack[stackPos];
1633
0
    node.mNumFlushed = node.mContent->GetChildCount();
1634
0
1635
0
    stackPos--;
1636
0
  }
1637
0
  mNotifyLevel = stackLen - 1;
1638
0
}
1639
1640
bool
1641
nsXMLContentSink::IsMonolithicContainer(mozilla::dom::NodeInfo* aNodeInfo)
1642
0
{
1643
0
  return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
1644
0
          (aNodeInfo->NameAtom() == nsGkAtoms::tr ||
1645
0
           aNodeInfo->NameAtom() == nsGkAtoms::select ||
1646
0
           aNodeInfo->NameAtom() == nsGkAtoms::object)) ||
1647
0
          (aNodeInfo->NamespaceID() == kNameSpaceID_MathML &&
1648
0
          (aNodeInfo->NameAtom() == nsGkAtoms::math))
1649
0
          );
1650
0
}
1651
1652
void
1653
nsXMLContentSink::ContinueInterruptedParsingIfEnabled()
1654
0
{
1655
0
  if (mParser && mParser->IsParserEnabled()) {
1656
0
    GetParser()->ContinueInterruptedParsing();
1657
0
  }
1658
0
}
1659
1660
void
1661
nsXMLContentSink::ContinueInterruptedParsingAsync()
1662
0
{
1663
0
  nsCOMPtr<nsIRunnable> ev =
1664
0
    NewRunnableMethod("nsXMLContentSink::ContinueInterruptedParsingIfEnabled",
1665
0
                      this,
1666
0
                      &nsXMLContentSink::ContinueInterruptedParsingIfEnabled);
1667
0
1668
0
  mDocument->Dispatch(mozilla::TaskCategory::Other, ev.forget());
1669
0
}
1670
1671
nsIParser*
1672
nsXMLContentSink::GetParser()
1673
0
{
1674
0
  return static_cast<nsIParser*>(mParser.get());
1675
0
}