Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/parser/htmlparser/./../../../parser/htmlparser/nsParser.cpp
Warning:line 327, column 20
Although the value stored to 'result' is used in the enclosing expression, the value is never actually read from 'result'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_parser_htmlparser0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/parser/htmlparser -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/parser/htmlparser -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/parser/htmlparser -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/parser/htmlparser -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/security/rlbox -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_parser_htmlparser0.cpp
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/. */
4
5#include "nsAtom.h"
6#include "nsParser.h"
7#include "nsString.h"
8#include "nsCRT.h"
9#include "nsScanner.h"
10#include "plstr.h"
11#include "nsIChannel.h"
12#include "nsIInputStream.h"
13#include "prenv.h"
14#include "prlock.h"
15#include "prcvar.h"
16#include "nsReadableUtils.h"
17#include "nsCOMPtr.h"
18#include "nsExpatDriver.h"
19#include "nsIFragmentContentSink.h"
20#include "nsStreamUtils.h"
21#include "nsXPCOMCIDInternal.h"
22#include "nsMimeTypes.h"
23#include "nsCharsetSource.h"
24#include "nsThreadUtils.h"
25
26#include "mozilla/CondVar.h"
27#include "mozilla/dom/ScriptLoader.h"
28#include "mozilla/Encoding.h"
29#include "mozilla/Mutex.h"
30
31using namespace mozilla;
32
33#define NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001 0x00000001
34#define NS_PARSER_FLAG_CAN_TOKENIZE0x00000002 0x00000002
35
36//-------------- Begin ParseContinue Event Definition ------------------------
37/*
38The parser can be explicitly interrupted by passing a return value of
39NS_ERROR_HTMLPARSER_INTERRUPTED from BuildModel on the DTD. This will cause
40the parser to stop processing and allow the application to return to the event
41loop. The data which was left at the time of interruption will be processed
42the next time OnDataAvailable is called. If the parser has received its final
43chunk of data then OnDataAvailable will no longer be called by the networking
44module, so the parser will schedule a nsParserContinueEvent which will call
45the parser to process the remaining data after returning to the event loop.
46If the parser is interrupted while processing the remaining data it will
47schedule another ParseContinueEvent. The processing of data followed by
48scheduling of the continue events will proceed until either:
49
50 1) All of the remaining data can be processed without interrupting
51 2) The parser has been cancelled.
52
53The nsContentSink records the time when the chunk has started processing and
54will return NS_ERROR_HTMLPARSER_INTERRUPTED if the token processing time has
55exceeded a threshold called max tokenizing processing time. This allows the
56content sink to limit how much data is processed in a single chunk which in
57turn gates how much time is spent away from the event loop. Processing smaller
58chunks of data also reduces the time spent in subsequent reflows.
59
60This capability is most apparent when loading large documents. If the maximum
61token processing time is set small enough the application will remain
62responsive during document load.
63
64A side-effect of this capability is that document load is not complete when
65the last chunk of data is passed to OnDataAvailable since the parser may have
66been interrupted when the last chunk of data arrived. The document is complete
67when all of the document has been tokenized and there aren't any pending
68nsParserContinueEvents. This can cause problems if the application assumes
69that it can monitor the load requests to determine when the document load has
70been completed. This is what happens in Mozilla. The document is considered
71completely loaded when all of the load requests have been satisfied.
72
73Currently the parser is ignores requests to be interrupted during the
74processing of script. This is because a document.write followed by JavaScript
75calls to manipulate the DOM may fail if the parser was interrupted during the
76document.write.
77
78For more details @see bugzilla bug 76722
79*/
80
81class nsParserContinueEvent : public Runnable {
82 public:
83 RefPtr<nsParser> mParser;
84
85 explicit nsParserContinueEvent(nsParser* aParser)
86 : mozilla::Runnable("nsParserContinueEvent"), mParser(aParser) {}
87
88 NS_IMETHODvirtual nsresult Run() override {
89 mParser->HandleParserContinueEvent(this);
90 return NS_OK;
91 }
92};
93
94//-------------- End ParseContinue Event Definition ------------------------
95
96/**
97 * default constructor
98 */
99nsParser::nsParser() : mCharset(WINDOWS_1252_ENCODING) { Initialize(); }
100
101nsParser::~nsParser() { Cleanup(); }
102
103void nsParser::Initialize() {
104 mContinueEvent = nullptr;
105 mCharsetSource = kCharsetUninitialized;
106 mCharset = WINDOWS_1252_ENCODING;
107 mInternalState = NS_OK;
108 mStreamStatus = NS_OK;
109 mCommand = eViewNormal;
110 mBlocked = 0;
111 mFlags = NS_PARSER_FLAG_CAN_TOKENIZE0x00000002;
112
113 mProcessingNetworkData = false;
114 mOnStopPending = false;
115}
116
117void nsParser::Cleanup() {
118 // It should not be possible for this flag to be set when we are getting
119 // destroyed since this flag implies a pending nsParserContinueEvent, which
120 // has an owning reference to |this|.
121 NS_ASSERTION(!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT), "bad")do { if (!(!(mFlags & 0x00000001))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "bad", "!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT)"
, "./../../../parser/htmlparser/nsParser.cpp", 121); MOZ_PretendNoReturn
(); } } while (0)
;
122}
123
124NS_IMPL_CYCLE_COLLECTION_CLASS(nsParser)nsParser::cycleCollection nsParser::_cycleCollectorGlobal;
125
126NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsParser)void nsParser::cycleCollection::Unlink(void* p) { nsParser* tmp
= DowncastCCParticipant<nsParser>(p);
127 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExpatDriver)ImplCycleCollectionUnlink(tmp->mExpatDriver);
128 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSink)ImplCycleCollectionUnlink(tmp->mSink);
129 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCEtmp->ClearWeakReferences();
130NS_IMPL_CYCLE_COLLECTION_UNLINK_END(void)tmp; }
131
132NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsParser)nsresult nsParser::cycleCollection::TraverseNative( void* p, nsCycleCollectionTraversalCallback
& cb) { nsParser* tmp = DowncastCCParticipant<nsParser
>(p); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "nsParser"
);
133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExpatDriver)ImplCycleCollectionTraverse(cb, tmp->mExpatDriver, "mExpatDriver"
, 0);
134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSink)ImplCycleCollectionTraverse(cb, tmp->mSink, "mSink", 0);
135NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END(void)tmp; return NS_OK; }
136
137NS_IMPL_CYCLE_COLLECTING_ADDREF(nsParser)MozExternalRefCountType nsParser::AddRef(void) { static_assert
(!std::is_destructible_v<nsParser>, "Reference-counted class "
"nsParser" " should not have a public destructor. " "Make this class's destructor non-public"
); do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "./../../../parser/htmlparser/nsParser.cpp"
, 137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 137
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("nsParser" " not thread-safe"
); nsISupports* base = nsParser::cycleCollection::Upcast(this
); nsrefcnt count = mRefCnt.incr(base); NS_LogAddRef((this), (
count), ("nsParser"), (uint32_t)(sizeof(*this))); return count
; }
138NS_IMPL_CYCLE_COLLECTING_RELEASE(nsParser)MozExternalRefCountType nsParser::Release(void) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(int32_t
(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "./../../../parser/htmlparser/nsParser.cpp"
, 138); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 138
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("nsParser" " not thread-safe"
); nsISupports* base = nsParser::cycleCollection::Upcast(this
); nsrefcnt count = mRefCnt.decr(base); if (count == 0) { NS_CycleCollectableHasRefCntZero
(); } NS_LogRelease((this), (count), ("nsParser")); return count
; } void nsParser::DeleteCycleCollectable(void) { delete (this
); }
139NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsParser)nsresult nsParser::QueryInterface(const nsIID& aIID, void
** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(
NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "./../../../parser/htmlparser/nsParser.cpp"
, 139); MOZ_PretendNoReturn(); } } while (0); nsISupports* foundInterface
; if (TopThreeWordsEquals( aIID, (nsXPCOMCycleCollectionParticipant
::kIID), (nsCycleCollectionISupports::kIID)) && (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID)) || LowWordEquals
(aIID, (nsCycleCollectionISupports::kIID)))) { if (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID))) { *aInstancePtr
= nsParser::cycleCollection::GetParticipant(); return NS_OK;
} if (LowWordEquals(aIID, (nsCycleCollectionISupports::kIID)
)) { *aInstancePtr = nsParser::cycleCollection::Upcast(this);
return NS_OK; } foundInterface = nullptr; } else
140 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIStreamListener>)) foundInterface
= static_cast<nsIStreamListener*>(this); else
141 NS_INTERFACE_MAP_ENTRY(nsIParser)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIParser>)) foundInterface = static_cast
<nsIParser*>(this); else
142 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIRequestObserver>)) foundInterface
= static_cast<nsIRequestObserver*>(this); else
143 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupportsWeakReference>)) foundInterface
= static_cast<nsISupportsWeakReference*>(this); else
144 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParser)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupports>)) foundInterface = static_cast
<nsISupports*>(static_cast<nsIParser*>(this)); else
145NS_INTERFACE_MAP_ENDfoundInterface = 0; nsresult status; if (!foundInterface) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIID.Equals((nsISupports::kIID)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIID.Equals((nsISupports::kIID
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!aIID.Equals((nsISupports::kIID))", "./../../../parser/htmlparser/nsParser.cpp"
, 145); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIID.Equals((nsISupports::kIID))"
")"); do { MOZ_CrashSequence(__null, 145); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); status = NS_NOINTERFACE
; } else { (foundInterface)->AddRef(); status = NS_OK; } *
aInstancePtr = foundInterface; return status; }
146
147// The parser continue event is posted only if
148// all of the data to parse has been passed to ::OnDataAvailable
149// and the parser has been interrupted by the content sink
150// because the processing of tokens took too long.
151
152nsresult nsParser::PostContinueEvent() {
153 if (!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001)) {
154 // If this flag isn't set, then there shouldn't be a live continue event!
155 NS_ASSERTION(!mContinueEvent, "bad")do { if (!(!mContinueEvent)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "bad", "!mContinueEvent", "./../../../parser/htmlparser/nsParser.cpp"
, 155); MOZ_PretendNoReturn(); } } while (0)
;
156
157 // This creates a reference cycle between this and the event that is
158 // broken when the event fires.
159 nsCOMPtr<nsIRunnable> event = new nsParserContinueEvent(this);
160 if (NS_FAILED(NS_DispatchToCurrentThread(event))((bool)(__builtin_expect(!!(NS_FAILED_impl(NS_DispatchToCurrentThread
(event))), 0)))
) {
161 NS_WARNING("failed to dispatch parser continuation event")NS_DebugBreak(NS_DEBUG_WARNING, "failed to dispatch parser continuation event"
, nullptr, "./../../../parser/htmlparser/nsParser.cpp", 161)
;
162 } else {
163 mFlags |= NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001;
164 mContinueEvent = event;
165 }
166 }
167 return NS_OK;
168}
169
170NS_IMETHODIMP_(void)void
171nsParser::GetCommand(nsCString& aCommand) { aCommand = mCommandStr; }
172
173/**
174 * Call this method once you've created a parser, and want to instruct it
175 * about the command which caused the parser to be constructed. For example,
176 * this allows us to select a DTD which can do, say, view-source.
177 *
178 * @param aCommand the command string to set
179 */
180NS_IMETHODIMP_(void)void
181nsParser::SetCommand(const char* aCommand) {
182 mCommandStr.Assign(aCommand);
183 if (mCommandStr.EqualsLiteral("view-source")) {
184 mCommand = eViewSource;
185 } else if (mCommandStr.EqualsLiteral("view-fragment")) {
186 mCommand = eViewFragment;
187 } else {
188 mCommand = eViewNormal;
189 }
190}
191
192/**
193 * Call this method once you've created a parser, and want to instruct it
194 * about the command which caused the parser to be constructed. For example,
195 * this allows us to select a DTD which can do, say, view-source.
196 *
197 * @param aParserCommand the command to set
198 */
199NS_IMETHODIMP_(void)void
200nsParser::SetCommand(eParserCommands aParserCommand) {
201 mCommand = aParserCommand;
202}
203
204/**
205 * Call this method once you've created a parser, and want to instruct it
206 * about what charset to load
207 *
208 * @param aCharset- the charset of a document
209 * @param aCharsetSource- the source of the charset
210 */
211void nsParser::SetDocumentCharset(NotNull<const Encoding*> aCharset,
212 int32_t aCharsetSource,
213 bool aForceAutoDetection) {
214 mCharset = aCharset;
215 mCharsetSource = aCharsetSource;
216 if (mParserContext) {
217 mParserContext->mScanner.SetDocumentCharset(aCharset, aCharsetSource);
218 }
219}
220
221void nsParser::SetSinkCharset(NotNull<const Encoding*> aCharset) {
222 if (mSink) {
223 mSink->SetDocumentCharset(aCharset);
224 }
225}
226
227/**
228 * This method gets called in order to set the content
229 * sink for this parser to dump nodes to.
230 *
231 * @param nsIContentSink interface for node receiver
232 */
233NS_IMETHODIMP_(void)void
234nsParser::SetContentSink(nsIContentSink* aSink) {
235 MOZ_ASSERT(aSink, "sink cannot be null!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSink)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(aSink))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aSink" " (" "sink cannot be null!"
")", "./../../../parser/htmlparser/nsParser.cpp", 235); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSink" ") (" "sink cannot be null!" ")"); do
{ MOZ_CrashSequence(__null, 235); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
236 mSink = aSink;
237
238 if (mSink) {
239 mSink->SetParser(this);
240 }
241}
242
243/**
244 * retrieve the sink set into the parser
245 * @return current sink
246 */
247NS_IMETHODIMP_(nsIContentSink*)nsIContentSink*
248nsParser::GetContentSink() { return mSink; }
249
250////////////////////////////////////////////////////////////////////////
251
252/**
253 * This gets called just prior to the model actually
254 * being constructed. It's important to make this the
255 * last thing that happens right before parsing, so we
256 * can delay until the last moment the resolution of
257 * which DTD to use (unless of course we're assigned one).
258 */
259nsresult nsParser::WillBuildModel() {
260 if (!mParserContext) return NS_ERROR_HTMLPARSER_INVALIDPARSERCONTEXT;
261
262 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
263 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
264 // to avoid introducing unintentional changes to behavior.
265 return mInternalState;
266 }
267
268 if (eUnknownDetect != mParserContext->mAutoDetectStatus) return NS_OK;
269
270 if (eDTDMode_autodetect == mParserContext->mDTDMode) {
271 mParserContext->mDTDMode = eDTDMode_full_standards;
272 mParserContext->mDocType = eXML;
273 } // else XML fragment with nested parser context
274
275 // We always find a DTD.
276 mParserContext->mAutoDetectStatus = ePrimaryDetect;
277
278 // Quick check for view source.
279 MOZ_ASSERT(mParserContext->mParserCommand != eViewSource,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mParserContext->mParserCommand != eViewSource)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mParserContext->mParserCommand != eViewSource))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("mParserContext->mParserCommand != eViewSource"
" (" "The old parser is not supposed to be used for View Source "
"anymore." ")", "./../../../parser/htmlparser/nsParser.cpp",
281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mParserContext->mParserCommand != eViewSource"
") (" "The old parser is not supposed to be used for View Source "
"anymore." ")"); do { MOZ_CrashSequence(__null, 281); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
280 "The old parser is not supposed to be used for View Source "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mParserContext->mParserCommand != eViewSource)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mParserContext->mParserCommand != eViewSource))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("mParserContext->mParserCommand != eViewSource"
" (" "The old parser is not supposed to be used for View Source "
"anymore." ")", "./../../../parser/htmlparser/nsParser.cpp",
281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mParserContext->mParserCommand != eViewSource"
") (" "The old parser is not supposed to be used for View Source "
"anymore." ")"); do { MOZ_CrashSequence(__null, 281); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
281 "anymore.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mParserContext->mParserCommand != eViewSource)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mParserContext->mParserCommand != eViewSource))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("mParserContext->mParserCommand != eViewSource"
" (" "The old parser is not supposed to be used for View Source "
"anymore." ")", "./../../../parser/htmlparser/nsParser.cpp",
281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mParserContext->mParserCommand != eViewSource"
") (" "The old parser is not supposed to be used for View Source "
"anymore." ")"); do { MOZ_CrashSequence(__null, 281); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
282
283 RefPtr<nsExpatDriver> expat = new nsExpatDriver();
284 nsresult rv = expat->Initialize(mParserContext->mScanner.GetURI(), mSink);
285 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../parser/htmlparser/nsParser.cpp"
, 285); return rv; } } while (false)
;
286
287 mExpatDriver = expat.forget();
288
289 return mSink->WillBuildModel();
290}
291
292/**
293 * This gets called when the parser is done with its input.
294 */
295void nsParser::DidBuildModel() {
296 if (IsComplete() && mParserContext) {
297 // Let sink know if we're about to end load because we've been terminated.
298 // In that case we don't want it to run deferred scripts.
299 bool terminated = mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING;
300 if (mExpatDriver && mSink) {
301 mExpatDriver->DidBuildModel();
302 mSink->DidBuildModel(terminated);
303 }
304
305 // Ref. to bug 61462.
306 mParserContext->mRequest = nullptr;
307 }
308}
309
310/**
311 * Call this when you want to *force* the parser to terminate the
312 * parsing process altogether. This is binary -- so once you terminate
313 * you can't resume without restarting altogether.
314 */
315NS_IMETHODIMPnsresult
316nsParser::Terminate(void) {
317 // We should only call DidBuildModel once, so don't do anything if this is
318 // the second time that Terminate has been called.
319 if (mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING) {
320 return NS_OK;
321 }
322
323 nsresult result = NS_OK;
324 // XXX - [ until we figure out a way to break parser-sink circularity ]
325 // Hack - Hold a reference until we are completely done...
326 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
327 mInternalState = result = NS_ERROR_HTMLPARSER_STOPPARSING;
Although the value stored to 'result' is used in the enclosing expression, the value is never actually read from 'result'
328
329 // @see bug 108049
330 // If NS_PARSER_FLAG_PENDING_CONTINUE_EVENT is set then reset it so
331 // DidBuildModel will call DidBuildModel on the DTD. Note: The IsComplete()
332 // call inside of DidBuildModel looks at the pendingContinueEvents flag.
333 if (mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001) {
334 NS_ASSERTION(mContinueEvent, "mContinueEvent is null")do { if (!(mContinueEvent)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "mContinueEvent is null", "mContinueEvent", "./../../../parser/htmlparser/nsParser.cpp"
, 334); MOZ_PretendNoReturn(); } } while (0)
;
335 // Revoke the pending continue parsing event
336 mContinueEvent = nullptr;
337 mFlags &= ~NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001;
338 }
339
340 if (mExpatDriver) {
341 mExpatDriver->Terminate();
342 DidBuildModel();
343 } else if (mSink) {
344 // We have no parser context or no DTD yet (so we got terminated before we
345 // got any data). Manually break the reference cycle with the sink.
346 result = mSink->DidBuildModel(true);
347 NS_ENSURE_SUCCESS(result, result)do { nsresult __rv = result; if (((bool)(__builtin_expect(!!(
NS_FAILED_impl(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "result", "result", static_cast<
uint32_t>(__rv), name ? " (" : "", name ? name : "", name ?
")" : ""); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr
, "./../../../parser/htmlparser/nsParser.cpp", 347); return result
; } } while (false)
;
348 }
349
350 return NS_OK;
351}
352
353NS_IMETHODIMPnsresult
354nsParser::ContinueInterruptedParsing() {
355 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
356 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
357 // to avoid introducing unintentional changes to behavior.
358 return mInternalState;
359 }
360
361 if (mBlocked) {
362 // Whatever blocked the parser is responsible for ensuring
363 // that we don't stall.
364 return NS_OK;
365 }
366
367 // If there are scripts executing, this is probably due to a synchronous
368 // XMLHttpRequest, see bug 460706 and 1938290.
369 if (IsScriptExecuting()) {
370 ContinueParsingDocumentAfterCurrentScript();
371 return NS_OK;
372 }
373
374 if (mProcessingNetworkData) {
375 // The call already on stack is responsible for ensuring that we
376 // don't stall.
377 return NS_OK;
378 }
379
380 // If the stream has already finished, there's a good chance
381 // that we might start closing things down when the parser
382 // is reenabled. To make sure that we're not deleted across
383 // the reenabling process, hold a reference to ourselves.
384 nsresult result = NS_OK;
385 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
386 nsCOMPtr<nsIContentSink> sinkDeathGrip(mSink);
387
388 bool isFinalChunk =
389 mParserContext && mParserContext->mStreamListenerState == eOnStop;
390
391 mProcessingNetworkData = true;
392 if (sinkDeathGrip) {
393 sinkDeathGrip->WillParse();
394 }
395 result = ResumeParse(true, isFinalChunk); // Ref. bug 57999
396
397 // Bug 1899786 added a flag for deferring `eOnStop`, so `isFinalChunk`
398 // above may be false. Let's run the logic from bug 1899786:
399 // Check if someone spun the event loop while we were parsing (XML
400 // script...) If so, and OnStop was called during the spin, process it
401 // now.
402 if ((result == NS_OK) && mOnStopPending) {
403 mOnStopPending = false;
404 mParserContext->mStreamListenerState = eOnStop;
405 mParserContext->mScanner.SetIncremental(false);
406
407 if (sinkDeathGrip) {
408 sinkDeathGrip->WillParse();
409 }
410 result = ResumeParse(true, true);
411 }
412 mProcessingNetworkData = false;
413
414 if (result != NS_OK) {
415 result = mInternalState;
416 }
417
418 return result;
419}
420
421/**
422 * Stops parsing temporarily. That is, it will prevent the
423 * parser from building up content model while scripts
424 * are being loaded (either an external script from a web
425 * page, or any number of extension content scripts).
426 */
427NS_IMETHODIMP_(void)void
428nsParser::BlockParser() { mBlocked++; }
429
430/**
431 * Open up the parser for tokenization, building up content
432 * model..etc. However, this method does not resume parsing
433 * automatically. It's the callers' responsibility to restart
434 * the parsing engine.
435 */
436NS_IMETHODIMP_(void)void
437nsParser::UnblockParser() {
438 MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mBlocked > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mBlocked > 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("mBlocked > 0"
, "./../../../parser/htmlparser/nsParser.cpp", 438); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "mBlocked > 0" ")"); do { MOZ_CrashSequence
(__null, 438); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
439 if (MOZ_LIKELY(mBlocked > 0)(__builtin_expect(!!(mBlocked > 0), 1))) {
440 mBlocked--;
441 }
442}
443
444NS_IMETHODIMP_(void)void
445nsParser::ContinueInterruptedParsingAsync() {
446 MOZ_ASSERT(mSink)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mSink)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(mSink))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("mSink", "./../../../parser/htmlparser/nsParser.cpp"
, 446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mSink" ")");
do { MOZ_CrashSequence(__null, 446); __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
447 if (MOZ_LIKELY(mSink)(__builtin_expect(!!(mSink), 1))) {
448 mSink->ContinueInterruptedParsingAsync();
449 }
450}
451
452/**
453 * Call this to query whether the parser is enabled or not.
454 */
455NS_IMETHODIMP_(bool)bool
456nsParser::IsParserEnabled() { return !mBlocked; }
457
458/**
459 * Call this to query whether the parser thinks it's done with parsing.
460 */
461NS_IMETHODIMP_(bool)bool
462nsParser::IsComplete() {
463 return !(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001);
464}
465
466void nsParser::HandleParserContinueEvent(nsParserContinueEvent* ev) {
467 // Ignore any revoked continue events...
468 if (mContinueEvent != ev) return;
469
470 mFlags &= ~NS_PARSER_FLAG_PENDING_CONTINUE_EVENT0x00000001;
471 mContinueEvent = nullptr;
472
473 ContinueInterruptedParsing();
474}
475
476bool nsParser::IsInsertionPointDefined() { return false; }
477
478void nsParser::IncrementScriptNestingLevel() {}
479
480void nsParser::DecrementScriptNestingLevel() {}
481
482bool nsParser::HasNonzeroScriptNestingLevel() const { return false; }
483
484bool nsParser::IsScriptCreated() { return false; }
485
486bool nsParser::IsAboutBlankMode() { return false; }
487
488/**
489 * This is the main controlling routine in the parsing process.
490 * Note that it may get called multiple times for the same scanner,
491 * since this is a pushed based system, and all the tokens may
492 * not have been consumed by the scanner during a given invocation
493 * of this method.
494 */
495NS_IMETHODIMPnsresult
496nsParser::Parse(nsIURI* aURL) {
497 MOZ_ASSERT(aURL, "Error: Null URL given")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aURL)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aURL))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aURL" " (" "Error: Null URL given"
")", "./../../../parser/htmlparser/nsParser.cpp", 497); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aURL" ") (" "Error: Null URL given" ")"); do
{ MOZ_CrashSequence(__null, 497); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
498
499 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
500 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
501 // to avoid introducing unintentional changes to behavior.
502 return mInternalState;
503 }
504
505 if (!aURL) {
506 return NS_ERROR_HTMLPARSER_BADURL;
507 }
508
509 MOZ_ASSERT(!mParserContext, "We expect mParserContext to be null.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParserContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mParserContext))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mParserContext"
" (" "We expect mParserContext to be null." ")", "./../../../parser/htmlparser/nsParser.cpp"
, 509); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mParserContext"
") (" "We expect mParserContext to be null." ")"); do { MOZ_CrashSequence
(__null, 509); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
510
511 mParserContext = MakeUnique<CParserContext>(aURL, mCommand);
512
513 return NS_OK;
514}
515
516/**
517 * Used by XML fragment parsing below.
518 *
519 * @param aSourceBuffer contains a string-full of real content
520 */
521nsresult nsParser::Parse(const nsAString& aSourceBuffer, bool aLastCall) {
522 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
523 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
524 // to avoid introducing unintentional changes to behavior.
525 return mInternalState;
526 }
527
528 // Don't bother if we're never going to parse this.
529 if (mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING) {
530 return NS_OK;
531 }
532
533 if (!aLastCall && aSourceBuffer.IsEmpty()) {
534 // Nothing is being passed to the parser so return
535 // immediately. mUnusedInput will get processed when
536 // some data is actually passed in.
537 // But if this is the last call, make sure to finish up
538 // stuff correctly.
539 return NS_OK;
540 }
541
542 // Maintain a reference to ourselves so we don't go away
543 // till we're completely done.
544 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
545
546 if (!mParserContext) {
547 // Only make a new context if we don't have one.
548 mParserContext =
549 MakeUnique<CParserContext>(mUnusedInput, mCommand, aLastCall);
550
551 mUnusedInput.Truncate();
552 } else if (aLastCall) {
553 // Set stream listener state to eOnStop, on the final context - Fix
554 // 68160, to guarantee DidBuildModel() call - Fix 36148
555 mParserContext->mStreamListenerState = eOnStop;
556 mParserContext->mScanner.SetIncremental(false);
557 }
558
559 mParserContext->mScanner.Append(aSourceBuffer);
560 return ResumeParse(false, false, false);
561}
562
563nsresult nsParser::ParseFragment(const nsAString& aSourceBuffer,
564 nsTArray<nsString>& aTagStack) {
565 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
566 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
567 // to avoid introducing unintentional changes to behavior.
568 return mInternalState;
569 }
570
571 nsresult result = NS_OK;
572 nsAutoString theContext;
573 uint32_t theCount = aTagStack.Length();
574 uint32_t theIndex = 0;
575
576 for (theIndex = 0; theIndex < theCount; theIndex++) {
577 theContext.Append('<');
578 theContext.Append(aTagStack[theCount - theIndex - 1]);
579 theContext.Append('>');
580 }
581
582 if (theCount == 0) {
583 // Ensure that the buffer is not empty. Because none of the DTDs care
584 // about leading whitespace, this doesn't change the result.
585 theContext.Assign(' ');
586 }
587
588 // First, parse the context to build up the DTD's tag stack. Note that we
589 // pass false for the aLastCall parameter.
590 result = Parse(theContext, false);
591 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
592 return result;
593 }
594
595 if (!mSink) {
596 // Parse must have failed in the XML case and so the sink was killed.
597 return NS_ERROR_HTMLPARSER_STOPPARSING;
598 }
599
600 nsCOMPtr<nsIFragmentContentSink> fragSink = do_QueryInterface(mSink);
601 NS_ASSERTION(fragSink, "ParseFragment requires a fragment content sink")do { if (!(fragSink)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "ParseFragment requires a fragment content sink"
, "fragSink", "./../../../parser/htmlparser/nsParser.cpp", 601
); MOZ_PretendNoReturn(); } } while (0)
;
602
603 fragSink->WillBuildContent();
604 // Now, parse the actual content. Note that this is the last call
605 // for HTML content, but for XML, we will want to build and parse
606 // the end tags. However, if tagStack is empty, it's the last call
607 // for XML as well.
608 if (theCount == 0) {
609 result = Parse(aSourceBuffer, true);
610 fragSink->DidBuildContent();
611 } else {
612 // Add an end tag chunk, so expat will read the whole source buffer,
613 // and not worry about ']]' etc.
614 result = Parse(aSourceBuffer + u"</"_ns, false);
615 fragSink->DidBuildContent();
616
617 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1)))) {
618 nsAutoString endContext;
619 for (theIndex = 0; theIndex < theCount; theIndex++) {
620 // we already added an end tag chunk above
621 if (theIndex > 0) {
622 endContext.AppendLiteral("</");
623 }
624
625 nsString& thisTag = aTagStack[theIndex];
626 // was there an xmlns=?
627 int32_t endOfTag = thisTag.FindChar(char16_t(' '));
628 if (endOfTag == -1) {
629 endContext.Append(thisTag);
630 } else {
631 endContext.Append(Substring(thisTag, 0, endOfTag));
632 }
633
634 endContext.Append('>');
635 }
636
637 result = Parse(endContext, true);
638 }
639 }
640
641 mParserContext.reset();
642
643 return result;
644}
645
646/**
647 * This routine is called to cause the parser to continue parsing its
648 * underlying stream. This call allows the parse process to happen in
649 * chunks, such as when the content is push based, and we need to parse in
650 * pieces.
651 *
652 * An interesting change in how the parser gets used has led us to add extra
653 * processing to this method. The case occurs when the parser is blocked in
654 * one context, and gets a parse(string) call in another context. In this
655 * case, the parserContexts are linked. No problem.
656 *
657 * The problem is that Parse(string) assumes that it can proceed unabated,
658 * but if the parser is already blocked that assumption is false. So we
659 * needed to add a mechanism here to allow the parser to continue to process
660 * (the pop and free) contexts until 1) it get's blocked again; 2) it runs
661 * out of contexts.
662 *
663 *
664 * @param allowItertion : set to true if non-script resumption is requested
665 * @param aIsFinalChunk : tells us when the last chunk of data is provided.
666 * @return error code -- 0 if ok, non-zero if error.
667 */
668nsresult nsParser::ResumeParse(bool allowIteration, bool aIsFinalChunk,
669 bool aCanInterrupt) {
670 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
671 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
672 // to avoid introducing unintentional changes to behavior.
673 return mInternalState;
674 }
675
676 nsresult result = NS_OK;
677
678 if (!mBlocked && mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
679 result = WillBuildModel();
680 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
681 mFlags &= ~NS_PARSER_FLAG_CAN_TOKENIZE0x00000002;
682 return result;
683 }
684
685 if (mExpatDriver) {
686 mSink->WillResume();
687 bool theIterationIsOk = true;
688
689 while (result == NS_OK && theIterationIsOk) {
690 if (!mUnusedInput.IsEmpty()) {
691 // -- Ref: Bug# 22485 --
692 // Insert the unused input into the source buffer
693 // as if it was read from the input stream.
694 // Adding UngetReadable() per vidur!!
695 mParserContext->mScanner.UngetReadable(mUnusedInput);
696 mUnusedInput.Truncate(0);
697 }
698
699 // Only allow parsing to be interrupted in the subsequent call to
700 // build model.
701 nsresult theTokenizerResult;
702 if (mFlags & NS_PARSER_FLAG_CAN_TOKENIZE0x00000002) {
703 mParserContext->mScanner.Mark();
704 if (mParserContext->mDocType == eXML &&
705 mParserContext->mParserCommand != eViewSource) {
706 theTokenizerResult = mExpatDriver->ResumeParse(
707 mParserContext->mScanner, aIsFinalChunk);
708 if (NS_FAILED(theTokenizerResult)((bool)(__builtin_expect(!!(NS_FAILED_impl(theTokenizerResult
)), 0)))
) {
709 mParserContext->mScanner.RewindToMark();
710 if (NS_ERROR_HTMLPARSER_STOPPARSING == theTokenizerResult) {
711 theTokenizerResult = Terminate();
712 mSink = nullptr;
713 }
714 }
715 } else {
716 // Nothing to do for non-XML. Note that this should only be
717 // about:blank at this point, we're also checking for view-source
718 // above, but that shouldn't end up here anymore.
719 theTokenizerResult = NS_ERROR_HTMLPARSER_EOF;
720 }
721 } else {
722 theTokenizerResult = NS_OK;
723 }
724
725 result = mExpatDriver->BuildModel();
726 if (result == NS_ERROR_HTMLPARSER_INTERRUPTED && aIsFinalChunk) {
727 PostContinueEvent();
728 }
729
730 theIterationIsOk = theTokenizerResult != NS_ERROR_HTMLPARSER_EOF &&
731 result != NS_ERROR_HTMLPARSER_INTERRUPTED;
732
733 // Make sure not to stop parsing too early. Therefore, before shutting
734 // down the parser, it's important to check whether the input buffer
735 // has been scanned to completion (theTokenizerResult should be kEOF).
736 // kEOF -> End of buffer.
737
738 // If we're told the parser has been blocked, we disable all further
739 // parsing (and cache any data coming in) until the parser is
740 // re-enabled.
741 if (NS_ERROR_HTMLPARSER_BLOCK == result) {
742 mSink->WillInterrupt();
743 return NS_OK;
744 }
745 if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
746 // Note: Parser Terminate() calls DidBuildModel.
747 if (mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
748 DidBuildModel();
749 mInternalState = result;
750 }
751
752 return NS_OK;
753 }
754 if (((NS_OK == result &&
755 theTokenizerResult == NS_ERROR_HTMLPARSER_EOF) ||
756 result == NS_ERROR_HTMLPARSER_INTERRUPTED) &&
757 mParserContext->mStreamListenerState == eOnStop) {
758 DidBuildModel();
759 return NS_OK;
760 }
761
762 if (theTokenizerResult == NS_ERROR_HTMLPARSER_EOF ||
763 result == NS_ERROR_HTMLPARSER_INTERRUPTED) {
764 result = (result == NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
765 mSink->WillInterrupt();
766 }
767 }
768 } else {
769 mInternalState = result = NS_ERROR_HTMLPARSER_UNRESOLVEDDTD;
770 }
771 }
772
773 return (result == NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
774}
775
776/*******************************************************************
777 These methods are used to talk to the netlib system...
778 *******************************************************************/
779
780nsresult nsParser::OnStartRequest(nsIRequest* request) {
781 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
782 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
783 // to avoid introducing unintentional changes to behavior.
784 return mInternalState;
785 }
786
787 MOZ_ASSERT(eNone == mParserContext->mStreamListenerState,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(eNone == mParserContext->mStreamListenerState)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(eNone == mParserContext->mStreamListenerState))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("eNone == mParserContext->mStreamListenerState"
" (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")", "./../../../parser/htmlparser/nsParser.cpp", 789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "eNone == mParserContext->mStreamListenerState"
") (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")"); do { MOZ_CrashSequence(__null, 789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
788 "Parser's nsIStreamListener API was not setup "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(eNone == mParserContext->mStreamListenerState)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(eNone == mParserContext->mStreamListenerState))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("eNone == mParserContext->mStreamListenerState"
" (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")", "./../../../parser/htmlparser/nsParser.cpp", 789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "eNone == mParserContext->mStreamListenerState"
") (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")"); do { MOZ_CrashSequence(__null, 789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
789 "correctly in constructor.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(eNone == mParserContext->mStreamListenerState)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(eNone == mParserContext->mStreamListenerState))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("eNone == mParserContext->mStreamListenerState"
" (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")", "./../../../parser/htmlparser/nsParser.cpp", 789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "eNone == mParserContext->mStreamListenerState"
") (" "Parser's nsIStreamListener API was not setup " "correctly in constructor."
")"); do { MOZ_CrashSequence(__null, 789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
790
791 mParserContext->mStreamListenerState = eOnStart;
792 mParserContext->mAutoDetectStatus = eUnknownDetect;
793 mParserContext->mRequest = request;
794
795 mExpatDriver = nullptr;
796
797 nsresult rv;
798 nsAutoCString contentType;
799 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
800 if (channel) {
801 rv = channel->GetContentType(contentType);
802 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
803 mParserContext->SetMimeType(contentType);
804 }
805 }
806
807 rv = NS_OK;
808
809 return rv;
810}
811
812static bool ExtractCharsetFromXmlDeclaration(const unsigned char* aBytes,
813 int32_t aLen,
814 nsCString& oCharset) {
815 // This code is rather pointless to have. Might as well reuse expat as
816 // seen in nsHtml5StreamParser. -- hsivonen
817 oCharset.Truncate();
818 if ((aLen >= 5) && ('<' == aBytes[0]) && ('?' == aBytes[1]) &&
819 ('x' == aBytes[2]) && ('m' == aBytes[3]) && ('l' == aBytes[4])) {
820 int32_t i;
821 bool versionFound = false, encodingFound = false;
822 for (i = 6; i < aLen && !encodingFound; ++i) {
823 // end of XML declaration?
824 if ((((char*)aBytes)[i] == '?') && ((i + 1) < aLen) &&
825 (((char*)aBytes)[i + 1] == '>')) {
826 break;
827 }
828 // Version is required.
829 if (!versionFound) {
830 // Want to avoid string comparisons, hence looking for 'n'
831 // and only if found check the string leading to it. Not
832 // foolproof, but fast.
833 // The shortest string allowed before this is (strlen==13):
834 // <?xml version
835 if ((((char*)aBytes)[i] == 'n') && (i >= 12) &&
836 (0 == strncmp("versio", (char*)(aBytes + i - 6), 6))) {
837 // Fast forward through version
838 char q = 0;
839 for (++i; i < aLen; ++i) {
840 char qi = ((char*)aBytes)[i];
841 if (qi == '\'' || qi == '"') {
842 if (q && q == qi) {
843 // ending quote
844 versionFound = true;
845 break;
846 } else {
847 // Starting quote
848 q = qi;
849 }
850 }
851 }
852 }
853 } else {
854 // encoding must follow version
855 // Want to avoid string comparisons, hence looking for 'g'
856 // and only if found check the string leading to it. Not
857 // foolproof, but fast.
858 // The shortest allowed string before this (strlen==26):
859 // <?xml version="1" encoding
860 if ((((char*)aBytes)[i] == 'g') && (i >= 25) &&
861 (0 == strncmp("encodin", (char*)(aBytes + i - 7), 7))) {
862 int32_t encStart = 0;
863 char q = 0;
864 for (++i; i < aLen; ++i) {
865 char qi = ((char*)aBytes)[i];
866 if (qi == '\'' || qi == '"') {
867 if (q && q == qi) {
868 int32_t count = i - encStart;
869 // encoding value is invalid if it is UTF-16
870 if (count > 0 &&
871 PL_strncasecmp("UTF-16", (char*)(aBytes + encStart),
872 count)) {
873 oCharset.Assign((char*)(aBytes + encStart), count);
874 }
875 encodingFound = true;
876 break;
877 } else {
878 encStart = i + 1;
879 q = qi;
880 }
881 }
882 }
883 }
884 } // if (!versionFound)
885 } // for
886 }
887 return !oCharset.IsEmpty();
888}
889
890inline char GetNextChar(nsACString::const_iterator& aStart,
891 nsACString::const_iterator& aEnd) {
892 NS_ASSERTION(aStart != aEnd, "end of buffer")do { if (!(aStart != aEnd)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "end of buffer", "aStart != aEnd", "./../../../parser/htmlparser/nsParser.cpp"
, 892); MOZ_PretendNoReturn(); } } while (0)
;
893 return (++aStart != aEnd) ? *aStart : '\0';
894}
895
896typedef struct {
897 bool mNeedCharsetCheck;
898 nsParser* mParser;
899 nsScanner* mScanner;
900 nsIRequest* mRequest;
901} ParserWriteStruct;
902
903/*
904 * This function is invoked as a result of a call to a stream's
905 * ReadSegments() method. It is called for each contiguous buffer
906 * of data in the underlying stream or pipe. Using ReadSegments
907 * allows us to avoid copying data to read out of the stream.
908 */
909static nsresult ParserWriteFunc(nsIInputStream* in, void* closure,
910 const char* fromRawSegment, uint32_t toOffset,
911 uint32_t count, uint32_t* writeCount) {
912 nsresult result;
913 ParserWriteStruct* pws = static_cast<ParserWriteStruct*>(closure);
914 const unsigned char* buf =
915 reinterpret_cast<const unsigned char*>(fromRawSegment);
916 uint32_t theNumRead = count;
917
918 if (!pws) {
919 return NS_ERROR_FAILURE;
920 }
921
922 if (pws->mNeedCharsetCheck) {
923 pws->mNeedCharsetCheck = false;
924 int32_t source;
925 auto preferred = pws->mParser->GetDocumentCharset(source);
926
927 // This code was bogus when I found it. It expects the BOM or the XML
928 // declaration to be entirely in the first network buffer. -- hsivonen
929 const Encoding* encoding;
930 std::tie(encoding, std::ignore) = Encoding::ForBOM(Span(buf, count));
931 if (encoding) {
932 // The decoder will swallow the BOM. The UTF-16 will re-sniff for
933 // endianness. The value of preferred is now "UTF-8", "UTF-16LE"
934 // or "UTF-16BE".
935 preferred = WrapNotNull(encoding);
936 source = kCharsetFromByteOrderMark;
937 } else if (source < kCharsetFromChannel) {
938 nsAutoCString declCharset;
939
940 if (ExtractCharsetFromXmlDeclaration(buf, count, declCharset)) {
941 encoding = Encoding::ForLabel(declCharset);
942 if (encoding) {
943 preferred = WrapNotNull(encoding);
944 source = kCharsetFromMetaTag;
945 }
946 }
947 }
948
949 pws->mParser->SetDocumentCharset(preferred, source, false);
950 pws->mParser->SetSinkCharset(preferred);
951 }
952
953 result = pws->mScanner->Append(fromRawSegment, theNumRead);
954 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1)))) {
955 *writeCount = count;
956 }
957
958 return result;
959}
960
961nsresult nsParser::OnDataAvailable(nsIRequest* request,
962 nsIInputStream* pIStream,
963 uint64_t sourceOffset, uint32_t aLength) {
964 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
965 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
966 // to avoid introducing unintentional changes to behavior.
967 return mInternalState;
968 }
969
970 MOZ_ASSERT((eOnStart == mParserContext->mStreamListenerState ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
" (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")", "./../../../parser/htmlparser/nsParser.cpp", 972); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
") (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")"); do { MOZ_CrashSequence(__null, 972); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
971 eOnDataAvail == mParserContext->mStreamListenerState),do { static_assert( mozilla::detail::AssertionConditionType<
decltype((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
" (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")", "./../../../parser/htmlparser/nsParser.cpp", 972); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
") (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")"); do { MOZ_CrashSequence(__null, 972); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
972 "Error: OnStartRequest() must be called before OnDataAvailable()")do { static_assert( mozilla::detail::AssertionConditionType<
decltype((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((eOnStart == mParserContext->mStreamListenerState
|| eOnDataAvail == mParserContext->mStreamListenerState))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
" (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")", "./../../../parser/htmlparser/nsParser.cpp", 972); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(eOnStart == mParserContext->mStreamListenerState || eOnDataAvail == mParserContext->mStreamListenerState)"
") (" "Error: OnStartRequest() must be called before OnDataAvailable()"
")"); do { MOZ_CrashSequence(__null, 972); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
973 MOZ_ASSERT(NS_InputStreamIsBuffered(pIStream),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_InputStreamIsBuffered(pIStream))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_InputStreamIsBuffered(pIStream
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("NS_InputStreamIsBuffered(pIStream)" " (" "Must have a buffered input stream"
")", "./../../../parser/htmlparser/nsParser.cpp", 974); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "NS_InputStreamIsBuffered(pIStream)" ") (" "Must have a buffered input stream"
")"); do { MOZ_CrashSequence(__null, 974); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
974 "Must have a buffered input stream")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_InputStreamIsBuffered(pIStream))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_InputStreamIsBuffered(pIStream
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("NS_InputStreamIsBuffered(pIStream)" " (" "Must have a buffered input stream"
")", "./../../../parser/htmlparser/nsParser.cpp", 974); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "NS_InputStreamIsBuffered(pIStream)" ") (" "Must have a buffered input stream"
")"); do { MOZ_CrashSequence(__null, 974); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
975
976 nsresult rv = NS_OK;
977
978 if (mParserContext->mRequest == request) {
979 mParserContext->mStreamListenerState = eOnDataAvail;
980
981 uint32_t totalRead;
982 ParserWriteStruct pws;
983 pws.mNeedCharsetCheck = true;
984 pws.mParser = this;
985 pws.mScanner = &mParserContext->mScanner;
986 pws.mRequest = request;
987
988 rv = pIStream->ReadSegments(ParserWriteFunc, &pws, aLength, &totalRead);
989 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
990 return rv;
991 }
992
993 // If there are scripts executing, this is probably due to a synchronous
994 // XMLHttpRequest, see bug 460706 and 1938290.
995 if (IsScriptExecuting()) {
996 ContinueParsingDocumentAfterCurrentScript();
997 return rv;
998 }
999
1000 if (!mProcessingNetworkData) {
1001 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
1002 nsCOMPtr<nsIContentSink> sinkDeathGrip(mSink);
1003 mProcessingNetworkData = true;
1004 if (sinkDeathGrip) {
1005 sinkDeathGrip->WillParse();
1006 }
1007 rv = ResumeParse();
1008 // Check if someone spun the event loop while we were parsing (XML
1009 // script...) If so, and OnStop was called during the spin, process it
1010 // now.
1011 if ((mParserContext->mRequest == request) && mOnStopPending) {
1012 mOnStopPending = false;
1013 mParserContext->mStreamListenerState = eOnStop;
1014 mParserContext->mScanner.SetIncremental(false);
1015
1016 if (sinkDeathGrip) {
1017 sinkDeathGrip->WillParse();
1018 }
1019 rv = ResumeParse(true, true);
1020 }
1021 mProcessingNetworkData = false;
1022 }
1023 } else {
1024 rv = NS_ERROR_UNEXPECTED;
1025 }
1026
1027 return rv;
1028}
1029
1030/**
1031 * This is called by the networking library once the last block of data
1032 * has been collected from the net.
1033 */
1034nsresult nsParser::OnStopRequest(nsIRequest* request, nsresult status) {
1035 if (mInternalState == NS_ERROR_OUT_OF_MEMORY) {
1036 // Checking NS_ERROR_OUT_OF_MEMORY instead of NS_FAILED
1037 // to avoid introducing unintentional changes to behavior.
1038 return mInternalState;
1039 }
1040
1041 nsresult rv = NS_OK;
1042
1043 mStreamStatus = status;
1044
1045 // If the load was aborted (because we were removed from the DOM tree for
1046 // instance) we should abort the parser and terminate early. Resuming the
1047 // parse may trigger synchronous script execution here.
1048 if (status == NS_BINDING_ABORTED) {
1049 return Terminate();
1050 }
1051
1052 // If there are scripts executing, this is probably due to a synchronous
1053 // XMLHttpRequest, see bug 460706 and 1938290.
1054 if (IsScriptExecuting()) {
1055 // We'll have to handle this later
1056 mOnStopPending = true;
1057 ContinueParsingDocumentAfterCurrentScript();
1058 return rv;
1059 }
1060
1061 if (!mProcessingNetworkData && NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
1062 if (mParserContext->mRequest == request) {
1063 mParserContext->mStreamListenerState = eOnStop;
1064 mParserContext->mScanner.SetIncremental(false);
1065 }
1066 mProcessingNetworkData = true;
1067 if (mSink) {
1068 mSink->WillParse();
1069 }
1070 rv = ResumeParse(true, true);
1071 mProcessingNetworkData = false;
1072 } else {
1073 // We'll have to handle this later
1074 mOnStopPending = true;
1075 }
1076
1077 // If the parser isn't enabled, we don't finish parsing till
1078 // it is reenabled.
1079
1080 return rv;
1081}
1082
1083/**
1084 * Get this as nsIStreamListener
1085 */
1086nsIStreamListener* nsParser::GetStreamListener() { return this; }