Backed out 6 changesets (bug 1712140) for causing Linux related wpt failures in nsHtm...
[gecko.git] / parser / html / nsHtml5TreeBuilderCppSupplement.h
blobd84adcd8a0d7b8f1a19d4cc432a9951712c4ae5d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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/. */
7 #include "ErrorList.h"
8 #include "nsError.h"
9 #include "nsHtml5AttributeName.h"
10 #include "nsHtml5String.h"
11 #include "nsNetUtil.h"
12 #include "mozilla/dom/FetchPriority.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/Likely.h"
15 #include "mozilla/StaticPrefs_dom.h"
16 #include "mozilla/StaticPrefs_network.h"
17 #include "mozilla/UniquePtr.h"
18 #include "mozilla/UniquePtrExtensions.h"
20 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder)
21 : mode(0),
22 originalMode(0),
23 framesetOk(false),
24 tokenizer(nullptr),
25 scriptingEnabled(false),
26 needToDropLF(false),
27 fragment(false),
28 contextName(nullptr),
29 contextNamespace(kNameSpaceID_None),
30 contextNode(nullptr),
31 templateModePtr(0),
32 stackNodesIdx(0),
33 numStackNodes(0),
34 currentPtr(0),
35 listPtr(0),
36 formPointer(nullptr),
37 headPointer(nullptr),
38 charBufferLen(0),
39 quirks(false),
40 forceNoQuirks(false),
41 mBuilder(aBuilder),
42 mViewSource(nullptr),
43 mOpSink(nullptr),
44 mHandles(nullptr),
45 mHandlesUsed(0),
46 mSpeculativeLoadStage(nullptr),
47 mBroken(NS_OK),
48 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
49 mPreventScriptExecution(false),
50 mGenerateSpeculativeLoads(false),
51 mHasSeenImportMap(false)
52 #ifdef DEBUG
54 mActive(false)
55 #endif
57 MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
60 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
61 nsHtml5TreeOpStage* aStage,
62 bool aGenerateSpeculativeLoads)
63 : mode(0),
64 originalMode(0),
65 framesetOk(false),
66 tokenizer(nullptr),
67 scriptingEnabled(false),
68 needToDropLF(false),
69 fragment(false),
70 contextName(nullptr),
71 contextNamespace(kNameSpaceID_None),
72 contextNode(nullptr),
73 templateModePtr(0),
74 stackNodesIdx(0),
75 numStackNodes(0),
76 currentPtr(0),
77 listPtr(0),
78 formPointer(nullptr),
79 headPointer(nullptr),
80 charBufferLen(0),
81 quirks(false),
82 forceNoQuirks(false),
83 mBuilder(nullptr),
84 mViewSource(nullptr),
85 mOpSink(aOpSink),
86 mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]),
87 mHandlesUsed(0),
88 mSpeculativeLoadStage(aStage),
89 mBroken(NS_OK),
90 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
91 mPreventScriptExecution(false),
92 mGenerateSpeculativeLoads(aGenerateSpeculativeLoads),
93 mHasSeenImportMap(false)
94 #ifdef DEBUG
96 mActive(false)
97 #endif
99 MOZ_ASSERT(!(!aStage && aGenerateSpeculativeLoads),
100 "Must not generate speculative loads without a stage");
101 MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
104 nsHtml5TreeBuilder::~nsHtml5TreeBuilder() {
105 MOZ_COUNT_DTOR(nsHtml5TreeBuilder);
106 NS_ASSERTION(!mActive,
107 "nsHtml5TreeBuilder deleted without ever calling end() on it!");
108 mOpQueue.Clear();
111 nsIContentHandle* nsHtml5TreeBuilder::createElement(
112 int32_t aNamespace, nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
113 nsIContentHandle* aIntendedParent, nsHtml5ContentCreatorFunction aCreator) {
114 MOZ_ASSERT(aAttributes, "Got null attributes.");
115 MOZ_ASSERT(aName, "Got null name.");
116 MOZ_ASSERT(aNamespace == kNameSpaceID_XHTML ||
117 aNamespace == kNameSpaceID_SVG ||
118 aNamespace == kNameSpaceID_MathML,
119 "Bogus namespace.");
121 if (mBuilder) {
122 nsIContent* intendedParent =
123 aIntendedParent ? static_cast<nsIContent*>(aIntendedParent) : nullptr;
125 // intendedParent == nullptr is a special case where the
126 // intended parent is the document.
127 nsNodeInfoManager* nodeInfoManager =
128 intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager()
129 : mBuilder->GetNodeInfoManager();
131 nsIContent* elem;
132 if (aNamespace == kNameSpaceID_XHTML) {
133 elem = nsHtml5TreeOperation::CreateHTMLElement(
134 aName, aAttributes, mozilla::dom::FROM_PARSER_FRAGMENT,
135 nodeInfoManager, mBuilder, aCreator.html);
136 } else if (aNamespace == kNameSpaceID_SVG) {
137 elem = nsHtml5TreeOperation::CreateSVGElement(
138 aName, aAttributes, mozilla::dom::FROM_PARSER_FRAGMENT,
139 nodeInfoManager, mBuilder, aCreator.svg);
140 } else {
141 MOZ_ASSERT(aNamespace == kNameSpaceID_MathML);
142 elem = nsHtml5TreeOperation::CreateMathMLElement(
143 aName, aAttributes, nodeInfoManager, mBuilder);
145 if (MOZ_UNLIKELY(aAttributes != tokenizer->GetAttributes() &&
146 aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES)) {
147 delete aAttributes;
149 return elem;
152 nsIContentHandle* content = AllocateContentHandle();
153 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
154 if (MOZ_UNLIKELY(!treeOp)) {
155 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
156 return nullptr;
159 if (aNamespace == kNameSpaceID_XHTML) {
160 opCreateHTMLElement opeation(
161 content, aName, aAttributes, aCreator.html, aIntendedParent,
162 (!!mSpeculativeLoadStage) ? mozilla::dom::FROM_PARSER_NETWORK
163 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE);
164 treeOp->Init(mozilla::AsVariant(opeation));
165 } else if (aNamespace == kNameSpaceID_SVG) {
166 opCreateSVGElement operation(
167 content, aName, aAttributes, aCreator.svg, aIntendedParent,
168 (!!mSpeculativeLoadStage) ? mozilla::dom::FROM_PARSER_NETWORK
169 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE);
170 treeOp->Init(mozilla::AsVariant(operation));
171 } else {
172 // kNameSpaceID_MathML
173 opCreateMathMLElement operation(content, aName, aAttributes,
174 aIntendedParent);
175 treeOp->Init(mozilla::AsVariant(operation));
178 // mSpeculativeLoadStage is non-null only in the off-the-main-thread
179 // tree builder, which handles the network stream
181 // Start wall of code for speculative loading and line numbers
183 if (mGenerateSpeculativeLoads && mode != IN_TEMPLATE) {
184 switch (aNamespace) {
185 case kNameSpaceID_XHTML:
186 if (nsGkAtoms::img == aName) {
187 nsHtml5String loading =
188 aAttributes->getValue(nsHtml5AttributeName::ATTR_LOADING);
189 if (!loading.LowerCaseEqualsASCII("lazy")) {
190 nsHtml5String url =
191 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
192 nsHtml5String srcset =
193 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
194 nsHtml5String crossOrigin =
195 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
196 nsHtml5String referrerPolicy = aAttributes->getValue(
197 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
198 nsHtml5String sizes =
199 aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
200 mSpeculativeLoadQueue.AppendElement()->InitImage(
201 url, crossOrigin, /* aMedia = */ nullptr, referrerPolicy,
202 srcset, sizes, false);
204 } else if (nsGkAtoms::source == aName) {
205 nsHtml5String srcset =
206 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
207 // Sources without srcset cannot be selected. The source could also be
208 // for a media element, but in that context doesn't use srcset. See
209 // comments in nsHtml5SpeculativeLoad.h about <picture> preloading
210 if (srcset) {
211 nsHtml5String sizes =
212 aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
213 nsHtml5String type =
214 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
215 nsHtml5String media =
216 aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA);
217 mSpeculativeLoadQueue.AppendElement()->InitPictureSource(
218 srcset, sizes, type, media);
220 } else if (nsGkAtoms::script == aName) {
221 nsHtml5TreeOperation* treeOp =
222 mOpQueue.AppendElement(mozilla::fallible);
223 if (MOZ_UNLIKELY(!treeOp)) {
224 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
225 NS_ERROR_OUT_OF_MEMORY);
226 return nullptr;
228 opSetScriptLineAndColumnNumberAndFreeze operation(
229 content, tokenizer->getLineNumber(),
230 // NOTE: tokenizer->getColumnNumber() points '>'.
231 tokenizer->getColumnNumber() + 1);
232 treeOp->Init(mozilla::AsVariant(operation));
234 nsHtml5String type =
235 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
236 nsAutoString typeString;
237 type.ToString(typeString);
239 // Since `typeString` after trimming and lowercasing is only checked
240 // for "module" and " importmap", we don't need to remember
241 // pre-trimming emptiness here.
243 // ASCII whitespace https://infra.spec.whatwg.org/#ascii-whitespace:
244 // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE.
245 static const char kASCIIWhitespace[] = "\t\n\f\r ";
246 typeString.Trim(kASCIIWhitespace);
248 bool isModule = typeString.LowerCaseEqualsASCII("module");
249 bool importmap = typeString.LowerCaseEqualsASCII("importmap");
250 bool async = false;
251 bool defer = false;
252 bool nomodule =
253 aAttributes->contains(nsHtml5AttributeName::ATTR_NOMODULE);
255 // For microtask semantics, we need to queue either
256 // `opRunScriptThatMayDocumentWriteOrBlock` or
257 // `opRunScriptThatCannotDocumentWriteOrBlock` for every script
258 // element--even ones that we already know won't run.
259 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` controls which
260 // kind of operation is used for an HTML script, and this is the
261 // place where `mCurrentHtmlScriptCannotDocumentWriteOrBlock`
262 // needs to be set correctly.
264 // Non-async, non-defer classic scripts that will run MUST use
265 // `opRunScriptThatMayDocumentWriteOrBlock` in order to run
266 // the more complex code that
267 // 1. is able to resume the HTML parse after a parser-blocking
268 // scripts no longer blocks the parser
269 // 2. is able to receive more content to parse on the main thread
270 // via document.write
271 // 3. is able to throw away off-the-main-thread parsing results
272 // if what's document.written on the main thread invalidates
273 // the speculation.
275 // Async and defer classic scripts as well as module scripts and
276 // importmaps MUST use `opRunScriptThatCannotDocumentWriteOrBlock`.
277 // This is necessary particularly because the relevant main-thread
278 // code assumes it doesn't need to deal with resuming the HTML
279 // parse some time afterwards, so using a tree operation with
280 // mismatching expectations regarding that responsibility may
281 // cause the HTML parse to stall.
283 // Various scripts that won't actually run work with either type
284 // of tree op in the sense that the HTML parse won't stall.
285 // However, in the case where a script cannot block or insert
286 // data to the HTML parser via document.write, unnecessary use
287 // of `opRunScriptThatMayDocumentWriteOrBlock` instead of
288 // `opRunScriptThatCannotDocumentWriteOrBlock` causes the HTML
289 // parser to enter the speculative mode when doing so isn't
290 // actually required.
292 // Ideally, we would check for `type`/`language` attribute
293 // combinations that are known to cause non-execution as well as
294 // ScriptLoader::IsScriptEventHandler equivalent. That way, we
295 // wouldn't unnecessarily speculate after scripts that won't
296 // execute. https://bugzilla.mozilla.org/show_bug.cgi?id=1848311
298 if (importmap) {
299 // If we see an importmap, we don't want to later start speculative
300 // loads for modulepreloads, since such load might finish before
301 // the importmap is created. This also applies to module scripts so
302 // that any modulepreload integrity checks can be performed before
303 // the modules scripts are loaded.
304 // This state is not part of speculation rollback: If an importmap
305 // is seen speculatively and the speculation is rolled back, the
306 // importmap is still considered seen.
307 // TODO: Sync importmap seenness between the main thread and the
308 // parser thread.
309 // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312
310 mHasSeenImportMap = true;
312 nsHtml5String url =
313 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
314 if (url) {
315 async = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC);
316 defer = aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
317 if ((isModule && !mHasSeenImportMap) ||
318 (!isModule && !importmap && !nomodule)) {
319 nsHtml5String charset =
320 aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
321 nsHtml5String crossOrigin =
322 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
323 nsHtml5String nonce =
324 aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
325 nsHtml5String fetchPriority = aAttributes->getValue(
326 nsHtml5AttributeName::ATTR_FETCHPRIORITY);
327 nsHtml5String integrity =
328 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
329 nsHtml5String referrerPolicy = aAttributes->getValue(
330 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
331 mSpeculativeLoadQueue.AppendElement()->InitScript(
332 url, charset, type, crossOrigin, /* aMedia = */ nullptr,
333 nonce, fetchPriority, integrity, referrerPolicy,
334 mode == nsHtml5TreeBuilder::IN_HEAD, async, defer, false);
337 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` MUST be computed to
338 // match the ScriptLoader-perceived kind of the script regardless of
339 // enqueuing a speculative load. Scripts with the `nomodule` attribute
340 // never block or document.write: Either the attribute prevents a
341 // classic script execution or is ignored on a module script or
342 // importmap.
343 mCurrentHtmlScriptCannotDocumentWriteOrBlock =
344 isModule || importmap || async || defer || nomodule;
345 } else if (nsGkAtoms::link == aName) {
346 nsHtml5String rel =
347 aAttributes->getValue(nsHtml5AttributeName::ATTR_REL);
348 // Not splitting on space here is bogus but the old parser didn't even
349 // do a case-insensitive check.
350 if (rel) {
351 if (rel.LowerCaseEqualsASCII("stylesheet")) {
352 nsHtml5String url =
353 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
354 if (url) {
355 nsHtml5String charset =
356 aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
357 nsHtml5String crossOrigin = aAttributes->getValue(
358 nsHtml5AttributeName::ATTR_CROSSORIGIN);
359 nsHtml5String nonce =
360 aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
361 nsHtml5String integrity =
362 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
363 nsHtml5String referrerPolicy = aAttributes->getValue(
364 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
365 nsHtml5String media =
366 aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA);
367 nsHtml5String fetchPriority = aAttributes->getValue(
368 nsHtml5AttributeName::ATTR_FETCHPRIORITY);
369 mSpeculativeLoadQueue.AppendElement()->InitStyle(
370 url, charset, crossOrigin, media, referrerPolicy, nonce,
371 integrity, false, fetchPriority);
373 } else if (rel.LowerCaseEqualsASCII("preconnect")) {
374 nsHtml5String url =
375 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
376 if (url) {
377 nsHtml5String crossOrigin = aAttributes->getValue(
378 nsHtml5AttributeName::ATTR_CROSSORIGIN);
379 mSpeculativeLoadQueue.AppendElement()->InitPreconnect(
380 url, crossOrigin);
382 } else if (rel.LowerCaseEqualsASCII("preload")) {
383 nsHtml5String url =
384 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
385 if (url) {
386 nsHtml5String as =
387 aAttributes->getValue(nsHtml5AttributeName::ATTR_AS);
388 nsHtml5String charset =
389 aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
390 nsHtml5String crossOrigin = aAttributes->getValue(
391 nsHtml5AttributeName::ATTR_CROSSORIGIN);
392 nsHtml5String nonce =
393 aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
394 nsHtml5String integrity =
395 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
396 nsHtml5String referrerPolicy = aAttributes->getValue(
397 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
398 nsHtml5String media =
399 aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA);
400 nsHtml5String fetchPriority = aAttributes->getValue(
401 nsHtml5AttributeName::ATTR_FETCHPRIORITY);
403 // Note that respective speculative loaders for scripts and
404 // styles check all additional attributes to be equal to use the
405 // speculative load. So, if any of them is specified and the
406 // preload has to take the expected effect, those attributes
407 // must also be specified on the actual tag to use the preload.
408 // Omitting an attribute on both will make the values equal
409 // (empty) and thus use the preload.
410 if (as.LowerCaseEqualsASCII("script")) {
411 nsHtml5String type =
412 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
413 mSpeculativeLoadQueue.AppendElement()->InitScript(
414 url, charset, type, crossOrigin, media, nonce,
415 /* aFetchPriority */ fetchPriority, integrity,
416 referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
417 false, false, true);
418 } else if (as.LowerCaseEqualsASCII("style")) {
419 mSpeculativeLoadQueue.AppendElement()->InitStyle(
420 url, charset, crossOrigin, media, referrerPolicy, nonce,
421 integrity, true, fetchPriority);
422 } else if (as.LowerCaseEqualsASCII("image")) {
423 nsHtml5String srcset = aAttributes->getValue(
424 nsHtml5AttributeName::ATTR_IMAGESRCSET);
425 nsHtml5String sizes = aAttributes->getValue(
426 nsHtml5AttributeName::ATTR_IMAGESIZES);
427 mSpeculativeLoadQueue.AppendElement()->InitImage(
428 url, crossOrigin, media, referrerPolicy, srcset, sizes,
429 true);
430 } else if (as.LowerCaseEqualsASCII("font")) {
431 mSpeculativeLoadQueue.AppendElement()->InitFont(
432 url, crossOrigin, media, referrerPolicy, fetchPriority);
433 } else if (as.LowerCaseEqualsASCII("fetch")) {
434 mSpeculativeLoadQueue.AppendElement()->InitFetch(
435 url, crossOrigin, media, referrerPolicy, fetchPriority);
437 // Other "as" values will be supported later.
439 } else if (mozilla::StaticPrefs::network_modulepreload() &&
440 rel.LowerCaseEqualsASCII("modulepreload") &&
441 !mHasSeenImportMap) {
442 nsHtml5String url =
443 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
444 if (url && url.Length() != 0) {
445 nsHtml5String as =
446 aAttributes->getValue(nsHtml5AttributeName::ATTR_AS);
447 nsAutoString asString;
448 as.ToString(asString);
449 if (mozilla::net::IsScriptLikeOrInvalid(asString)) {
450 nsHtml5String charset =
451 aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
452 RefPtr<nsAtom> moduleType = nsGkAtoms::_module;
453 nsHtml5String type =
454 nsHtml5String::FromAtom(moduleType.forget());
455 nsHtml5String crossOrigin = aAttributes->getValue(
456 nsHtml5AttributeName::ATTR_CROSSORIGIN);
457 nsHtml5String media =
458 aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA);
459 nsHtml5String nonce =
460 aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
461 nsHtml5String integrity = aAttributes->getValue(
462 nsHtml5AttributeName::ATTR_INTEGRITY);
463 nsHtml5String referrerPolicy = aAttributes->getValue(
464 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
465 nsHtml5String fetchPriority = aAttributes->getValue(
466 nsHtml5AttributeName::ATTR_FETCHPRIORITY);
468 mSpeculativeLoadQueue.AppendElement()->InitScript(
469 url, charset, type, crossOrigin, media, nonce,
470 /* aFetchPriority */ fetchPriority, integrity,
471 referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD,
472 false, false, true);
477 } else if (nsGkAtoms::video == aName) {
478 nsHtml5String url =
479 aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
480 if (url) {
481 mSpeculativeLoadQueue.AppendElement()->InitImage(
482 url, nullptr, nullptr, nullptr, nullptr, nullptr, false);
484 } else if (nsGkAtoms::style == aName) {
485 mImportScanner.Start();
486 nsHtml5TreeOperation* treeOp =
487 mOpQueue.AppendElement(mozilla::fallible);
488 if (MOZ_UNLIKELY(!treeOp)) {
489 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
490 NS_ERROR_OUT_OF_MEMORY);
491 return nullptr;
493 opSetStyleLineNumber operation(content, tokenizer->getLineNumber());
494 treeOp->Init(mozilla::AsVariant(operation));
495 } else if (nsGkAtoms::html == aName) {
496 nsHtml5String url =
497 aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
498 mSpeculativeLoadQueue.AppendElement()->InitManifest(url);
499 } else if (nsGkAtoms::base == aName) {
500 nsHtml5String url =
501 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
502 if (url) {
503 mSpeculativeLoadQueue.AppendElement()->InitBase(url);
505 } else if (nsGkAtoms::meta == aName) {
506 if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString(
507 "content-security-policy",
508 aAttributes->getValue(
509 nsHtml5AttributeName::ATTR_HTTP_EQUIV))) {
510 nsHtml5String csp =
511 aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
512 if (csp) {
513 mSpeculativeLoadQueue.AppendElement()->InitMetaCSP(csp);
515 } else if (nsHtml5Portability::
516 lowerCaseLiteralEqualsIgnoreAsciiCaseString(
517 "referrer",
518 aAttributes->getValue(
519 nsHtml5AttributeName::ATTR_NAME))) {
520 nsHtml5String referrerPolicy =
521 aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
522 if (referrerPolicy) {
523 mSpeculativeLoadQueue.AppendElement()->InitMetaReferrerPolicy(
524 referrerPolicy);
528 break;
529 case kNameSpaceID_SVG:
530 if (nsGkAtoms::image == aName) {
531 nsHtml5String url =
532 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
533 if (!url) {
534 url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
536 if (url) {
537 mSpeculativeLoadQueue.AppendElement()->InitImage(
538 url, nullptr, nullptr, nullptr, nullptr, nullptr, false);
540 } else if (nsGkAtoms::script == aName) {
541 nsHtml5TreeOperation* treeOp =
542 mOpQueue.AppendElement(mozilla::fallible);
543 if (MOZ_UNLIKELY(!treeOp)) {
544 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
545 NS_ERROR_OUT_OF_MEMORY);
546 return nullptr;
548 opSetScriptLineAndColumnNumberAndFreeze operation(
549 content, tokenizer->getLineNumber(),
550 // NOTE: tokenizer->getColumnNumber() points '>'.
551 tokenizer->getColumnNumber() + 1);
552 treeOp->Init(mozilla::AsVariant(operation));
554 nsHtml5String url =
555 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
556 if (!url) {
557 url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
559 if (url) {
560 nsHtml5String type =
561 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
562 nsHtml5String crossOrigin =
563 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
564 nsHtml5String nonce =
565 aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE);
566 nsHtml5String integrity =
567 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
568 nsHtml5String referrerPolicy = aAttributes->getValue(
569 nsHtml5AttributeName::ATTR_REFERRERPOLICY);
571 // Bug 1847712: SVG's `<script>` element doesn't support
572 // `fetchpriority` yet.
573 // Use the empty string and rely on the
574 // "invalid value default" state being used later.
575 // Compared to using a non-empty string, this doesn't
576 // require calling `Release()` for the string.
577 nsHtml5String fetchPriority = nsHtml5String::EmptyString();
579 mSpeculativeLoadQueue.AppendElement()->InitScript(
580 url, nullptr, type, crossOrigin, /* aMedia = */ nullptr, nonce,
581 fetchPriority, integrity, referrerPolicy,
582 mode == nsHtml5TreeBuilder::IN_HEAD, false, false, false);
584 } else if (nsGkAtoms::style == aName) {
585 mImportScanner.Start();
586 nsHtml5TreeOperation* treeOp =
587 mOpQueue.AppendElement(mozilla::fallible);
588 if (MOZ_UNLIKELY(!treeOp)) {
589 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
590 NS_ERROR_OUT_OF_MEMORY);
591 return nullptr;
593 opSetStyleLineNumber operation(content, tokenizer->getLineNumber());
594 treeOp->Init(mozilla::AsVariant(operation));
596 break;
598 } else if (aNamespace != kNameSpaceID_MathML) {
599 // No speculative loader--just line numbers and defer/async check
600 if (nsGkAtoms::style == aName) {
601 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
602 if (MOZ_UNLIKELY(!treeOp)) {
603 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
604 return nullptr;
606 opSetStyleLineNumber operation(content, tokenizer->getLineNumber());
607 treeOp->Init(mozilla::AsVariant(operation));
608 } else if (nsGkAtoms::script == aName) {
609 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
610 if (MOZ_UNLIKELY(!treeOp)) {
611 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
612 return nullptr;
614 opSetScriptLineAndColumnNumberAndFreeze operation(
615 content, tokenizer->getLineNumber(),
616 // NOTE: tokenizer->getColumnNumber() points '>'.
617 tokenizer->getColumnNumber() + 1);
618 treeOp->Init(mozilla::AsVariant(operation));
619 if (aNamespace == kNameSpaceID_XHTML) {
620 // Although we come here in cases where the value of
621 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` doesn't actually
622 // matter, we also come here when parsing document.written content on
623 // the main thread. In that case, IT MATTERS that
624 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` is set correctly,
625 // so let's just always set it correctly even if it a bit of wasted work
626 // in the scenarios where no scripts execute and it doesn't matter.
628 // See the comments around generating speculative loads for HTML scripts
629 // elements for the details of when
630 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` needs to be set to
631 // `true` and when to `false`.
633 nsHtml5String type =
634 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
635 nsAutoString typeString;
636 type.ToString(typeString);
638 // Since `typeString` after trimming and lowercasing is only checked
639 // for "module" and " importmap", we don't need to remember
640 // pre-trimming emptiness here.
642 // ASCII whitespace https://infra.spec.whatwg.org/#ascii-whitespace:
643 // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE.
644 static const char kASCIIWhitespace[] = "\t\n\f\r ";
645 typeString.Trim(kASCIIWhitespace);
647 mCurrentHtmlScriptCannotDocumentWriteOrBlock =
648 typeString.LowerCaseEqualsASCII("module") ||
649 typeString.LowerCaseEqualsASCII("nomodule") ||
650 typeString.LowerCaseEqualsASCII("importmap");
652 if (!mCurrentHtmlScriptCannotDocumentWriteOrBlock &&
653 aAttributes->contains(nsHtml5AttributeName::ATTR_SRC)) {
654 mCurrentHtmlScriptCannotDocumentWriteOrBlock =
655 (aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
656 aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER));
659 } else if (aNamespace == kNameSpaceID_XHTML) {
660 if (nsGkAtoms::html == aName) {
661 nsHtml5String url =
662 aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
663 nsHtml5TreeOperation* treeOp =
664 mOpQueue.AppendElement(mozilla::fallible);
665 if (MOZ_UNLIKELY(!treeOp)) {
666 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
667 NS_ERROR_OUT_OF_MEMORY);
668 return nullptr;
670 if (url) {
671 nsString
672 urlString; // Not Auto, because using it to hold nsStringBuffer*
673 url.ToString(urlString);
674 opProcessOfflineManifest operation(ToNewUnicode(urlString));
675 treeOp->Init(mozilla::AsVariant(operation));
676 } else {
677 opProcessOfflineManifest operation(ToNewUnicode(u""_ns));
678 treeOp->Init(mozilla::AsVariant(operation));
680 } else if (nsGkAtoms::base == aName && mViewSource) {
681 nsHtml5String url =
682 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
683 if (url) {
684 mViewSource->AddBase(url);
690 // End wall of code for speculative loading
692 return content;
695 nsIContentHandle* nsHtml5TreeBuilder::createElement(
696 int32_t aNamespace, nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
697 nsIContentHandle* aFormElement, nsIContentHandle* aIntendedParent,
698 nsHtml5ContentCreatorFunction aCreator) {
699 nsIContentHandle* content =
700 createElement(aNamespace, aName, aAttributes, aIntendedParent, aCreator);
701 if (aFormElement) {
702 if (mBuilder) {
703 nsHtml5TreeOperation::SetFormElement(
704 static_cast<nsIContent*>(content),
705 static_cast<nsIContent*>(aFormElement));
706 } else {
707 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
708 if (MOZ_UNLIKELY(!treeOp)) {
709 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
710 return nullptr;
712 opSetFormElement operation(content, aFormElement);
713 treeOp->Init(mozilla::AsVariant(operation));
716 return content;
719 nsIContentHandle* nsHtml5TreeBuilder::createHtmlElementSetAsRoot(
720 nsHtml5HtmlAttributes* aAttributes) {
721 nsHtml5ContentCreatorFunction creator;
722 // <html> uses NS_NewHTMLSharedElement creator
723 creator.html = NS_NewHTMLSharedElement;
724 nsIContentHandle* content = createElement(kNameSpaceID_XHTML, nsGkAtoms::html,
725 aAttributes, nullptr, creator);
726 if (mBuilder) {
727 nsresult rv = nsHtml5TreeOperation::AppendToDocument(
728 static_cast<nsIContent*>(content), mBuilder);
729 if (NS_FAILED(rv)) {
730 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
732 } else {
733 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
734 if (MOZ_UNLIKELY(!treeOp)) {
735 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
736 return nullptr;
738 opAppendToDocument operation(content);
739 treeOp->Init(mozilla::AsVariant(operation));
741 return content;
744 nsIContentHandle* nsHtml5TreeBuilder::createAndInsertFosterParentedElement(
745 int32_t aNamespace, nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
746 nsIContentHandle* aFormElement, nsIContentHandle* aTable,
747 nsIContentHandle* aStackParent, nsHtml5ContentCreatorFunction aCreator) {
748 MOZ_ASSERT(aTable, "Null table");
749 MOZ_ASSERT(aStackParent, "Null stack parent");
751 if (mBuilder) {
752 // Get the foster parent to use as the intended parent when creating
753 // the child element.
754 nsIContent* fosterParent = nsHtml5TreeOperation::GetFosterParent(
755 static_cast<nsIContent*>(aTable),
756 static_cast<nsIContent*>(aStackParent));
758 nsIContentHandle* child = createElement(
759 aNamespace, aName, aAttributes, aFormElement, fosterParent, aCreator);
761 insertFosterParentedChild(child, aTable, aStackParent);
763 return child;
766 // Tree op to get the foster parent that we use as the intended parent
767 // when creating the child element.
768 nsHtml5TreeOperation* fosterParentTreeOp = mOpQueue.AppendElement();
769 NS_ASSERTION(fosterParentTreeOp, "Tree op allocation failed.");
770 nsIContentHandle* fosterParentHandle = AllocateContentHandle();
771 opGetFosterParent operation(aTable, aStackParent, fosterParentHandle);
772 fosterParentTreeOp->Init(mozilla::AsVariant(operation));
774 // Create the element with the correct intended parent.
775 nsIContentHandle* child =
776 createElement(aNamespace, aName, aAttributes, aFormElement,
777 fosterParentHandle, aCreator);
779 // Insert the child into the foster parent.
780 insertFosterParentedChild(child, aTable, aStackParent);
782 return child;
785 void nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement) {
786 MOZ_ASSERT(aElement, "Null element");
788 if (mBuilder) {
789 nsHtml5TreeOperation::Detach(static_cast<nsIContent*>(aElement), mBuilder);
790 return;
793 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
794 if (MOZ_UNLIKELY(!treeOp)) {
795 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
796 return;
798 opDetach operation(aElement);
799 treeOp->Init(mozilla::AsVariant(operation));
802 void nsHtml5TreeBuilder::appendElement(nsIContentHandle* aChild,
803 nsIContentHandle* aParent) {
804 MOZ_ASSERT(aChild, "Null child");
805 MOZ_ASSERT(aParent, "Null parent");
807 if (mBuilder) {
808 nsresult rv = nsHtml5TreeOperation::Append(
809 static_cast<nsIContent*>(aChild), static_cast<nsIContent*>(aParent),
810 mozilla::dom::FROM_PARSER_FRAGMENT, mBuilder);
811 if (NS_FAILED(rv)) {
812 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
814 return;
817 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
818 if (MOZ_UNLIKELY(!treeOp)) {
819 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
820 return;
823 opAppend operation(aChild, aParent,
824 (!!mSpeculativeLoadStage)
825 ? mozilla::dom::FROM_PARSER_NETWORK
826 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE);
827 treeOp->Init(mozilla::AsVariant(operation));
830 void nsHtml5TreeBuilder::appendChildrenToNewParent(
831 nsIContentHandle* aOldParent, nsIContentHandle* aNewParent) {
832 MOZ_ASSERT(aOldParent, "Null old parent");
833 MOZ_ASSERT(aNewParent, "Null new parent");
835 if (mBuilder) {
836 nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent(
837 static_cast<nsIContent*>(aOldParent),
838 static_cast<nsIContent*>(aNewParent), mBuilder);
839 if (NS_FAILED(rv)) {
840 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
842 return;
845 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
846 if (MOZ_UNLIKELY(!treeOp)) {
847 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
848 return;
850 opAppendChildrenToNewParent operation(aOldParent, aNewParent);
851 treeOp->Init(mozilla::AsVariant(operation));
854 void nsHtml5TreeBuilder::insertFosterParentedCharacters(
855 char16_t* aBuffer, int32_t aStart, int32_t aLength,
856 nsIContentHandle* aTable, nsIContentHandle* aStackParent) {
857 MOZ_ASSERT(aBuffer, "Null buffer");
858 MOZ_ASSERT(aTable, "Null table");
859 MOZ_ASSERT(aStackParent, "Null stack parent");
860 MOZ_ASSERT(!aStart, "aStart must always be zero.");
862 if (mBuilder) {
863 nsresult rv = nsHtml5TreeOperation::FosterParentText(
864 static_cast<nsIContent*>(aStackParent),
865 aBuffer, // XXX aStart always ignored???
866 aLength, static_cast<nsIContent*>(aTable), mBuilder);
867 if (NS_FAILED(rv)) {
868 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
870 return;
873 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
874 if (!bufferCopy) {
875 // Just assigning mBroken instead of generating tree op. The caller
876 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
877 mBroken = NS_ERROR_OUT_OF_MEMORY;
878 requestSuspension();
879 return;
882 memcpy(bufferCopy.get(), aBuffer, aLength * sizeof(char16_t));
884 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
885 if (MOZ_UNLIKELY(!treeOp)) {
886 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
887 return;
889 opFosterParentText operation(aStackParent, bufferCopy.release(), aTable,
890 aLength);
891 treeOp->Init(mozilla::AsVariant(operation));
894 void nsHtml5TreeBuilder::insertFosterParentedChild(
895 nsIContentHandle* aChild, nsIContentHandle* aTable,
896 nsIContentHandle* aStackParent) {
897 MOZ_ASSERT(aChild, "Null child");
898 MOZ_ASSERT(aTable, "Null table");
899 MOZ_ASSERT(aStackParent, "Null stack parent");
901 if (mBuilder) {
902 nsresult rv = nsHtml5TreeOperation::FosterParent(
903 static_cast<nsIContent*>(aChild),
904 static_cast<nsIContent*>(aStackParent),
905 static_cast<nsIContent*>(aTable), mBuilder);
906 if (NS_FAILED(rv)) {
907 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
909 return;
912 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
913 if (MOZ_UNLIKELY(!treeOp)) {
914 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
915 return;
917 opFosterParent operation(aChild, aStackParent, aTable);
918 treeOp->Init(mozilla::AsVariant(operation));
921 void nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent,
922 char16_t* aBuffer, int32_t aStart,
923 int32_t aLength) {
924 MOZ_ASSERT(aBuffer, "Null buffer");
925 MOZ_ASSERT(aParent, "Null parent");
926 MOZ_ASSERT(!aStart, "aStart must always be zero.");
928 if (mBuilder) {
929 nsresult rv = nsHtml5TreeOperation::AppendText(
930 aBuffer, // XXX aStart always ignored???
931 aLength, static_cast<nsIContent*>(aParent), mBuilder);
932 if (NS_FAILED(rv)) {
933 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
935 return;
938 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
939 if (!bufferCopy) {
940 // Just assigning mBroken instead of generating tree op. The caller
941 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
942 mBroken = NS_ERROR_OUT_OF_MEMORY;
943 requestSuspension();
944 return;
947 memcpy(bufferCopy.get(), aBuffer, aLength * sizeof(char16_t));
949 if (mImportScanner.ShouldScan()) {
950 nsTArray<nsString> imports =
951 mImportScanner.Scan(mozilla::Span(aBuffer, aLength));
952 for (nsString& url : imports) {
953 mSpeculativeLoadQueue.AppendElement()->InitImportStyle(std::move(url));
957 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
958 if (MOZ_UNLIKELY(!treeOp)) {
959 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
960 return;
962 opAppendText operation(aParent, bufferCopy.release(), aLength);
963 treeOp->Init(mozilla::AsVariant(operation));
966 void nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent,
967 char16_t* aBuffer, int32_t aStart,
968 int32_t aLength) {
969 MOZ_ASSERT(aBuffer, "Null buffer");
970 MOZ_ASSERT(aParent, "Null parent");
971 MOZ_ASSERT(!aStart, "aStart must always be zero.");
973 if (mBuilder) {
974 nsresult rv = nsHtml5TreeOperation::AppendComment(
975 static_cast<nsIContent*>(aParent),
976 aBuffer, // XXX aStart always ignored???
977 aLength, mBuilder);
978 if (NS_FAILED(rv)) {
979 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
981 return;
984 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
985 if (!bufferCopy) {
986 // Just assigning mBroken instead of generating tree op. The caller
987 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
988 mBroken = NS_ERROR_OUT_OF_MEMORY;
989 requestSuspension();
990 return;
993 memcpy(bufferCopy.get(), aBuffer, aLength * sizeof(char16_t));
995 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
996 if (MOZ_UNLIKELY(!treeOp)) {
997 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
998 return;
1000 opAppendComment operation(aParent, bufferCopy.release(), aLength);
1001 treeOp->Init(mozilla::AsVariant(operation));
1004 void nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer,
1005 int32_t aStart,
1006 int32_t aLength) {
1007 MOZ_ASSERT(aBuffer, "Null buffer");
1008 MOZ_ASSERT(!aStart, "aStart must always be zero.");
1010 if (mBuilder) {
1011 nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument(
1012 aBuffer, // XXX aStart always ignored???
1013 aLength, mBuilder);
1014 if (NS_FAILED(rv)) {
1015 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1017 return;
1020 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
1021 if (!bufferCopy) {
1022 // Just assigning mBroken instead of generating tree op. The caller
1023 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
1024 mBroken = NS_ERROR_OUT_OF_MEMORY;
1025 requestSuspension();
1026 return;
1029 memcpy(bufferCopy.get(), aBuffer, aLength * sizeof(char16_t));
1031 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1032 if (MOZ_UNLIKELY(!treeOp)) {
1033 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1034 return;
1036 opAppendCommentToDocument data(bufferCopy.release(), aLength);
1037 treeOp->Init(mozilla::AsVariant(data));
1040 void nsHtml5TreeBuilder::addAttributesToElement(
1041 nsIContentHandle* aElement, nsHtml5HtmlAttributes* aAttributes) {
1042 MOZ_ASSERT(aElement, "Null element");
1043 MOZ_ASSERT(aAttributes, "Null attributes");
1045 if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
1046 return;
1049 if (mBuilder) {
1050 MOZ_ASSERT(
1051 aAttributes == tokenizer->GetAttributes(),
1052 "Using attribute other than the tokenizer's to add to body or html.");
1053 nsresult rv = nsHtml5TreeOperation::AddAttributes(
1054 static_cast<nsIContent*>(aElement), aAttributes, mBuilder);
1055 if (NS_FAILED(rv)) {
1056 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1058 return;
1061 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1062 if (MOZ_UNLIKELY(!treeOp)) {
1063 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1064 return;
1066 opAddAttributes opeation(aElement, aAttributes);
1067 treeOp->Init(mozilla::AsVariant(opeation));
1070 void nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement) {
1071 MOZ_ASSERT(aElement, "Null element");
1073 if (mBuilder) {
1074 nsHtml5TreeOperation::MarkMalformedIfScript(
1075 static_cast<nsIContent*>(aElement));
1076 return;
1079 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1080 if (MOZ_UNLIKELY(!treeOp)) {
1081 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1082 return;
1084 opMarkMalformedIfScript operation(aElement);
1085 treeOp->Init(mozilla::AsVariant(operation));
1088 void nsHtml5TreeBuilder::start(bool fragment) {
1089 mCurrentHtmlScriptCannotDocumentWriteOrBlock = false;
1090 #ifdef DEBUG
1091 mActive = true;
1092 #endif
1095 void nsHtml5TreeBuilder::end() {
1096 mOpQueue.Clear();
1097 #ifdef DEBUG
1098 mActive = false;
1099 #endif
1102 void nsHtml5TreeBuilder::appendDoctypeToDocument(nsAtom* aName,
1103 nsHtml5String aPublicId,
1104 nsHtml5String aSystemId) {
1105 MOZ_ASSERT(aName, "Null name");
1106 nsString publicId; // Not Auto, because using it to hold nsStringBuffer*
1107 nsString systemId; // Not Auto, because using it to hold nsStringBuffer*
1108 aPublicId.ToString(publicId);
1109 aSystemId.ToString(systemId);
1110 if (mBuilder) {
1111 nsresult rv = nsHtml5TreeOperation::AppendDoctypeToDocument(
1112 aName, publicId, systemId, mBuilder);
1113 if (NS_FAILED(rv)) {
1114 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1116 return;
1119 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1120 if (MOZ_UNLIKELY(!treeOp)) {
1121 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1122 return;
1124 opAppendDoctypeToDocument operation(aName, publicId, systemId);
1125 treeOp->Init(mozilla::AsVariant(operation));
1126 // nsXMLContentSink can flush here, but what's the point?
1127 // It can also interrupt here, but we can't.
1130 void nsHtml5TreeBuilder::elementPushed(int32_t aNamespace, nsAtom* aName,
1131 nsIContentHandle* aElement) {
1132 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML ||
1133 aNamespace == kNameSpaceID_SVG ||
1134 aNamespace == kNameSpaceID_MathML,
1135 "Element isn't HTML, SVG or MathML!");
1136 NS_ASSERTION(aName, "Element doesn't have local name!");
1137 NS_ASSERTION(aElement, "No element!");
1139 * The frame constructor uses recursive algorithms, so it can't deal with
1140 * arbitrarily deep trees. This is especially a problem on Windows where
1141 * the permitted depth of the runtime stack is rather small.
1143 * The following is a protection against author incompetence--not against
1144 * malice. There are other ways to make the DOM deep anyway.
1146 * The basic idea is that when the tree builder stack gets too deep,
1147 * append operations no longer append to the node that the HTML parsing
1148 * algorithm says they should but instead text nodes are append to the last
1149 * element that was seen before a magic tree builder stack threshold was
1150 * reached and element and comment nodes aren't appended to the DOM at all.
1152 * However, for security reasons, non-child descendant text nodes inside an
1153 * SVG script or style element should not become children. Also, non-cell
1154 * table elements shouldn't be used as surrogate parents for user experience
1155 * reasons.
1157 if (aNamespace != kNameSpaceID_XHTML) {
1158 return;
1160 if (aName == nsGkAtoms::body || aName == nsGkAtoms::frameset) {
1161 if (mBuilder) {
1162 // InnerHTML and DOMParser shouldn't start layout anyway
1163 return;
1165 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1166 if (MOZ_UNLIKELY(!treeOp)) {
1167 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1168 return;
1170 treeOp->Init(mozilla::AsVariant(opStartLayout()));
1171 return;
1173 if (nsIContent::RequiresDoneCreatingElement(kNameSpaceID_XHTML, aName)) {
1174 if (mBuilder) {
1175 nsHtml5TreeOperation::DoneCreatingElement(
1176 static_cast<nsIContent*>(aElement));
1177 } else {
1178 opDoneCreatingElement operation(aElement);
1179 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1181 return;
1183 if (mGenerateSpeculativeLoads && aName == nsGkAtoms::picture) {
1184 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1185 mSpeculativeLoadQueue.AppendElement()->InitOpenPicture();
1186 return;
1188 if (aName == nsGkAtoms::_template) {
1189 if (tokenizer->TemplatePushedOrHeadPopped()) {
1190 requestSuspension();
1195 void nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsAtom* aName,
1196 nsIContentHandle* aElement) {
1197 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML ||
1198 aNamespace == kNameSpaceID_SVG ||
1199 aNamespace == kNameSpaceID_MathML,
1200 "Element isn't HTML, SVG or MathML!");
1201 NS_ASSERTION(aName, "Element doesn't have local name!");
1202 NS_ASSERTION(aElement, "No element!");
1203 if (aNamespace == kNameSpaceID_MathML) {
1204 return;
1206 // we now have only SVG and HTML
1207 if (aName == nsGkAtoms::script) {
1208 if (mPreventScriptExecution) {
1209 if (mBuilder) {
1210 nsHtml5TreeOperation::PreventScriptExecution(
1211 static_cast<nsIContent*>(aElement));
1212 return;
1214 opPreventScriptExecution operation(aElement);
1215 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1216 return;
1218 if (mBuilder) {
1219 return;
1221 if (mCurrentHtmlScriptCannotDocumentWriteOrBlock) {
1222 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML,
1223 "Only HTML scripts may be async/defer.");
1224 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1225 if (MOZ_UNLIKELY(!treeOp)) {
1226 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1227 return;
1229 opRunScriptThatCannotDocumentWriteOrBlock operation(aElement);
1230 treeOp->Init(mozilla::AsVariant(operation));
1231 mCurrentHtmlScriptCannotDocumentWriteOrBlock = false;
1232 return;
1234 requestSuspension();
1235 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1236 if (MOZ_UNLIKELY(!treeOp)) {
1237 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1238 return;
1240 opRunScriptThatMayDocumentWriteOrBlock operation(aElement, nullptr);
1241 treeOp->Init(mozilla::AsVariant(operation));
1242 return;
1244 // Some nodes need DoneAddingChildren() called to initialize
1245 // properly (e.g. form state restoration).
1246 if (nsIContent::RequiresDoneAddingChildren(aNamespace, aName)) {
1247 if (mBuilder) {
1248 nsHtml5TreeOperation::DoneAddingChildren(
1249 static_cast<nsIContent*>(aElement));
1250 return;
1252 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1253 if (MOZ_UNLIKELY(!treeOp)) {
1254 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1255 return;
1257 opDoneAddingChildren operation(aElement);
1258 treeOp->Init(mozilla::AsVariant(operation));
1259 if (aNamespace == kNameSpaceID_XHTML && aName == nsGkAtoms::head) {
1260 if (tokenizer->TemplatePushedOrHeadPopped()) {
1261 requestSuspension();
1264 return;
1266 if (aName == nsGkAtoms::style ||
1267 (aNamespace == kNameSpaceID_XHTML && aName == nsGkAtoms::link)) {
1268 if (mBuilder) {
1269 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
1270 "Scripts must be blocked.");
1271 mBuilder->UpdateStyleSheet(static_cast<nsIContent*>(aElement));
1272 return;
1275 if (aName == nsGkAtoms::style) {
1276 nsTArray<nsString> imports = mImportScanner.Stop();
1277 for (nsString& url : imports) {
1278 mSpeculativeLoadQueue.AppendElement()->InitImportStyle(std::move(url));
1282 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1283 if (MOZ_UNLIKELY(!treeOp)) {
1284 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1285 return;
1287 opUpdateStyleSheet operation(aElement);
1288 treeOp->Init(mozilla::AsVariant(operation));
1289 return;
1291 if (aNamespace == kNameSpaceID_SVG) {
1292 if (aName == nsGkAtoms::svg) {
1293 if (!scriptingEnabled || mPreventScriptExecution) {
1294 return;
1296 if (mBuilder) {
1297 nsHtml5TreeOperation::SvgLoad(static_cast<nsIContent*>(aElement));
1298 return;
1300 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1301 if (MOZ_UNLIKELY(!treeOp)) {
1302 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1303 return;
1305 opSvgLoad operation(aElement);
1306 treeOp->Init(mozilla::AsVariant(operation));
1308 return;
1311 if (mGenerateSpeculativeLoads && aName == nsGkAtoms::picture) {
1312 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1313 mSpeculativeLoadQueue.AppendElement()->InitEndPicture();
1314 return;
1318 void nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf,
1319 int32_t aStart, int32_t aLength) {
1320 MOZ_RELEASE_ASSERT(charBufferLen + aLength <= charBuffer.length,
1321 "About to memcpy past the end of the buffer!");
1322 memcpy(charBuffer + charBufferLen, aBuf + aStart, sizeof(char16_t) * aLength);
1323 charBufferLen += aLength;
1326 // INT32_MAX is (2^31)-1. Therefore, the highest power-of-two that fits
1327 // is 2^30. Note that this is counting char16_t units. The underlying
1328 // bytes will be twice that, but they fit even in 32-bit size_t even
1329 // if a contiguous chunk of memory of that size is pretty unlikely to
1330 // be available on a 32-bit system.
1331 #define MAX_POWER_OF_TWO_IN_INT32 0x40000000
1333 bool nsHtml5TreeBuilder::EnsureBufferSpace(int32_t aLength) {
1334 // TODO: Unify nsHtml5Tokenizer::strBuf and nsHtml5TreeBuilder::charBuffer
1335 // so that this method becomes unnecessary.
1336 mozilla::CheckedInt<int32_t> worstCase(charBufferLen);
1337 worstCase += aLength;
1338 if (!worstCase.isValid()) {
1339 return false;
1341 if (worstCase.value() > MAX_POWER_OF_TWO_IN_INT32) {
1342 return false;
1344 if (!charBuffer) {
1345 if (worstCase.value() < MAX_POWER_OF_TWO_IN_INT32) {
1346 // Add one to round to the next power of two to avoid immediate
1347 // reallocation once there are a few characters in the buffer.
1348 worstCase += 1;
1350 charBuffer = jArray<char16_t, int32_t>::newFallibleJArray(
1351 mozilla::RoundUpPow2(worstCase.value()));
1352 if (!charBuffer) {
1353 return false;
1355 } else if (worstCase.value() > charBuffer.length) {
1356 jArray<char16_t, int32_t> newBuf =
1357 jArray<char16_t, int32_t>::newFallibleJArray(
1358 mozilla::RoundUpPow2(worstCase.value()));
1359 if (!newBuf) {
1360 return false;
1362 memcpy(newBuf, charBuffer, sizeof(char16_t) * size_t(charBufferLen));
1363 charBuffer = newBuf;
1365 return true;
1368 nsIContentHandle* nsHtml5TreeBuilder::AllocateContentHandle() {
1369 if (MOZ_UNLIKELY(mBuilder)) {
1370 MOZ_ASSERT_UNREACHABLE("Must never allocate a handle with builder.");
1371 return nullptr;
1373 if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) {
1374 mOldHandles.AppendElement(std::move(mHandles));
1375 mHandles = mozilla::MakeUnique<nsIContent*[]>(
1376 NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH);
1377 mHandlesUsed = 0;
1379 #ifdef DEBUG
1380 mHandles[mHandlesUsed] = reinterpret_cast<nsIContent*>(uintptr_t(0xC0DEDBAD));
1381 #endif
1382 return &mHandles[mHandlesUsed++];
1385 bool nsHtml5TreeBuilder::HasScriptThatMayDocumentWriteOrBlock() {
1386 uint32_t len = mOpQueue.Length();
1387 if (!len) {
1388 return false;
1390 return mOpQueue.ElementAt(len - 1).IsRunScriptThatMayDocumentWriteOrBlock();
1393 mozilla::Result<bool, nsresult> nsHtml5TreeBuilder::Flush(bool aDiscretionary) {
1394 if (MOZ_UNLIKELY(mBuilder)) {
1395 MOZ_ASSERT_UNREACHABLE("Must never flush with builder.");
1396 return false;
1398 if (NS_SUCCEEDED(mBroken)) {
1399 if (!aDiscretionary || !(charBufferLen && currentPtr >= 0 &&
1400 stack[currentPtr]->isFosterParenting())) {
1401 // Don't flush text on discretionary flushes if the current element on
1402 // the stack is a foster-parenting element and there's pending text,
1403 // because flushing in that case would make the tree shape dependent on
1404 // where the flush points fall.
1405 flushCharacters();
1407 FlushLoads();
1409 if (mOpSink) {
1410 bool hasOps = !mOpQueue.IsEmpty();
1411 if (hasOps) {
1412 // If the builder is broken and mOpQueue is not empty, there must be
1413 // one op and it must be eTreeOpMarkAsBroken.
1414 if (NS_FAILED(mBroken)) {
1415 MOZ_ASSERT(mOpQueue.Length() == 1,
1416 "Tree builder is broken with a non-empty op queue whose "
1417 "length isn't 1.");
1418 MOZ_ASSERT(mOpQueue[0].IsMarkAsBroken(),
1419 "Tree builder is broken but the op in queue is not marked "
1420 "as broken.");
1422 if (!mOpSink->MoveOpsFrom(mOpQueue)) {
1423 return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
1426 return hasOps;
1428 // no op sink: throw away ops
1429 mOpQueue.Clear();
1430 return false;
1433 void nsHtml5TreeBuilder::FlushLoads() {
1434 if (MOZ_UNLIKELY(mBuilder)) {
1435 MOZ_ASSERT_UNREACHABLE("Must never flush loads with builder.");
1436 return;
1438 if (!mSpeculativeLoadQueue.IsEmpty()) {
1439 mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
1443 void nsHtml5TreeBuilder::SetDocumentCharset(NotNull<const Encoding*> aEncoding,
1444 nsCharsetSource aCharsetSource,
1445 bool aCommitEncodingSpeculation) {
1446 MOZ_ASSERT(!mBuilder, "How did we call this with builder?");
1447 MOZ_ASSERT(mSpeculativeLoadStage,
1448 "How did we call this without a speculative load stage?");
1449 mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset(
1450 aEncoding, aCharsetSource, aCommitEncodingSpeculation);
1453 void nsHtml5TreeBuilder::UpdateCharsetSource(nsCharsetSource aCharsetSource) {
1454 MOZ_ASSERT(!mBuilder, "How did we call this with builder?");
1455 MOZ_ASSERT(mSpeculativeLoadStage,
1456 "How did we call this without a speculative load stage (even "
1457 "though we don't need it right here)?");
1458 if (mViewSource) {
1459 mViewSource->UpdateCharsetSource(aCharsetSource);
1460 return;
1462 opUpdateCharsetSource operation(aCharsetSource);
1463 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1466 void nsHtml5TreeBuilder::StreamEnded() {
1467 MOZ_ASSERT(!mBuilder, "Must not call StreamEnded with builder.");
1468 MOZ_ASSERT(!fragment, "Must not parse fragments off the main thread.");
1469 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1470 if (MOZ_UNLIKELY(!treeOp)) {
1471 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1472 return;
1474 treeOp->Init(mozilla::AsVariant(opStreamEnded()));
1477 void nsHtml5TreeBuilder::NeedsCharsetSwitchTo(
1478 NotNull<const Encoding*> aEncoding, int32_t aCharsetSource,
1479 int32_t aLineNumber) {
1480 if (MOZ_UNLIKELY(mBuilder)) {
1481 MOZ_ASSERT_UNREACHABLE("Must never switch charset with builder.");
1482 return;
1484 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1485 if (MOZ_UNLIKELY(!treeOp)) {
1486 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1487 return;
1489 opCharsetSwitchTo opeation(aEncoding, aCharsetSource, aLineNumber);
1490 treeOp->Init(mozilla::AsVariant(opeation));
1493 void nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId,
1494 bool aError,
1495 int32_t aLineNumber) {
1496 if (MOZ_UNLIKELY(mBuilder)) {
1497 MOZ_ASSERT_UNREACHABLE("Must never complain about charset with builder.");
1498 return;
1501 if (mSpeculativeLoadStage) {
1502 mSpeculativeLoadQueue.AppendElement()->InitMaybeComplainAboutCharset(
1503 aMsgId, aError, aLineNumber);
1504 } else {
1505 opMaybeComplainAboutCharset opeartion(const_cast<char*>(aMsgId), aError,
1506 aLineNumber);
1507 mOpQueue.AppendElement()->Init(mozilla::AsVariant(opeartion));
1511 void nsHtml5TreeBuilder::TryToEnableEncodingMenu() {
1512 if (MOZ_UNLIKELY(mBuilder)) {
1513 MOZ_ASSERT_UNREACHABLE("Must never disable encoding menu with builder.");
1514 return;
1516 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1517 NS_ASSERTION(treeOp, "Tree op allocation failed.");
1518 treeOp->Init(mozilla::AsVariant(opEnableEncodingMenu()));
1521 void nsHtml5TreeBuilder::AddSnapshotToScript(
1522 nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine) {
1523 if (MOZ_UNLIKELY(mBuilder)) {
1524 MOZ_ASSERT_UNREACHABLE("Must never use snapshots with builder.");
1525 return;
1527 MOZ_ASSERT(HasScriptThatMayDocumentWriteOrBlock(),
1528 "No script to add a snapshot to!");
1529 MOZ_ASSERT(aSnapshot, "Got null snapshot.");
1530 mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
1533 void nsHtml5TreeBuilder::DropHandles() {
1534 MOZ_ASSERT(!mBuilder, "Must not drop handles with builder.");
1535 mOldHandles.Clear();
1536 mHandlesUsed = 0;
1539 void nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv) {
1540 if (MOZ_UNLIKELY(mBuilder)) {
1541 MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
1542 return;
1544 mBroken = aRv;
1545 mOpQueue.Clear(); // Previous ops don't matter anymore
1546 opMarkAsBroken operation(aRv);
1547 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1550 void nsHtml5TreeBuilder::MarkAsBrokenFromPortability(nsresult aRv) {
1551 if (mBuilder) {
1552 MarkAsBrokenAndRequestSuspensionWithBuilder(aRv);
1553 return;
1555 mBroken = aRv;
1556 requestSuspension();
1559 void nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle) {
1560 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1562 startTag(nsHtml5ElementName::ELT_META,
1563 nsHtml5ViewSourceUtils::NewMetaViewportAttributes(), false);
1565 startTag(nsHtml5ElementName::ELT_TITLE,
1566 nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES, false);
1568 // XUL will add the "Source of: " prefix.
1569 uint32_t length = aTitle.Length();
1570 if (length > INT32_MAX) {
1571 length = INT32_MAX;
1573 characters(aTitle.get(), 0, (int32_t)length);
1574 endTag(nsHtml5ElementName::ELT_TITLE);
1576 startTag(nsHtml5ElementName::ELT_LINK,
1577 nsHtml5ViewSourceUtils::NewLinkAttributes(), false);
1579 startTag(nsHtml5ElementName::ELT_BODY,
1580 nsHtml5ViewSourceUtils::NewBodyAttributes(), false);
1582 StartPlainTextBody();
1585 void nsHtml5TreeBuilder::StartPlainText() {
1586 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1587 setForceNoQuirks(true);
1588 startTag(nsHtml5ElementName::ELT_LINK,
1589 nsHtml5PlainTextUtils::NewLinkAttributes(), false);
1591 startTag(nsHtml5ElementName::ELT_BODY,
1592 nsHtml5PlainTextUtils::NewBodyAttributes(), false);
1594 StartPlainTextBody();
1597 void nsHtml5TreeBuilder::StartPlainTextBody() {
1598 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1599 startTag(nsHtml5ElementName::ELT_PRE, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
1600 false);
1601 needToDropLF = false;
1604 // DocumentModeHandler
1605 void nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m) {
1606 if (mBuilder) {
1607 mBuilder->SetDocumentMode(m);
1608 return;
1610 if (mSpeculativeLoadStage) {
1611 mSpeculativeLoadQueue.AppendElement()->InitSetDocumentMode(m);
1612 return;
1614 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1615 if (MOZ_UNLIKELY(!treeOp)) {
1616 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1617 return;
1619 treeOp->Init(mozilla::AsVariant(m));
1622 nsIContentHandle* nsHtml5TreeBuilder::getDocumentFragmentForTemplate(
1623 nsIContentHandle* aTemplate) {
1624 if (mBuilder) {
1625 return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(
1626 static_cast<nsIContent*>(aTemplate));
1628 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1629 if (MOZ_UNLIKELY(!treeOp)) {
1630 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1631 return nullptr;
1633 nsIContentHandle* fragHandle = AllocateContentHandle();
1634 opGetDocumentFragmentForTemplate operation(aTemplate, fragHandle);
1635 treeOp->Init(mozilla::AsVariant(operation));
1636 return fragHandle;
1639 nsIContentHandle* nsHtml5TreeBuilder::getFormPointerForContext(
1640 nsIContentHandle* aContext) {
1641 MOZ_ASSERT(mBuilder, "Must have builder.");
1642 if (!aContext) {
1643 return nullptr;
1646 MOZ_ASSERT(NS_IsMainThread());
1648 // aContext must always be an element that already exists
1649 // in the document.
1650 nsIContent* contextNode = static_cast<nsIContent*>(aContext);
1651 nsIContent* currentAncestor = contextNode;
1653 // We traverse the ancestors of the context node to find the nearest
1654 // form pointer. This traversal is why aContext must not be an emtpy handle.
1655 nsIContent* nearestForm = nullptr;
1656 while (currentAncestor) {
1657 if (currentAncestor->IsHTMLElement(nsGkAtoms::form)) {
1658 nearestForm = currentAncestor;
1659 break;
1661 currentAncestor = currentAncestor->GetParent();
1664 if (!nearestForm) {
1665 return nullptr;
1668 return nearestForm;
1671 // Error reporting
1673 void nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter* aHighlighter) {
1674 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1675 mViewSource = aHighlighter;
1678 void nsHtml5TreeBuilder::errDeepTree() {
1679 if (MOZ_UNLIKELY(mViewSource)) {
1680 mViewSource->AddErrorToCurrentRun("errDeepTree");
1681 } else if (!mBuilder) {
1682 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1683 MOZ_ASSERT(treeOp, "Tree op allocation failed.");
1684 opMaybeComplainAboutDeepTree operation(tokenizer->getLineNumber());
1685 treeOp->Init(mozilla::AsVariant(operation));
1689 void nsHtml5TreeBuilder::errStrayStartTag(nsAtom* aName) {
1690 if (MOZ_UNLIKELY(mViewSource)) {
1691 mViewSource->AddErrorToCurrentRun("errStrayStartTag2", aName);
1695 void nsHtml5TreeBuilder::errStrayEndTag(nsAtom* aName) {
1696 if (MOZ_UNLIKELY(mViewSource)) {
1697 mViewSource->AddErrorToCurrentRun("errStrayEndTag", aName);
1701 void nsHtml5TreeBuilder::errUnclosedElements(int32_t aIndex, nsAtom* aName) {
1702 if (MOZ_UNLIKELY(mViewSource)) {
1703 mViewSource->AddErrorToCurrentRun("errUnclosedElements", aName);
1707 void nsHtml5TreeBuilder::errUnclosedElementsImplied(int32_t aIndex,
1708 nsAtom* aName) {
1709 if (MOZ_UNLIKELY(mViewSource)) {
1710 mViewSource->AddErrorToCurrentRun("errUnclosedElementsImplied", aName);
1714 void nsHtml5TreeBuilder::errUnclosedElementsCell(int32_t aIndex) {
1715 if (MOZ_UNLIKELY(mViewSource)) {
1716 mViewSource->AddErrorToCurrentRun("errUnclosedElementsCell");
1720 void nsHtml5TreeBuilder::errStrayDoctype() {
1721 if (MOZ_UNLIKELY(mViewSource)) {
1722 mViewSource->AddErrorToCurrentRun("errStrayDoctype");
1726 void nsHtml5TreeBuilder::errAlmostStandardsDoctype() {
1727 if (MOZ_UNLIKELY(mViewSource) && !forceNoQuirks) {
1728 mViewSource->AddErrorToCurrentRun("errAlmostStandardsDoctype");
1732 void nsHtml5TreeBuilder::errQuirkyDoctype() {
1733 if (MOZ_UNLIKELY(mViewSource) && !forceNoQuirks) {
1734 mViewSource->AddErrorToCurrentRun("errQuirkyDoctype");
1738 void nsHtml5TreeBuilder::errNonSpaceInTrailer() {
1739 if (MOZ_UNLIKELY(mViewSource)) {
1740 mViewSource->AddErrorToCurrentRun("errNonSpaceInTrailer");
1744 void nsHtml5TreeBuilder::errNonSpaceAfterFrameset() {
1745 if (MOZ_UNLIKELY(mViewSource)) {
1746 mViewSource->AddErrorToCurrentRun("errNonSpaceAfterFrameset");
1750 void nsHtml5TreeBuilder::errNonSpaceInFrameset() {
1751 if (MOZ_UNLIKELY(mViewSource)) {
1752 mViewSource->AddErrorToCurrentRun("errNonSpaceInFrameset");
1756 void nsHtml5TreeBuilder::errNonSpaceAfterBody() {
1757 if (MOZ_UNLIKELY(mViewSource)) {
1758 mViewSource->AddErrorToCurrentRun("errNonSpaceAfterBody");
1762 void nsHtml5TreeBuilder::errNonSpaceInColgroupInFragment() {
1763 if (MOZ_UNLIKELY(mViewSource)) {
1764 mViewSource->AddErrorToCurrentRun("errNonSpaceInColgroupInFragment");
1768 void nsHtml5TreeBuilder::errNonSpaceInNoscriptInHead() {
1769 if (MOZ_UNLIKELY(mViewSource)) {
1770 mViewSource->AddErrorToCurrentRun("errNonSpaceInNoscriptInHead");
1774 void nsHtml5TreeBuilder::errFooBetweenHeadAndBody(nsAtom* aName) {
1775 if (MOZ_UNLIKELY(mViewSource)) {
1776 mViewSource->AddErrorToCurrentRun("errFooBetweenHeadAndBody", aName);
1780 void nsHtml5TreeBuilder::errStartTagWithoutDoctype() {
1781 if (MOZ_UNLIKELY(mViewSource) && !forceNoQuirks) {
1782 mViewSource->AddErrorToCurrentRun("errStartTagWithoutDoctype");
1786 void nsHtml5TreeBuilder::errNoSelectInTableScope() {
1787 if (MOZ_UNLIKELY(mViewSource)) {
1788 mViewSource->AddErrorToCurrentRun("errNoSelectInTableScope");
1792 void nsHtml5TreeBuilder::errStartSelectWhereEndSelectExpected() {
1793 if (MOZ_UNLIKELY(mViewSource)) {
1794 mViewSource->AddErrorToCurrentRun("errStartSelectWhereEndSelectExpected");
1798 void nsHtml5TreeBuilder::errStartTagWithSelectOpen(nsAtom* aName) {
1799 if (MOZ_UNLIKELY(mViewSource)) {
1800 mViewSource->AddErrorToCurrentRun("errStartTagWithSelectOpen", aName);
1804 void nsHtml5TreeBuilder::errBadStartTagInNoscriptInHead(nsAtom* aName) {
1805 if (MOZ_UNLIKELY(mViewSource)) {
1806 mViewSource->AddErrorToCurrentRun("errBadStartTagInNoscriptInHead", aName);
1810 void nsHtml5TreeBuilder::errImage() {
1811 if (MOZ_UNLIKELY(mViewSource)) {
1812 mViewSource->AddErrorToCurrentRun("errImage");
1816 void nsHtml5TreeBuilder::errIsindex() {
1817 if (MOZ_UNLIKELY(mViewSource)) {
1818 mViewSource->AddErrorToCurrentRun("errIsindex");
1822 void nsHtml5TreeBuilder::errFooSeenWhenFooOpen(nsAtom* aName) {
1823 if (MOZ_UNLIKELY(mViewSource)) {
1824 mViewSource->AddErrorToCurrentRun("errFooSeenWhenFooOpen2", aName);
1828 void nsHtml5TreeBuilder::errHeadingWhenHeadingOpen() {
1829 if (MOZ_UNLIKELY(mViewSource)) {
1830 mViewSource->AddErrorToCurrentRun("errHeadingWhenHeadingOpen");
1834 void nsHtml5TreeBuilder::errFramesetStart() {
1835 if (MOZ_UNLIKELY(mViewSource)) {
1836 mViewSource->AddErrorToCurrentRun("errFramesetStart");
1840 void nsHtml5TreeBuilder::errNoCellToClose() {
1841 if (MOZ_UNLIKELY(mViewSource)) {
1842 mViewSource->AddErrorToCurrentRun("errNoCellToClose");
1846 void nsHtml5TreeBuilder::errStartTagInTable(nsAtom* aName) {
1847 if (MOZ_UNLIKELY(mViewSource)) {
1848 mViewSource->AddErrorToCurrentRun("errStartTagInTable", aName);
1852 void nsHtml5TreeBuilder::errFormWhenFormOpen() {
1853 if (MOZ_UNLIKELY(mViewSource)) {
1854 mViewSource->AddErrorToCurrentRun("errFormWhenFormOpen");
1858 void nsHtml5TreeBuilder::errTableSeenWhileTableOpen() {
1859 if (MOZ_UNLIKELY(mViewSource)) {
1860 mViewSource->AddErrorToCurrentRun("errTableSeenWhileTableOpen");
1864 void nsHtml5TreeBuilder::errStartTagInTableBody(nsAtom* aName) {
1865 if (MOZ_UNLIKELY(mViewSource)) {
1866 mViewSource->AddErrorToCurrentRun("errStartTagInTableBody", aName);
1870 void nsHtml5TreeBuilder::errEndTagSeenWithoutDoctype() {
1871 if (MOZ_UNLIKELY(mViewSource) && !forceNoQuirks) {
1872 mViewSource->AddErrorToCurrentRun("errEndTagSeenWithoutDoctype");
1876 void nsHtml5TreeBuilder::errEndTagAfterBody() {
1877 if (MOZ_UNLIKELY(mViewSource)) {
1878 mViewSource->AddErrorToCurrentRun("errEndTagAfterBody");
1882 void nsHtml5TreeBuilder::errEndTagSeenWithSelectOpen(nsAtom* aName) {
1883 if (MOZ_UNLIKELY(mViewSource)) {
1884 mViewSource->AddErrorToCurrentRun("errEndTagSeenWithSelectOpen", aName);
1888 void nsHtml5TreeBuilder::errGarbageInColgroup() {
1889 if (MOZ_UNLIKELY(mViewSource)) {
1890 mViewSource->AddErrorToCurrentRun("errGarbageInColgroup");
1894 void nsHtml5TreeBuilder::errEndTagBr() {
1895 if (MOZ_UNLIKELY(mViewSource)) {
1896 mViewSource->AddErrorToCurrentRun("errEndTagBr");
1900 void nsHtml5TreeBuilder::errNoElementToCloseButEndTagSeen(nsAtom* aName) {
1901 if (MOZ_UNLIKELY(mViewSource)) {
1902 mViewSource->AddErrorToCurrentRun("errNoElementToCloseButEndTagSeen",
1903 aName);
1907 void nsHtml5TreeBuilder::errHtmlStartTagInForeignContext(nsAtom* aName) {
1908 if (MOZ_UNLIKELY(mViewSource)) {
1909 mViewSource->AddErrorToCurrentRun("errHtmlStartTagInForeignContext", aName);
1913 void nsHtml5TreeBuilder::errNoTableRowToClose() {
1914 if (MOZ_UNLIKELY(mViewSource)) {
1915 mViewSource->AddErrorToCurrentRun("errNoTableRowToClose");
1919 void nsHtml5TreeBuilder::errNonSpaceInTable() {
1920 if (MOZ_UNLIKELY(mViewSource)) {
1921 mViewSource->AddErrorToCurrentRun("errNonSpaceInTable");
1925 void nsHtml5TreeBuilder::errUnclosedChildrenInRuby() {
1926 if (MOZ_UNLIKELY(mViewSource)) {
1927 mViewSource->AddErrorToCurrentRun("errUnclosedChildrenInRuby");
1931 void nsHtml5TreeBuilder::errStartTagSeenWithoutRuby(nsAtom* aName) {
1932 if (MOZ_UNLIKELY(mViewSource)) {
1933 mViewSource->AddErrorToCurrentRun("errStartTagSeenWithoutRuby", aName);
1937 void nsHtml5TreeBuilder::errSelfClosing() {
1938 if (MOZ_UNLIKELY(mViewSource)) {
1939 mViewSource->AddErrorToCurrentSlash("errSelfClosing");
1943 void nsHtml5TreeBuilder::errNoCheckUnclosedElementsOnStack() {
1944 if (MOZ_UNLIKELY(mViewSource)) {
1945 mViewSource->AddErrorToCurrentRun("errNoCheckUnclosedElementsOnStack");
1949 void nsHtml5TreeBuilder::errEndTagDidNotMatchCurrentOpenElement(
1950 nsAtom* aName, nsAtom* aOther) {
1951 if (MOZ_UNLIKELY(mViewSource)) {
1952 mViewSource->AddErrorToCurrentRun("errEndTagDidNotMatchCurrentOpenElement",
1953 aName, aOther);
1957 void nsHtml5TreeBuilder::errEndTagViolatesNestingRules(nsAtom* aName) {
1958 if (MOZ_UNLIKELY(mViewSource)) {
1959 mViewSource->AddErrorToCurrentRun("errEndTagViolatesNestingRules", aName);
1963 void nsHtml5TreeBuilder::errEndWithUnclosedElements(nsAtom* aName) {
1964 if (MOZ_UNLIKELY(mViewSource)) {
1965 mViewSource->AddErrorToCurrentRun("errEndWithUnclosedElements", aName);
1969 void nsHtml5TreeBuilder::errListUnclosedStartTags(int32_t aIgnored) {
1970 if (MOZ_UNLIKELY(mViewSource)) {
1971 mViewSource->AddErrorToCurrentRun("errListUnclosedStartTags");