1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsHtml5StringParser.h"
6 #include "nsHtml5DependentUTF16Buffer.h"
7 #include "nsHtml5Tokenizer.h"
8 #include "nsHtml5TreeBuilder.h"
9 #include "nsHtml5TreeOpExecutor.h"
10 #include "nsIContent.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/DocumentFragment.h"
14 using mozilla::dom::Document
;
16 NS_IMPL_ISUPPORTS0(nsHtml5StringParser
)
18 nsHtml5StringParser::nsHtml5StringParser()
19 : mBuilder(new nsHtml5OplessBuilder()),
20 mTreeBuilder(new nsHtml5TreeBuilder(mBuilder
)),
21 mTokenizer(new nsHtml5Tokenizer(mTreeBuilder
.get(), false)) {
22 mTokenizer
->setInterner(&mAtomTable
);
23 mTokenizer
->setKeepBuffer(true);
24 mTreeBuilder
->setKeepBuffer(true);
27 nsHtml5StringParser::~nsHtml5StringParser() { ClearCaches(); }
29 nsresult
nsHtml5StringParser::ParseFragment(
30 const nsAString
& aSourceBuffer
, nsIContent
* aTargetNode
,
31 nsAtom
* aContextLocalName
, int32_t aContextNamespace
, bool aQuirks
,
32 bool aPreventScriptExecution
, bool aAllowDeclarativeShadowRoots
) {
33 NS_ENSURE_TRUE(aSourceBuffer
.Length() <= INT32_MAX
, NS_ERROR_OUT_OF_MEMORY
);
35 Document
* doc
= aTargetNode
->OwnerDoc();
36 nsIURI
* uri
= doc
->GetDocumentURI();
37 NS_ENSURE_TRUE(uri
, NS_ERROR_NOT_AVAILABLE
);
39 mTreeBuilder
->setFragmentContext(aContextLocalName
, aContextNamespace
,
40 aTargetNode
, aQuirks
);
43 if (!aPreventScriptExecution
) {
44 NS_ASSERTION(!aTargetNode
->IsInUncomposedDoc(),
45 "If script execution isn't prevented, "
46 "the target node must not be in doc.");
48 aTargetNode
->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE
,
49 "If script execution isn't prevented, must parse to DOM fragment.");
53 mTreeBuilder
->SetPreventScriptExecution(aPreventScriptExecution
);
55 return Tokenize(aSourceBuffer
, doc
, true, aAllowDeclarativeShadowRoots
);
58 nsresult
nsHtml5StringParser::ParseDocument(
59 const nsAString
& aSourceBuffer
, Document
* aTargetDoc
,
60 bool aScriptingEnabledForNoscriptParsing
) {
61 MOZ_ASSERT(!aTargetDoc
->GetFirstChild());
63 NS_ENSURE_TRUE(aSourceBuffer
.Length() <= INT32_MAX
, NS_ERROR_OUT_OF_MEMORY
);
65 mTreeBuilder
->setFragmentContext(nullptr, kNameSpaceID_None
, nullptr, false);
67 mTreeBuilder
->SetPreventScriptExecution(true);
69 return Tokenize(aSourceBuffer
, aTargetDoc
,
70 aScriptingEnabledForNoscriptParsing
,
71 aTargetDoc
->AllowsDeclarativeShadowRoots());
74 void nsHtml5StringParser::ClearCaches() {
75 mTokenizer
->dropBufferIfLongerThan(0);
76 mTreeBuilder
->dropBufferIfLongerThan(0);
78 mCacheClearer
->Disconnect();
79 mCacheClearer
= nullptr;
83 void nsHtml5StringParser::TryCache() {
84 const int32_t kMaxBuffer
= 1024 * 1024;
85 bool didDrop
= mTokenizer
->dropBufferIfLongerThan(kMaxBuffer
);
86 didDrop
|= mTreeBuilder
->dropBufferIfLongerThan(kMaxBuffer
);
92 mCacheClearer
= new CacheClearer(this);
93 nsCOMPtr
<nsIRunnable
> runnable
= mCacheClearer
.get();
94 NS_DispatchToMainThreadQueue(runnable
.forget(),
95 mozilla::EventQueuePriority::Idle
);
99 nsresult
nsHtml5StringParser::Tokenize(const nsAString
& aSourceBuffer
,
101 bool aScriptingEnabledForNoscriptParsing
,
102 bool aDeclarativeShadowRootsAllowed
) {
103 nsIURI
* uri
= aDocument
->GetDocumentURI();
105 mBuilder
->Init(aDocument
, uri
, nullptr, nullptr);
107 mBuilder
->SetParser(this);
108 mBuilder
->SetNodeInfoManager(aDocument
->NodeInfoManager());
110 // Mark the parser as *not* broken by passing NS_OK
111 nsresult rv
= mBuilder
->MarkAsBroken(NS_OK
);
113 mTreeBuilder
->setScriptingEnabled(aScriptingEnabledForNoscriptParsing
);
114 mTreeBuilder
->setIsSrcdocDocument(aDocument
->IsSrcdocDocument());
115 mTreeBuilder
->setAllowDeclarativeShadowRoots(aDeclarativeShadowRootsAllowed
);
118 if (!aSourceBuffer
.IsEmpty()) {
119 bool lastWasCR
= false;
120 nsHtml5DependentUTF16Buffer
buffer(aSourceBuffer
);
121 while (buffer
.hasMore()) {
122 buffer
.adjust(lastWasCR
);
124 if (buffer
.hasMore()) {
125 if (!mTokenizer
->EnsureBufferSpace(buffer
.getLength())) {
126 rv
= mBuilder
->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY
);
129 lastWasCR
= mTokenizer
->tokenizeBuffer(&buffer
);
130 if (NS_FAILED(rv
= mBuilder
->IsBroken())) {
136 if (NS_SUCCEEDED(rv
)) {