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/. */
9 #include "nsHtml5AttributeName.h"
10 #include "nsHtml5HtmlAttributes.h"
11 #include "nsHtml5String.h"
12 #include "nsNetUtil.h"
13 #include "mozilla/dom/FetchPriority.h"
14 #include "mozilla/dom/ShadowRoot.h"
15 #include "mozilla/dom/ShadowRootBinding.h"
16 #include "mozilla/glean/GleanMetrics.h"
17 #include "mozilla/CheckedInt.h"
18 #include "mozilla/Likely.h"
19 #include "mozilla/StaticPrefs_dom.h"
20 #include "mozilla/StaticPrefs_network.h"
21 #include "mozilla/UniquePtr.h"
22 #include "mozilla/UniquePtrExtensions.h"
24 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder
* aBuilder
)
29 scriptingEnabled(false),
33 contextNamespace(kNameSpaceID_None
),
45 allowDeclarativeShadowRoots(false),
52 mSpeculativeLoadStage(nullptr),
54 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
55 mPreventScriptExecution(false),
56 mGenerateSpeculativeLoads(false),
57 mHasSeenImportMap(false)
63 MOZ_COUNT_CTOR(nsHtml5TreeBuilder
);
66 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink
* aOpSink
,
67 nsHtml5TreeOpStage
* aStage
,
68 bool aGenerateSpeculativeLoads
)
73 scriptingEnabled(false),
77 contextNamespace(kNameSpaceID_None
),
89 allowDeclarativeShadowRoots(false),
94 mHandles(new nsIContent
*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH
]),
96 mSpeculativeLoadStage(aStage
),
98 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
99 mPreventScriptExecution(false),
100 mGenerateSpeculativeLoads(aGenerateSpeculativeLoads
),
101 mHasSeenImportMap(false)
107 MOZ_ASSERT(!(!aStage
&& aGenerateSpeculativeLoads
),
108 "Must not generate speculative loads without a stage");
109 MOZ_COUNT_CTOR(nsHtml5TreeBuilder
);
112 nsHtml5TreeBuilder::~nsHtml5TreeBuilder() {
113 MOZ_COUNT_DTOR(nsHtml5TreeBuilder
);
114 NS_ASSERTION(!mActive
,
115 "nsHtml5TreeBuilder deleted without ever calling end() on it!");
119 static void getTypeString(nsHtml5String
& aType
, nsAString
& aTypeString
) {
120 aType
.ToString(aTypeString
);
122 // Since `typeString` after trimming and lowercasing is only checked
123 // for "module" and " importmap", we don't need to remember
124 // pre-trimming emptiness here.
126 // ASCII whitespace https://infra.spec.whatwg.org/#ascii-whitespace:
127 // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE.
128 static const char kASCIIWhitespace
[] = "\t\n\f\r ";
129 aTypeString
.Trim(kASCIIWhitespace
);
132 nsIContentHandle
* nsHtml5TreeBuilder::createElement(
133 int32_t aNamespace
, nsAtom
* aName
, nsHtml5HtmlAttributes
* aAttributes
,
134 nsIContentHandle
* aIntendedParent
, nsHtml5ContentCreatorFunction aCreator
) {
135 MOZ_ASSERT(aAttributes
, "Got null attributes.");
136 MOZ_ASSERT(aName
, "Got null name.");
137 MOZ_ASSERT(aNamespace
== kNameSpaceID_XHTML
||
138 aNamespace
== kNameSpaceID_SVG
||
139 aNamespace
== kNameSpaceID_MathML
,
143 nsIContent
* intendedParent
=
144 aIntendedParent
? static_cast<nsIContent
*>(aIntendedParent
) : nullptr;
146 // intendedParent == nullptr is a special case where the
147 // intended parent is the document.
148 nsNodeInfoManager
* nodeInfoManager
=
149 intendedParent
? intendedParent
->OwnerDoc()->NodeInfoManager()
150 : mBuilder
->GetNodeInfoManager();
153 if (aNamespace
== kNameSpaceID_XHTML
) {
154 elem
= nsHtml5TreeOperation::CreateHTMLElement(
155 aName
, aAttributes
, mozilla::dom::FROM_PARSER_FRAGMENT
,
156 nodeInfoManager
, mBuilder
, aCreator
.html
);
157 } else if (aNamespace
== kNameSpaceID_SVG
) {
158 elem
= nsHtml5TreeOperation::CreateSVGElement(
159 aName
, aAttributes
, mozilla::dom::FROM_PARSER_FRAGMENT
,
160 nodeInfoManager
, mBuilder
, aCreator
.svg
);
162 MOZ_ASSERT(aNamespace
== kNameSpaceID_MathML
);
163 elem
= nsHtml5TreeOperation::CreateMathMLElement(
164 aName
, aAttributes
, nodeInfoManager
, mBuilder
);
166 if (MOZ_UNLIKELY(aAttributes
!= tokenizer
->GetAttributes() &&
167 aAttributes
!= nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES
)) {
173 nsIContentHandle
* content
= AllocateContentHandle();
174 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
175 if (MOZ_UNLIKELY(!treeOp
)) {
176 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
180 if (aNamespace
== kNameSpaceID_XHTML
) {
181 opCreateHTMLElement
opeation(
182 content
, aName
, aAttributes
, aCreator
.html
, aIntendedParent
,
183 (!!mSpeculativeLoadStage
) ? mozilla::dom::FROM_PARSER_NETWORK
184 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE
);
185 treeOp
->Init(mozilla::AsVariant(opeation
));
186 } else if (aNamespace
== kNameSpaceID_SVG
) {
187 opCreateSVGElement
operation(
188 content
, aName
, aAttributes
, aCreator
.svg
, aIntendedParent
,
189 (!!mSpeculativeLoadStage
) ? mozilla::dom::FROM_PARSER_NETWORK
190 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE
);
191 treeOp
->Init(mozilla::AsVariant(operation
));
193 // kNameSpaceID_MathML
194 opCreateMathMLElement
operation(content
, aName
, aAttributes
,
196 treeOp
->Init(mozilla::AsVariant(operation
));
199 // mSpeculativeLoadStage is non-null only in the off-the-main-thread
200 // tree builder, which handles the network stream
202 // Start wall of code for speculative loading and line numbers
204 if (mGenerateSpeculativeLoads
&& mode
!= IN_TEMPLATE
) {
205 switch (aNamespace
) {
206 case kNameSpaceID_XHTML
:
207 if (nsGkAtoms::img
== aName
) {
208 nsHtml5String loading
=
209 aAttributes
->getValue(nsHtml5AttributeName::ATTR_LOADING
);
210 if (!loading
.LowerCaseEqualsASCII("lazy")) {
212 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SRC
);
213 nsHtml5String srcset
=
214 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SRCSET
);
215 nsHtml5String crossOrigin
=
216 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN
);
217 nsHtml5String referrerPolicy
= aAttributes
->getValue(
218 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
219 nsHtml5String sizes
=
220 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SIZES
);
221 nsHtml5String fetchPriority
=
222 aAttributes
->getValue(nsHtml5AttributeName::ATTR_FETCHPRIORITY
);
223 mSpeculativeLoadQueue
.AppendElement()->InitImage(
224 url
, crossOrigin
, /* aMedia = */ nullptr, referrerPolicy
,
225 srcset
, sizes
, false, fetchPriority
);
227 } else if (nsGkAtoms::source
== aName
) {
228 nsHtml5String srcset
=
229 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SRCSET
);
230 // Sources without srcset cannot be selected. The source could also be
231 // for a media element, but in that context doesn't use srcset. See
232 // comments in nsHtml5SpeculativeLoad.h about <picture> preloading
234 nsHtml5String sizes
=
235 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SIZES
);
237 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
238 nsHtml5String media
=
239 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MEDIA
);
240 mSpeculativeLoadQueue
.AppendElement()->InitPictureSource(
241 srcset
, sizes
, type
, media
);
243 } else if (nsGkAtoms::script
== aName
) {
244 nsHtml5TreeOperation
* treeOp
=
245 mOpQueue
.AppendElement(mozilla::fallible
);
246 if (MOZ_UNLIKELY(!treeOp
)) {
247 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
248 NS_ERROR_OUT_OF_MEMORY
);
251 opSetScriptLineAndColumnNumberAndFreeze
operation(
252 content
, tokenizer
->getLineNumber(),
253 // NOTE: tokenizer->getColumnNumber() points '>'.
254 tokenizer
->getColumnNumber() + 1);
255 treeOp
->Init(mozilla::AsVariant(operation
));
258 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
259 nsAutoString typeString
;
260 getTypeString(type
, typeString
);
262 bool isModule
= typeString
.LowerCaseEqualsASCII("module");
263 bool importmap
= typeString
.LowerCaseEqualsASCII("importmap");
267 aAttributes
->contains(nsHtml5AttributeName::ATTR_NOMODULE
);
269 // For microtask semantics, we need to queue either
270 // `opRunScriptThatMayDocumentWriteOrBlock` or
271 // `opRunScriptThatCannotDocumentWriteOrBlock` for every script
272 // element--even ones that we already know won't run.
273 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` controls which
274 // kind of operation is used for an HTML script, and this is the
275 // place where `mCurrentHtmlScriptCannotDocumentWriteOrBlock`
276 // needs to be set correctly.
278 // Non-async, non-defer classic scripts that will run MUST use
279 // `opRunScriptThatMayDocumentWriteOrBlock` in order to run
280 // the more complex code that
281 // 1. is able to resume the HTML parse after a parser-blocking
282 // scripts no longer blocks the parser
283 // 2. is able to receive more content to parse on the main thread
284 // via document.write
285 // 3. is able to throw away off-the-main-thread parsing results
286 // if what's document.written on the main thread invalidates
289 // Async and defer classic scripts as well as module scripts and
290 // importmaps MUST use `opRunScriptThatCannotDocumentWriteOrBlock`.
291 // This is necessary particularly because the relevant main-thread
292 // code assumes it doesn't need to deal with resuming the HTML
293 // parse some time afterwards, so using a tree operation with
294 // mismatching expectations regarding that responsibility may
295 // cause the HTML parse to stall.
297 // Various scripts that won't actually run work with either type
298 // of tree op in the sense that the HTML parse won't stall.
299 // However, in the case where a script cannot block or insert
300 // data to the HTML parser via document.write, unnecessary use
301 // of `opRunScriptThatMayDocumentWriteOrBlock` instead of
302 // `opRunScriptThatCannotDocumentWriteOrBlock` causes the HTML
303 // parser to enter the speculative mode when doing so isn't
304 // actually required.
306 // Ideally, we would check for `type`/`language` attribute
307 // combinations that are known to cause non-execution as well as
308 // ScriptLoader::IsScriptEventHandler equivalent. That way, we
309 // wouldn't unnecessarily speculate after scripts that won't
310 // execute. https://bugzilla.mozilla.org/show_bug.cgi?id=1848311
313 // If we see an importmap, we don't want to later start speculative
314 // loads for modulepreloads, since such load might finish before
315 // the importmap is created. This also applies to module scripts so
316 // that any modulepreload integrity checks can be performed before
317 // the modules scripts are loaded.
318 // This state is not part of speculation rollback: If an importmap
319 // is seen speculatively and the speculation is rolled back, the
320 // importmap is still considered seen.
321 // TODO: Sync importmap seenness between the main thread and the
323 // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312
324 mHasSeenImportMap
= true;
327 aAttributes
->getValue(nsHtml5AttributeName::ATTR_SRC
);
329 async
= aAttributes
->contains(nsHtml5AttributeName::ATTR_ASYNC
);
330 defer
= aAttributes
->contains(nsHtml5AttributeName::ATTR_DEFER
);
331 if ((isModule
&& !mHasSeenImportMap
) ||
332 (!isModule
&& !importmap
&& !nomodule
)) {
333 nsHtml5String charset
=
334 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CHARSET
);
335 nsHtml5String crossOrigin
=
336 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN
);
337 nsHtml5String nonce
=
338 aAttributes
->getValue(nsHtml5AttributeName::ATTR_NONCE
);
339 nsHtml5String fetchPriority
= aAttributes
->getValue(
340 nsHtml5AttributeName::ATTR_FETCHPRIORITY
);
341 nsHtml5String integrity
=
342 aAttributes
->getValue(nsHtml5AttributeName::ATTR_INTEGRITY
);
343 nsHtml5String referrerPolicy
= aAttributes
->getValue(
344 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
345 mSpeculativeLoadQueue
.AppendElement()->InitScript(
346 url
, charset
, type
, crossOrigin
, /* aMedia = */ nullptr,
347 nonce
, fetchPriority
, integrity
, referrerPolicy
,
348 mode
== nsHtml5TreeBuilder::IN_HEAD
, async
, defer
, false);
351 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` MUST be computed to
352 // match the ScriptLoader-perceived kind of the script regardless of
353 // enqueuing a speculative load. Scripts with the `nomodule` attribute
354 // never block or document.write: Either the attribute prevents a
355 // classic script execution or is ignored on a module script or
357 mCurrentHtmlScriptCannotDocumentWriteOrBlock
=
358 isModule
|| importmap
|| async
|| defer
|| nomodule
;
359 } else if (nsGkAtoms::link
== aName
) {
361 aAttributes
->getValue(nsHtml5AttributeName::ATTR_REL
);
362 // Not splitting on space here is bogus but the old parser didn't even
363 // do a case-insensitive check.
365 if (rel
.LowerCaseEqualsASCII("stylesheet")) {
367 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
369 nsHtml5String charset
=
370 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CHARSET
);
371 nsHtml5String crossOrigin
= aAttributes
->getValue(
372 nsHtml5AttributeName::ATTR_CROSSORIGIN
);
373 nsHtml5String nonce
=
374 aAttributes
->getValue(nsHtml5AttributeName::ATTR_NONCE
);
375 nsHtml5String integrity
=
376 aAttributes
->getValue(nsHtml5AttributeName::ATTR_INTEGRITY
);
377 nsHtml5String referrerPolicy
= aAttributes
->getValue(
378 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
379 nsHtml5String media
=
380 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MEDIA
);
381 nsHtml5String fetchPriority
= aAttributes
->getValue(
382 nsHtml5AttributeName::ATTR_FETCHPRIORITY
);
383 mSpeculativeLoadQueue
.AppendElement()->InitStyle(
384 url
, charset
, crossOrigin
, media
, referrerPolicy
, nonce
,
385 integrity
, false, fetchPriority
);
387 } else if (rel
.LowerCaseEqualsASCII("preconnect")) {
389 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
391 nsHtml5String crossOrigin
= aAttributes
->getValue(
392 nsHtml5AttributeName::ATTR_CROSSORIGIN
);
393 mSpeculativeLoadQueue
.AppendElement()->InitPreconnect(
396 } else if (rel
.LowerCaseEqualsASCII("preload")) {
398 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
401 aAttributes
->getValue(nsHtml5AttributeName::ATTR_AS
);
402 nsHtml5String charset
=
403 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CHARSET
);
404 nsHtml5String crossOrigin
= aAttributes
->getValue(
405 nsHtml5AttributeName::ATTR_CROSSORIGIN
);
406 nsHtml5String nonce
=
407 aAttributes
->getValue(nsHtml5AttributeName::ATTR_NONCE
);
408 nsHtml5String integrity
=
409 aAttributes
->getValue(nsHtml5AttributeName::ATTR_INTEGRITY
);
410 nsHtml5String referrerPolicy
= aAttributes
->getValue(
411 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
412 nsHtml5String media
=
413 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MEDIA
);
414 nsHtml5String fetchPriority
= aAttributes
->getValue(
415 nsHtml5AttributeName::ATTR_FETCHPRIORITY
);
417 // Note that respective speculative loaders for scripts and
418 // styles check all additional attributes to be equal to use the
419 // speculative load. So, if any of them is specified and the
420 // preload has to take the expected effect, those attributes
421 // must also be specified on the actual tag to use the preload.
422 // Omitting an attribute on both will make the values equal
423 // (empty) and thus use the preload.
424 if (as
.LowerCaseEqualsASCII("script")) {
426 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
427 mSpeculativeLoadQueue
.AppendElement()->InitScript(
428 url
, charset
, type
, crossOrigin
, media
, nonce
,
429 /* aFetchPriority */ fetchPriority
, integrity
,
430 referrerPolicy
, mode
== nsHtml5TreeBuilder::IN_HEAD
,
432 } else if (as
.LowerCaseEqualsASCII("style")) {
433 mSpeculativeLoadQueue
.AppendElement()->InitStyle(
434 url
, charset
, crossOrigin
, media
, referrerPolicy
, nonce
,
435 integrity
, true, fetchPriority
);
436 } else if (as
.LowerCaseEqualsASCII("image")) {
437 nsHtml5String srcset
= aAttributes
->getValue(
438 nsHtml5AttributeName::ATTR_IMAGESRCSET
);
439 nsHtml5String sizes
= aAttributes
->getValue(
440 nsHtml5AttributeName::ATTR_IMAGESIZES
);
441 mSpeculativeLoadQueue
.AppendElement()->InitImage(
442 url
, crossOrigin
, media
, referrerPolicy
, srcset
, sizes
,
443 true, fetchPriority
);
444 } else if (as
.LowerCaseEqualsASCII("font")) {
445 mSpeculativeLoadQueue
.AppendElement()->InitFont(
446 url
, crossOrigin
, media
, referrerPolicy
, fetchPriority
);
447 } else if (as
.LowerCaseEqualsASCII("fetch")) {
448 mSpeculativeLoadQueue
.AppendElement()->InitFetch(
449 url
, crossOrigin
, media
, referrerPolicy
, fetchPriority
);
451 // Other "as" values will be supported later.
453 } else if (mozilla::StaticPrefs::network_modulepreload() &&
454 rel
.LowerCaseEqualsASCII("modulepreload") &&
455 !mHasSeenImportMap
) {
457 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
458 if (url
&& url
.Length() != 0) {
460 aAttributes
->getValue(nsHtml5AttributeName::ATTR_AS
);
461 nsAutoString asString
;
462 as
.ToString(asString
);
463 if (mozilla::net::IsScriptLikeOrInvalid(asString
)) {
464 nsHtml5String charset
=
465 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CHARSET
);
466 RefPtr
<nsAtom
> moduleType
= nsGkAtoms::_module
;
468 nsHtml5String::FromAtom(moduleType
.forget());
469 nsHtml5String crossOrigin
= aAttributes
->getValue(
470 nsHtml5AttributeName::ATTR_CROSSORIGIN
);
471 nsHtml5String media
=
472 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MEDIA
);
473 nsHtml5String nonce
=
474 aAttributes
->getValue(nsHtml5AttributeName::ATTR_NONCE
);
475 nsHtml5String integrity
= aAttributes
->getValue(
476 nsHtml5AttributeName::ATTR_INTEGRITY
);
477 nsHtml5String referrerPolicy
= aAttributes
->getValue(
478 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
479 nsHtml5String fetchPriority
= aAttributes
->getValue(
480 nsHtml5AttributeName::ATTR_FETCHPRIORITY
);
482 mSpeculativeLoadQueue
.AppendElement()->InitScript(
483 url
, charset
, type
, crossOrigin
, media
, nonce
,
484 /* aFetchPriority */ fetchPriority
, integrity
,
485 referrerPolicy
, mode
== nsHtml5TreeBuilder::IN_HEAD
,
491 } else if (nsGkAtoms::video
== aName
) {
493 aAttributes
->getValue(nsHtml5AttributeName::ATTR_POSTER
);
495 // Fetch priority is not supported for video. Nullptr will map to
497 // (https://html.spec.whatwg.org/#fetch-priority-attribute).
498 auto fetchPriority
= nullptr;
500 mSpeculativeLoadQueue
.AppendElement()->InitImage(
501 url
, nullptr, nullptr, nullptr, nullptr, nullptr, false,
504 } else if (nsGkAtoms::style
== aName
) {
505 mImportScanner
.Start();
506 nsHtml5TreeOperation
* treeOp
=
507 mOpQueue
.AppendElement(mozilla::fallible
);
508 if (MOZ_UNLIKELY(!treeOp
)) {
509 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
510 NS_ERROR_OUT_OF_MEMORY
);
513 opSetStyleLineNumber
operation(content
, tokenizer
->getLineNumber());
514 treeOp
->Init(mozilla::AsVariant(operation
));
515 } else if (nsGkAtoms::html
== aName
) {
517 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MANIFEST
);
518 mSpeculativeLoadQueue
.AppendElement()->InitManifest(url
);
519 } else if (nsGkAtoms::base
== aName
) {
521 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
523 mSpeculativeLoadQueue
.AppendElement()->InitBase(url
);
525 } else if (nsGkAtoms::meta
== aName
) {
526 if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString(
527 "content-security-policy",
528 aAttributes
->getValue(
529 nsHtml5AttributeName::ATTR_HTTP_EQUIV
))) {
531 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CONTENT
);
533 mSpeculativeLoadQueue
.AppendElement()->InitMetaCSP(csp
);
535 } else if (nsHtml5Portability::
536 lowerCaseLiteralEqualsIgnoreAsciiCaseString(
538 aAttributes
->getValue(
539 nsHtml5AttributeName::ATTR_NAME
))) {
540 nsHtml5String referrerPolicy
=
541 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CONTENT
);
542 if (referrerPolicy
) {
543 mSpeculativeLoadQueue
.AppendElement()->InitMetaReferrerPolicy(
549 case kNameSpaceID_SVG
:
550 if (nsGkAtoms::image
== aName
) {
552 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
554 url
= aAttributes
->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF
);
557 // Currently SVG's `<image>` element lacks support for
558 // `fetchpriority`, see bug 1847712. Hence passing nullptr which
559 // maps to the auto state
560 // (https://html.spec.whatwg.org/#fetch-priority-attribute).
561 auto fetchPriority
= nullptr;
563 mSpeculativeLoadQueue
.AppendElement()->InitImage(
564 url
, nullptr, nullptr, nullptr, nullptr, nullptr, false,
567 } else if (nsGkAtoms::script
== aName
) {
568 nsHtml5TreeOperation
* treeOp
=
569 mOpQueue
.AppendElement(mozilla::fallible
);
570 if (MOZ_UNLIKELY(!treeOp
)) {
571 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
572 NS_ERROR_OUT_OF_MEMORY
);
575 opSetScriptLineAndColumnNumberAndFreeze
operation(
576 content
, tokenizer
->getLineNumber(),
577 // NOTE: tokenizer->getColumnNumber() points '>'.
578 tokenizer
->getColumnNumber() + 1);
579 treeOp
->Init(mozilla::AsVariant(operation
));
582 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
583 nsAutoString typeString
;
584 getTypeString(type
, typeString
);
586 bool isModule
= typeString
.LowerCaseEqualsASCII("module");
587 bool importmap
= typeString
.LowerCaseEqualsASCII("importmap");
592 // If we see an importmap, we don't want to later start speculative
593 // loads for modulepreloads, since such load might finish before
594 // the importmap is created. This also applies to module scripts so
595 // that any modulepreload integrity checks can be performed before
596 // the modules scripts are loaded.
597 // This state is not part of speculation rollback: If an importmap
598 // is seen speculatively and the speculation is rolled back, the
599 // importmap is still considered seen.
600 // TODO: Sync importmap seenness between the main thread and the
602 // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312
603 mHasSeenImportMap
= true;
606 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
608 url
= aAttributes
->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF
);
611 async
= aAttributes
->contains(nsHtml5AttributeName::ATTR_ASYNC
);
612 defer
= aAttributes
->contains(nsHtml5AttributeName::ATTR_DEFER
);
613 if ((isModule
&& !mHasSeenImportMap
) || (!isModule
&& !importmap
)) {
615 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
616 nsHtml5String crossOrigin
=
617 aAttributes
->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN
);
618 nsHtml5String nonce
=
619 aAttributes
->getValue(nsHtml5AttributeName::ATTR_NONCE
);
620 nsHtml5String integrity
=
621 aAttributes
->getValue(nsHtml5AttributeName::ATTR_INTEGRITY
);
622 nsHtml5String referrerPolicy
= aAttributes
->getValue(
623 nsHtml5AttributeName::ATTR_REFERRERPOLICY
);
625 // Bug 1847712: SVG's `<script>` element doesn't support
626 // `fetchpriority` yet.
627 // Use the empty string and rely on the
628 // "invalid value default" state being used later.
629 // Compared to using a non-empty string, this doesn't
630 // require calling `Release()` for the string.
631 nsHtml5String fetchPriority
= nsHtml5String::EmptyString();
633 mSpeculativeLoadQueue
.AppendElement()->InitScript(
634 url
, nullptr, type
, crossOrigin
, /* aMedia = */ nullptr,
635 nonce
, fetchPriority
, integrity
, referrerPolicy
,
636 mode
== nsHtml5TreeBuilder::IN_HEAD
, async
, defer
, false);
639 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` MUST be computed to
640 // match the ScriptLoader-perceived kind of the script regardless of
641 // enqueuing a speculative load. Either the attribute prevents a
642 // classic script execution or is ignored on a module script.
643 mCurrentHtmlScriptCannotDocumentWriteOrBlock
=
644 isModule
|| importmap
|| async
|| defer
;
645 } else if (nsGkAtoms::style
== aName
) {
646 mImportScanner
.Start();
647 nsHtml5TreeOperation
* treeOp
=
648 mOpQueue
.AppendElement(mozilla::fallible
);
649 if (MOZ_UNLIKELY(!treeOp
)) {
650 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
651 NS_ERROR_OUT_OF_MEMORY
);
654 opSetStyleLineNumber
operation(content
, tokenizer
->getLineNumber());
655 treeOp
->Init(mozilla::AsVariant(operation
));
659 } else if (aNamespace
!= kNameSpaceID_MathML
) {
660 // No speculative loader--just line numbers and defer/async check
661 if (nsGkAtoms::style
== aName
) {
662 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
663 if (MOZ_UNLIKELY(!treeOp
)) {
664 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
667 opSetStyleLineNumber
operation(content
, tokenizer
->getLineNumber());
668 treeOp
->Init(mozilla::AsVariant(operation
));
669 } else if (nsGkAtoms::script
== aName
) {
670 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
671 if (MOZ_UNLIKELY(!treeOp
)) {
672 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
675 opSetScriptLineAndColumnNumberAndFreeze
operation(
676 content
, tokenizer
->getLineNumber(),
677 // NOTE: tokenizer->getColumnNumber() points '>'.
678 tokenizer
->getColumnNumber() + 1);
679 treeOp
->Init(mozilla::AsVariant(operation
));
680 if (aNamespace
== kNameSpaceID_XHTML
) {
681 // Although we come here in cases where the value of
682 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` doesn't actually
683 // matter, we also come here when parsing document.written content on
684 // the main thread. In that case, IT MATTERS that
685 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` is set correctly,
686 // so let's just always set it correctly even if it a bit of wasted work
687 // in the scenarios where no scripts execute and it doesn't matter.
689 // See the comments around generating speculative loads for HTML scripts
690 // elements for the details of when
691 // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` needs to be set to
692 // `true` and when to `false`.
695 aAttributes
->getValue(nsHtml5AttributeName::ATTR_TYPE
);
696 nsAutoString typeString
;
697 getTypeString(type
, typeString
);
699 mCurrentHtmlScriptCannotDocumentWriteOrBlock
=
700 typeString
.LowerCaseEqualsASCII("module") ||
701 typeString
.LowerCaseEqualsASCII("nomodule") ||
702 typeString
.LowerCaseEqualsASCII("importmap");
704 if (!mCurrentHtmlScriptCannotDocumentWriteOrBlock
&&
705 aAttributes
->contains(nsHtml5AttributeName::ATTR_SRC
)) {
706 mCurrentHtmlScriptCannotDocumentWriteOrBlock
=
707 (aAttributes
->contains(nsHtml5AttributeName::ATTR_ASYNC
) ||
708 aAttributes
->contains(nsHtml5AttributeName::ATTR_DEFER
));
711 } else if (aNamespace
== kNameSpaceID_XHTML
) {
712 if (nsGkAtoms::html
== aName
) {
714 aAttributes
->getValue(nsHtml5AttributeName::ATTR_MANIFEST
);
715 nsHtml5TreeOperation
* treeOp
=
716 mOpQueue
.AppendElement(mozilla::fallible
);
717 if (MOZ_UNLIKELY(!treeOp
)) {
718 MarkAsBrokenAndRequestSuspensionWithoutBuilder(
719 NS_ERROR_OUT_OF_MEMORY
);
724 urlString
; // Not Auto, because using it to hold nsStringBuffer*
725 url
.ToString(urlString
);
726 opProcessOfflineManifest
operation(ToNewUnicode(urlString
));
727 treeOp
->Init(mozilla::AsVariant(operation
));
729 opProcessOfflineManifest
operation(ToNewUnicode(u
""_ns
));
730 treeOp
->Init(mozilla::AsVariant(operation
));
732 } else if (nsGkAtoms::base
== aName
&& mViewSource
) {
734 aAttributes
->getValue(nsHtml5AttributeName::ATTR_HREF
);
736 mViewSource
->AddBase(url
);
742 // End wall of code for speculative loading
747 nsIContentHandle
* nsHtml5TreeBuilder::createElement(
748 int32_t aNamespace
, nsAtom
* aName
, nsHtml5HtmlAttributes
* aAttributes
,
749 nsIContentHandle
* aFormElement
, nsIContentHandle
* aIntendedParent
,
750 nsHtml5ContentCreatorFunction aCreator
) {
751 nsIContentHandle
* content
=
752 createElement(aNamespace
, aName
, aAttributes
, aIntendedParent
, aCreator
);
755 nsHtml5TreeOperation::SetFormElement(
756 static_cast<nsIContent
*>(content
),
757 static_cast<nsIContent
*>(aFormElement
),
758 static_cast<nsIContent
*>(aIntendedParent
));
760 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
761 if (MOZ_UNLIKELY(!treeOp
)) {
762 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
765 opSetFormElement
operation(content
, aFormElement
, aIntendedParent
);
766 treeOp
->Init(mozilla::AsVariant(operation
));
772 nsIContentHandle
* nsHtml5TreeBuilder::createHtmlElementSetAsRoot(
773 nsHtml5HtmlAttributes
* aAttributes
) {
774 nsHtml5ContentCreatorFunction creator
;
775 // <html> uses NS_NewHTMLSharedElement creator
776 creator
.html
= NS_NewHTMLSharedElement
;
777 nsIContentHandle
* content
= createElement(kNameSpaceID_XHTML
, nsGkAtoms::html
,
778 aAttributes
, nullptr, creator
);
780 nsresult rv
= nsHtml5TreeOperation::AppendToDocument(
781 static_cast<nsIContent
*>(content
), mBuilder
);
783 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
786 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
787 if (MOZ_UNLIKELY(!treeOp
)) {
788 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
791 opAppendToDocument
operation(content
);
792 treeOp
->Init(mozilla::AsVariant(operation
));
797 nsIContentHandle
* nsHtml5TreeBuilder::createAndInsertFosterParentedElement(
798 int32_t aNamespace
, nsAtom
* aName
, nsHtml5HtmlAttributes
* aAttributes
,
799 nsIContentHandle
* aFormElement
, nsIContentHandle
* aTable
,
800 nsIContentHandle
* aStackParent
, nsHtml5ContentCreatorFunction aCreator
) {
801 MOZ_ASSERT(aTable
, "Null table");
802 MOZ_ASSERT(aStackParent
, "Null stack parent");
805 // Get the foster parent to use as the intended parent when creating
806 // the child element.
807 nsIContent
* fosterParent
= nsHtml5TreeOperation::GetFosterParent(
808 static_cast<nsIContent
*>(aTable
),
809 static_cast<nsIContent
*>(aStackParent
));
811 nsIContentHandle
* child
= createElement(
812 aNamespace
, aName
, aAttributes
, aFormElement
, fosterParent
, aCreator
);
814 insertFosterParentedChild(child
, aTable
, aStackParent
);
819 // Tree op to get the foster parent that we use as the intended parent
820 // when creating the child element.
821 nsHtml5TreeOperation
* fosterParentTreeOp
= mOpQueue
.AppendElement();
822 NS_ASSERTION(fosterParentTreeOp
, "Tree op allocation failed.");
823 nsIContentHandle
* fosterParentHandle
= AllocateContentHandle();
824 opGetFosterParent
operation(aTable
, aStackParent
, fosterParentHandle
);
825 fosterParentTreeOp
->Init(mozilla::AsVariant(operation
));
827 // Create the element with the correct intended parent.
828 nsIContentHandle
* child
=
829 createElement(aNamespace
, aName
, aAttributes
, aFormElement
,
830 fosterParentHandle
, aCreator
);
832 // Insert the child into the foster parent.
833 insertFosterParentedChild(child
, aTable
, aStackParent
);
838 void nsHtml5TreeBuilder::detachFromParent(nsIContentHandle
* aElement
) {
839 MOZ_ASSERT(aElement
, "Null element");
842 nsHtml5TreeOperation::Detach(static_cast<nsIContent
*>(aElement
), mBuilder
);
846 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
847 if (MOZ_UNLIKELY(!treeOp
)) {
848 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
851 opDetach
operation(aElement
);
852 treeOp
->Init(mozilla::AsVariant(operation
));
855 void nsHtml5TreeBuilder::appendElement(nsIContentHandle
* aChild
,
856 nsIContentHandle
* aParent
) {
857 MOZ_ASSERT(aChild
, "Null child");
858 MOZ_ASSERT(aParent
, "Null parent");
861 nsresult rv
= nsHtml5TreeOperation::Append(
862 static_cast<nsIContent
*>(aChild
), static_cast<nsIContent
*>(aParent
),
863 mozilla::dom::FROM_PARSER_FRAGMENT
, mBuilder
);
865 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
870 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
871 if (MOZ_UNLIKELY(!treeOp
)) {
872 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
876 opAppend
operation(aChild
, aParent
,
877 (!!mSpeculativeLoadStage
)
878 ? mozilla::dom::FROM_PARSER_NETWORK
879 : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE
);
880 treeOp
->Init(mozilla::AsVariant(operation
));
883 void nsHtml5TreeBuilder::appendChildrenToNewParent(
884 nsIContentHandle
* aOldParent
, nsIContentHandle
* aNewParent
) {
885 MOZ_ASSERT(aOldParent
, "Null old parent");
886 MOZ_ASSERT(aNewParent
, "Null new parent");
889 nsresult rv
= nsHtml5TreeOperation::AppendChildrenToNewParent(
890 static_cast<nsIContent
*>(aOldParent
),
891 static_cast<nsIContent
*>(aNewParent
), mBuilder
);
893 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
898 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
899 if (MOZ_UNLIKELY(!treeOp
)) {
900 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
903 opAppendChildrenToNewParent
operation(aOldParent
, aNewParent
);
904 treeOp
->Init(mozilla::AsVariant(operation
));
907 void nsHtml5TreeBuilder::insertFosterParentedCharacters(
908 char16_t
* aBuffer
, int32_t aStart
, int32_t aLength
,
909 nsIContentHandle
* aTable
, nsIContentHandle
* aStackParent
) {
910 MOZ_ASSERT(aBuffer
, "Null buffer");
911 MOZ_ASSERT(aTable
, "Null table");
912 MOZ_ASSERT(aStackParent
, "Null stack parent");
913 MOZ_ASSERT(!aStart
, "aStart must always be zero.");
916 nsresult rv
= nsHtml5TreeOperation::FosterParentText(
917 static_cast<nsIContent
*>(aStackParent
),
918 aBuffer
, // XXX aStart always ignored???
919 aLength
, static_cast<nsIContent
*>(aTable
), mBuilder
);
921 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
926 auto bufferCopy
= mozilla::MakeUniqueFallible
<char16_t
[]>(aLength
);
928 // Just assigning mBroken instead of generating tree op. The caller
929 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
930 mBroken
= NS_ERROR_OUT_OF_MEMORY
;
935 memcpy(bufferCopy
.get(), aBuffer
, aLength
* sizeof(char16_t
));
937 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
938 if (MOZ_UNLIKELY(!treeOp
)) {
939 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
942 opFosterParentText
operation(aStackParent
, bufferCopy
.release(), aTable
,
944 treeOp
->Init(mozilla::AsVariant(operation
));
947 void nsHtml5TreeBuilder::insertFosterParentedChild(
948 nsIContentHandle
* aChild
, nsIContentHandle
* aTable
,
949 nsIContentHandle
* aStackParent
) {
950 MOZ_ASSERT(aChild
, "Null child");
951 MOZ_ASSERT(aTable
, "Null table");
952 MOZ_ASSERT(aStackParent
, "Null stack parent");
955 nsresult rv
= nsHtml5TreeOperation::FosterParent(
956 static_cast<nsIContent
*>(aChild
),
957 static_cast<nsIContent
*>(aStackParent
),
958 static_cast<nsIContent
*>(aTable
), mBuilder
);
960 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
965 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
966 if (MOZ_UNLIKELY(!treeOp
)) {
967 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
970 opFosterParent
operation(aChild
, aStackParent
, aTable
);
971 treeOp
->Init(mozilla::AsVariant(operation
));
974 void nsHtml5TreeBuilder::appendCharacters(nsIContentHandle
* aParent
,
975 char16_t
* aBuffer
, int32_t aStart
,
977 MOZ_ASSERT(aBuffer
, "Null buffer");
978 MOZ_ASSERT(aParent
, "Null parent");
979 MOZ_ASSERT(!aStart
, "aStart must always be zero.");
982 nsresult rv
= nsHtml5TreeOperation::AppendText(
983 aBuffer
, // XXX aStart always ignored???
984 aLength
, static_cast<nsIContent
*>(aParent
), mBuilder
);
986 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
991 auto bufferCopy
= mozilla::MakeUniqueFallible
<char16_t
[]>(aLength
);
993 // Just assigning mBroken instead of generating tree op. The caller
994 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
995 mBroken
= NS_ERROR_OUT_OF_MEMORY
;
1000 memcpy(bufferCopy
.get(), aBuffer
, aLength
* sizeof(char16_t
));
1002 if (mImportScanner
.ShouldScan()) {
1003 nsTArray
<nsString
> imports
=
1004 mImportScanner
.Scan(mozilla::Span(aBuffer
, aLength
));
1005 for (nsString
& url
: imports
) {
1006 mSpeculativeLoadQueue
.AppendElement()->InitImportStyle(std::move(url
));
1010 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1011 if (MOZ_UNLIKELY(!treeOp
)) {
1012 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1015 opAppendText
operation(aParent
, bufferCopy
.release(), aLength
);
1016 treeOp
->Init(mozilla::AsVariant(operation
));
1019 void nsHtml5TreeBuilder::appendComment(nsIContentHandle
* aParent
,
1020 char16_t
* aBuffer
, int32_t aStart
,
1022 MOZ_ASSERT(aBuffer
, "Null buffer");
1023 MOZ_ASSERT(aParent
, "Null parent");
1024 MOZ_ASSERT(!aStart
, "aStart must always be zero.");
1027 nsresult rv
= nsHtml5TreeOperation::AppendComment(
1028 static_cast<nsIContent
*>(aParent
),
1029 aBuffer
, // XXX aStart always ignored???
1031 if (NS_FAILED(rv
)) {
1032 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
1037 auto bufferCopy
= mozilla::MakeUniqueFallible
<char16_t
[]>(aLength
);
1039 // Just assigning mBroken instead of generating tree op. The caller
1040 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
1041 mBroken
= NS_ERROR_OUT_OF_MEMORY
;
1042 requestSuspension();
1046 memcpy(bufferCopy
.get(), aBuffer
, aLength
* sizeof(char16_t
));
1048 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1049 if (MOZ_UNLIKELY(!treeOp
)) {
1050 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1053 opAppendComment
operation(aParent
, bufferCopy
.release(), aLength
);
1054 treeOp
->Init(mozilla::AsVariant(operation
));
1057 void nsHtml5TreeBuilder::appendCommentToDocument(char16_t
* aBuffer
,
1060 MOZ_ASSERT(aBuffer
, "Null buffer");
1061 MOZ_ASSERT(!aStart
, "aStart must always be zero.");
1064 nsresult rv
= nsHtml5TreeOperation::AppendCommentToDocument(
1065 aBuffer
, // XXX aStart always ignored???
1067 if (NS_FAILED(rv
)) {
1068 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
1073 auto bufferCopy
= mozilla::MakeUniqueFallible
<char16_t
[]>(aLength
);
1075 // Just assigning mBroken instead of generating tree op. The caller
1076 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
1077 mBroken
= NS_ERROR_OUT_OF_MEMORY
;
1078 requestSuspension();
1082 memcpy(bufferCopy
.get(), aBuffer
, aLength
* sizeof(char16_t
));
1084 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1085 if (MOZ_UNLIKELY(!treeOp
)) {
1086 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1089 opAppendCommentToDocument
data(bufferCopy
.release(), aLength
);
1090 treeOp
->Init(mozilla::AsVariant(data
));
1093 void nsHtml5TreeBuilder::addAttributesToElement(
1094 nsIContentHandle
* aElement
, nsHtml5HtmlAttributes
* aAttributes
) {
1095 MOZ_ASSERT(aElement
, "Null element");
1096 MOZ_ASSERT(aAttributes
, "Null attributes");
1098 if (aAttributes
== nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES
) {
1104 aAttributes
== tokenizer
->GetAttributes(),
1105 "Using attribute other than the tokenizer's to add to body or html.");
1106 nsresult rv
= nsHtml5TreeOperation::AddAttributes(
1107 static_cast<nsIContent
*>(aElement
), aAttributes
, mBuilder
);
1108 if (NS_FAILED(rv
)) {
1109 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
1114 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1115 if (MOZ_UNLIKELY(!treeOp
)) {
1116 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1119 opAddAttributes
opeation(aElement
, aAttributes
);
1120 treeOp
->Init(mozilla::AsVariant(opeation
));
1123 void nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle
* aElement
) {
1124 MOZ_ASSERT(aElement
, "Null element");
1127 nsHtml5TreeOperation::MarkMalformedIfScript(
1128 static_cast<nsIContent
*>(aElement
));
1132 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1133 if (MOZ_UNLIKELY(!treeOp
)) {
1134 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1137 opMarkMalformedIfScript
operation(aElement
);
1138 treeOp
->Init(mozilla::AsVariant(operation
));
1141 void nsHtml5TreeBuilder::start(bool fragment
) {
1142 mCurrentHtmlScriptCannotDocumentWriteOrBlock
= false;
1143 mozilla::glean::parsing::svg_unusual_pcdata
.AddToDenominator(1);
1150 void nsHtml5TreeBuilder::end() {
1157 void nsHtml5TreeBuilder::appendDoctypeToDocument(nsAtom
* aName
,
1158 nsHtml5String aPublicId
,
1159 nsHtml5String aSystemId
) {
1160 MOZ_ASSERT(aName
, "Null name");
1161 nsString publicId
; // Not Auto, because using it to hold nsStringBuffer*
1162 nsString systemId
; // Not Auto, because using it to hold nsStringBuffer*
1163 aPublicId
.ToString(publicId
);
1164 aSystemId
.ToString(systemId
);
1166 nsresult rv
= nsHtml5TreeOperation::AppendDoctypeToDocument(
1167 aName
, publicId
, systemId
, mBuilder
);
1168 if (NS_FAILED(rv
)) {
1169 MarkAsBrokenAndRequestSuspensionWithBuilder(rv
);
1174 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1175 if (MOZ_UNLIKELY(!treeOp
)) {
1176 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1179 opAppendDoctypeToDocument
operation(aName
, publicId
, systemId
);
1180 treeOp
->Init(mozilla::AsVariant(operation
));
1181 // nsXMLContentSink can flush here, but what's the point?
1182 // It can also interrupt here, but we can't.
1185 void nsHtml5TreeBuilder::elementPushed(int32_t aNamespace
, nsAtom
* aName
,
1186 nsIContentHandle
* aElement
) {
1187 NS_ASSERTION(aNamespace
== kNameSpaceID_XHTML
||
1188 aNamespace
== kNameSpaceID_SVG
||
1189 aNamespace
== kNameSpaceID_MathML
,
1190 "Element isn't HTML, SVG or MathML!");
1191 NS_ASSERTION(aName
, "Element doesn't have local name!");
1192 NS_ASSERTION(aElement
, "No element!");
1194 * The frame constructor uses recursive algorithms, so it can't deal with
1195 * arbitrarily deep trees. This is especially a problem on Windows where
1196 * the permitted depth of the runtime stack is rather small.
1198 * The following is a protection against author incompetence--not against
1199 * malice. There are other ways to make the DOM deep anyway.
1201 * The basic idea is that when the tree builder stack gets too deep,
1202 * append operations no longer append to the node that the HTML parsing
1203 * algorithm says they should but instead text nodes are append to the last
1204 * element that was seen before a magic tree builder stack threshold was
1205 * reached and element and comment nodes aren't appended to the DOM at all.
1207 * However, for security reasons, non-child descendant text nodes inside an
1208 * SVG script or style element should not become children. Also, non-cell
1209 * table elements shouldn't be used as surrogate parents for user experience
1213 if (MOZ_UNLIKELY(isInSVGOddPCData
)) {
1214 // We are seeing an element that has children, which could not have child
1215 // elements in HTML, i.e., is parsed as PCDATA in SVG but CDATA in HTML.
1216 mozilla::glean::parsing::svg_unusual_pcdata
.AddToNumerator(1);
1218 if (MOZ_UNLIKELY(aNamespace
== kNameSpaceID_SVG
)) {
1219 if ((aName
== nsGkAtoms::style
) || (aName
== nsGkAtoms::xmp
) ||
1220 (aName
== nsGkAtoms::iframe
) || (aName
== nsGkAtoms::noembed
) ||
1221 (aName
== nsGkAtoms::noframes
) || (aName
== nsGkAtoms::noscript
) ||
1222 (aName
== nsGkAtoms::script
)) {
1227 if (aNamespace
!= kNameSpaceID_XHTML
) {
1230 if (aName
== nsGkAtoms::body
|| aName
== nsGkAtoms::frameset
) {
1232 // InnerHTML and DOMParser shouldn't start layout anyway
1235 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1236 if (MOZ_UNLIKELY(!treeOp
)) {
1237 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1240 treeOp
->Init(mozilla::AsVariant(opStartLayout()));
1243 if (nsIContent::RequiresDoneCreatingElement(kNameSpaceID_XHTML
, aName
)) {
1245 nsHtml5TreeOperation::DoneCreatingElement(
1246 static_cast<nsIContent
*>(aElement
));
1248 opDoneCreatingElement
operation(aElement
);
1249 mOpQueue
.AppendElement()->Init(mozilla::AsVariant(operation
));
1253 if (mGenerateSpeculativeLoads
&& aName
== nsGkAtoms::picture
) {
1254 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1255 mSpeculativeLoadQueue
.AppendElement()->InitOpenPicture();
1258 if (aName
== nsGkAtoms::_template
) {
1259 if (tokenizer
->TemplatePushedOrHeadPopped()) {
1260 requestSuspension();
1265 void nsHtml5TreeBuilder::elementPopped(int32_t aNamespace
, nsAtom
* aName
,
1266 nsIContentHandle
* aElement
) {
1267 NS_ASSERTION(aNamespace
== kNameSpaceID_XHTML
||
1268 aNamespace
== kNameSpaceID_SVG
||
1269 aNamespace
== kNameSpaceID_MathML
,
1270 "Element isn't HTML, SVG or MathML!");
1271 NS_ASSERTION(aName
, "Element doesn't have local name!");
1272 NS_ASSERTION(aElement
, "No element!");
1273 if (aNamespace
== kNameSpaceID_MathML
) {
1276 if (MOZ_UNLIKELY(aNamespace
== kNameSpaceID_SVG
)) {
1277 if ((aName
== nsGkAtoms::style
) || (aName
== nsGkAtoms::xmp
) ||
1278 (aName
== nsGkAtoms::iframe
) || (aName
== nsGkAtoms::noembed
) ||
1279 (aName
== nsGkAtoms::noframes
) || (aName
== nsGkAtoms::noscript
) ||
1280 (aName
== nsGkAtoms::script
)) {
1284 // we now have only SVG and HTML
1285 if (aName
== nsGkAtoms::script
) {
1286 if (mPreventScriptExecution
) {
1288 nsHtml5TreeOperation::PreventScriptExecution(
1289 static_cast<nsIContent
*>(aElement
));
1292 opPreventScriptExecution
operation(aElement
);
1293 mOpQueue
.AppendElement()->Init(mozilla::AsVariant(operation
));
1299 if (mCurrentHtmlScriptCannotDocumentWriteOrBlock
) {
1301 aNamespace
== kNameSpaceID_XHTML
|| aNamespace
== kNameSpaceID_SVG
,
1302 "Only HTML and SVG scripts may be async/defer.");
1303 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1304 if (MOZ_UNLIKELY(!treeOp
)) {
1305 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1308 opRunScriptThatCannotDocumentWriteOrBlock
operation(aElement
);
1309 treeOp
->Init(mozilla::AsVariant(operation
));
1310 mCurrentHtmlScriptCannotDocumentWriteOrBlock
= false;
1313 requestSuspension();
1314 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1315 if (MOZ_UNLIKELY(!treeOp
)) {
1316 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1319 opRunScriptThatMayDocumentWriteOrBlock
operation(aElement
, nullptr);
1320 treeOp
->Init(mozilla::AsVariant(operation
));
1323 // Some nodes need DoneAddingChildren() called to initialize
1324 // properly (e.g. form state restoration).
1325 if (nsIContent::RequiresDoneAddingChildren(aNamespace
, aName
)) {
1327 nsHtml5TreeOperation::DoneAddingChildren(
1328 static_cast<nsIContent
*>(aElement
));
1331 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1332 if (MOZ_UNLIKELY(!treeOp
)) {
1333 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1336 opDoneAddingChildren
operation(aElement
);
1337 treeOp
->Init(mozilla::AsVariant(operation
));
1338 if (aNamespace
== kNameSpaceID_XHTML
&& aName
== nsGkAtoms::head
) {
1339 if (tokenizer
->TemplatePushedOrHeadPopped()) {
1340 requestSuspension();
1345 if (aName
== nsGkAtoms::style
||
1346 (aNamespace
== kNameSpaceID_XHTML
&& aName
== nsGkAtoms::link
)) {
1348 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
1349 "Scripts must be blocked.");
1350 mBuilder
->UpdateStyleSheet(static_cast<nsIContent
*>(aElement
));
1354 if (aName
== nsGkAtoms::style
) {
1355 nsTArray
<nsString
> imports
= mImportScanner
.Stop();
1356 for (nsString
& url
: imports
) {
1357 mSpeculativeLoadQueue
.AppendElement()->InitImportStyle(std::move(url
));
1361 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1362 if (MOZ_UNLIKELY(!treeOp
)) {
1363 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1366 opUpdateStyleSheet
operation(aElement
);
1367 treeOp
->Init(mozilla::AsVariant(operation
));
1370 if (aNamespace
== kNameSpaceID_SVG
) {
1371 if (aName
== nsGkAtoms::svg
) {
1372 if (!scriptingEnabled
|| mPreventScriptExecution
) {
1376 nsHtml5TreeOperation::SvgLoad(static_cast<nsIContent
*>(aElement
));
1379 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1380 if (MOZ_UNLIKELY(!treeOp
)) {
1381 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1384 opSvgLoad
operation(aElement
);
1385 treeOp
->Init(mozilla::AsVariant(operation
));
1390 if (mGenerateSpeculativeLoads
&& aName
== nsGkAtoms::picture
) {
1391 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1392 mSpeculativeLoadQueue
.AppendElement()->InitEndPicture();
1397 void nsHtml5TreeBuilder::accumulateCharacters(const char16_t
* aBuf
,
1398 int32_t aStart
, int32_t aLength
) {
1399 MOZ_RELEASE_ASSERT(charBufferLen
+ aLength
<= charBuffer
.length
,
1400 "About to memcpy past the end of the buffer!");
1401 memcpy(charBuffer
+ charBufferLen
, aBuf
+ aStart
, sizeof(char16_t
) * aLength
);
1402 charBufferLen
+= aLength
;
1405 // INT32_MAX is (2^31)-1. Therefore, the highest power-of-two that fits
1406 // is 2^30. Note that this is counting char16_t units. The underlying
1407 // bytes will be twice that, but they fit even in 32-bit size_t even
1408 // if a contiguous chunk of memory of that size is pretty unlikely to
1409 // be available on a 32-bit system.
1410 #define MAX_POWER_OF_TWO_IN_INT32 0x40000000
1412 bool nsHtml5TreeBuilder::EnsureBufferSpace(int32_t aLength
) {
1413 // TODO: Unify nsHtml5Tokenizer::strBuf and nsHtml5TreeBuilder::charBuffer
1414 // so that this method becomes unnecessary.
1415 mozilla::CheckedInt
<int32_t> worstCase(charBufferLen
);
1416 worstCase
+= aLength
;
1417 if (!worstCase
.isValid()) {
1420 if (worstCase
.value() > MAX_POWER_OF_TWO_IN_INT32
) {
1424 if (worstCase
.value() < MAX_POWER_OF_TWO_IN_INT32
) {
1425 // Add one to round to the next power of two to avoid immediate
1426 // reallocation once there are a few characters in the buffer.
1429 charBuffer
= jArray
<char16_t
, int32_t>::newFallibleJArray(
1430 mozilla::RoundUpPow2(worstCase
.value()));
1434 } else if (worstCase
.value() > charBuffer
.length
) {
1435 jArray
<char16_t
, int32_t> newBuf
=
1436 jArray
<char16_t
, int32_t>::newFallibleJArray(
1437 mozilla::RoundUpPow2(worstCase
.value()));
1441 memcpy(newBuf
, charBuffer
, sizeof(char16_t
) * size_t(charBufferLen
));
1442 charBuffer
= newBuf
;
1447 nsIContentHandle
* nsHtml5TreeBuilder::AllocateContentHandle() {
1448 if (MOZ_UNLIKELY(mBuilder
)) {
1449 MOZ_ASSERT_UNREACHABLE("Must never allocate a handle with builder.");
1452 if (mHandlesUsed
== NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH
) {
1453 mOldHandles
.AppendElement(std::move(mHandles
));
1454 mHandles
= mozilla::MakeUnique
<nsIContent
*[]>(
1455 NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH
);
1459 mHandles
[mHandlesUsed
] = reinterpret_cast<nsIContent
*>(uintptr_t(0xC0DEDBAD));
1461 return &mHandles
[mHandlesUsed
++];
1464 bool nsHtml5TreeBuilder::HasScriptThatMayDocumentWriteOrBlock() {
1465 uint32_t len
= mOpQueue
.Length();
1469 return mOpQueue
.ElementAt(len
- 1).IsRunScriptThatMayDocumentWriteOrBlock();
1472 mozilla::Result
<bool, nsresult
> nsHtml5TreeBuilder::Flush(bool aDiscretionary
) {
1473 if (MOZ_UNLIKELY(mBuilder
)) {
1474 MOZ_ASSERT_UNREACHABLE("Must never flush with builder.");
1477 if (NS_SUCCEEDED(mBroken
)) {
1478 if (!aDiscretionary
|| !(charBufferLen
&& currentPtr
>= 0 &&
1479 stack
[currentPtr
]->isFosterParenting())) {
1480 // Don't flush text on discretionary flushes if the current element on
1481 // the stack is a foster-parenting element and there's pending text,
1482 // because flushing in that case would make the tree shape dependent on
1483 // where the flush points fall.
1489 bool hasOps
= !mOpQueue
.IsEmpty();
1491 // If the builder is broken and mOpQueue is not empty, there must be
1492 // one op and it must be eTreeOpMarkAsBroken.
1493 if (NS_FAILED(mBroken
)) {
1494 MOZ_ASSERT(mOpQueue
.Length() == 1,
1495 "Tree builder is broken with a non-empty op queue whose "
1497 MOZ_ASSERT(mOpQueue
[0].IsMarkAsBroken(),
1498 "Tree builder is broken but the op in queue is not marked "
1501 if (!mOpSink
->MoveOpsFrom(mOpQueue
)) {
1502 return mozilla::Err(NS_ERROR_OUT_OF_MEMORY
);
1507 // no op sink: throw away ops
1512 void nsHtml5TreeBuilder::FlushLoads() {
1513 if (MOZ_UNLIKELY(mBuilder
)) {
1514 MOZ_ASSERT_UNREACHABLE("Must never flush loads with builder.");
1517 if (!mSpeculativeLoadQueue
.IsEmpty()) {
1518 mSpeculativeLoadStage
->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue
);
1522 void nsHtml5TreeBuilder::SetDocumentCharset(NotNull
<const Encoding
*> aEncoding
,
1523 nsCharsetSource aCharsetSource
,
1524 bool aCommitEncodingSpeculation
) {
1525 MOZ_ASSERT(!mBuilder
, "How did we call this with builder?");
1526 MOZ_ASSERT(mSpeculativeLoadStage
,
1527 "How did we call this without a speculative load stage?");
1528 mSpeculativeLoadQueue
.AppendElement()->InitSetDocumentCharset(
1529 aEncoding
, aCharsetSource
, aCommitEncodingSpeculation
);
1532 void nsHtml5TreeBuilder::UpdateCharsetSource(nsCharsetSource aCharsetSource
) {
1533 MOZ_ASSERT(!mBuilder
, "How did we call this with builder?");
1534 MOZ_ASSERT(mSpeculativeLoadStage
,
1535 "How did we call this without a speculative load stage (even "
1536 "though we don't need it right here)?");
1538 mViewSource
->UpdateCharsetSource(aCharsetSource
);
1541 opUpdateCharsetSource
operation(aCharsetSource
);
1542 mOpQueue
.AppendElement()->Init(mozilla::AsVariant(operation
));
1545 void nsHtml5TreeBuilder::StreamEnded() {
1546 MOZ_ASSERT(!mBuilder
, "Must not call StreamEnded with builder.");
1547 MOZ_ASSERT(!fragment
, "Must not parse fragments off the main thread.");
1548 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1549 if (MOZ_UNLIKELY(!treeOp
)) {
1550 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1553 treeOp
->Init(mozilla::AsVariant(opStreamEnded()));
1556 void nsHtml5TreeBuilder::NeedsCharsetSwitchTo(
1557 NotNull
<const Encoding
*> aEncoding
, int32_t aCharsetSource
,
1558 int32_t aLineNumber
) {
1559 if (MOZ_UNLIKELY(mBuilder
)) {
1560 MOZ_ASSERT_UNREACHABLE("Must never switch charset with builder.");
1563 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1564 if (MOZ_UNLIKELY(!treeOp
)) {
1565 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1568 opCharsetSwitchTo
opeation(aEncoding
, aCharsetSource
, aLineNumber
);
1569 treeOp
->Init(mozilla::AsVariant(opeation
));
1572 void nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId
,
1574 int32_t aLineNumber
) {
1575 if (MOZ_UNLIKELY(mBuilder
)) {
1576 MOZ_ASSERT_UNREACHABLE("Must never complain about charset with builder.");
1580 if (mSpeculativeLoadStage
) {
1581 mSpeculativeLoadQueue
.AppendElement()->InitMaybeComplainAboutCharset(
1582 aMsgId
, aError
, aLineNumber
);
1584 opMaybeComplainAboutCharset
opeartion(const_cast<char*>(aMsgId
), aError
,
1586 mOpQueue
.AppendElement()->Init(mozilla::AsVariant(opeartion
));
1590 void nsHtml5TreeBuilder::TryToEnableEncodingMenu() {
1591 if (MOZ_UNLIKELY(mBuilder
)) {
1592 MOZ_ASSERT_UNREACHABLE("Must never disable encoding menu with builder.");
1595 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement();
1596 NS_ASSERTION(treeOp
, "Tree op allocation failed.");
1597 treeOp
->Init(mozilla::AsVariant(opEnableEncodingMenu()));
1600 void nsHtml5TreeBuilder::AddSnapshotToScript(
1601 nsAHtml5TreeBuilderState
* aSnapshot
, int32_t aLine
) {
1602 if (MOZ_UNLIKELY(mBuilder
)) {
1603 MOZ_ASSERT_UNREACHABLE("Must never use snapshots with builder.");
1606 MOZ_ASSERT(HasScriptThatMayDocumentWriteOrBlock(),
1607 "No script to add a snapshot to!");
1608 MOZ_ASSERT(aSnapshot
, "Got null snapshot.");
1609 mOpQueue
.ElementAt(mOpQueue
.Length() - 1).SetSnapshot(aSnapshot
, aLine
);
1612 void nsHtml5TreeBuilder::DropHandles() {
1613 MOZ_ASSERT(!mBuilder
, "Must not drop handles with builder.");
1614 mOldHandles
.Clear();
1618 void nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv
) {
1619 if (MOZ_UNLIKELY(mBuilder
)) {
1620 MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
1624 mOpQueue
.Clear(); // Previous ops don't matter anymore
1625 opMarkAsBroken
operation(aRv
);
1626 mOpQueue
.AppendElement()->Init(mozilla::AsVariant(operation
));
1629 void nsHtml5TreeBuilder::MarkAsBrokenFromPortability(nsresult aRv
) {
1631 MarkAsBrokenAndRequestSuspensionWithBuilder(aRv
);
1635 requestSuspension();
1638 void nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString
& aTitle
) {
1639 MOZ_ASSERT(!mBuilder
, "Must not view source with builder.");
1641 startTag(nsHtml5ElementName::ELT_META
,
1642 nsHtml5ViewSourceUtils::NewMetaViewportAttributes(), false);
1644 startTag(nsHtml5ElementName::ELT_TITLE
,
1645 nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES
, false);
1647 // XUL will add the "Source of: " prefix.
1648 uint32_t length
= aTitle
.Length();
1649 if (length
> INT32_MAX
) {
1652 characters(aTitle
.get(), 0, (int32_t)length
);
1653 endTag(nsHtml5ElementName::ELT_TITLE
);
1655 startTag(nsHtml5ElementName::ELT_LINK
,
1656 nsHtml5ViewSourceUtils::NewLinkAttributes(), false);
1658 startTag(nsHtml5ElementName::ELT_BODY
,
1659 nsHtml5ViewSourceUtils::NewBodyAttributes(), false);
1661 StartPlainTextBody();
1664 void nsHtml5TreeBuilder::StartPlainText() {
1665 MOZ_ASSERT(!mBuilder
, "Must not view source with builder.");
1666 setForceNoQuirks(true);
1667 startTag(nsHtml5ElementName::ELT_LINK
,
1668 nsHtml5PlainTextUtils::NewLinkAttributes(), false);
1670 startTag(nsHtml5ElementName::ELT_BODY
,
1671 nsHtml5PlainTextUtils::NewBodyAttributes(), false);
1673 StartPlainTextBody();
1676 void nsHtml5TreeBuilder::StartPlainTextBody() {
1677 MOZ_ASSERT(!mBuilder
, "Must not view source with builder.");
1678 startTag(nsHtml5ElementName::ELT_PRE
, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES
,
1680 needToDropLF
= false;
1683 // DocumentModeHandler
1684 void nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m
) {
1686 mBuilder
->SetDocumentMode(m
);
1689 if (mSpeculativeLoadStage
) {
1690 mSpeculativeLoadQueue
.AppendElement()->InitSetDocumentMode(m
);
1693 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1694 if (MOZ_UNLIKELY(!treeOp
)) {
1695 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1698 treeOp
->Init(mozilla::AsVariant(m
));
1701 nsIContentHandle
* nsHtml5TreeBuilder::getDocumentFragmentForTemplate(
1702 nsIContentHandle
* aTemplate
) {
1704 return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(
1705 static_cast<nsIContent
*>(aTemplate
));
1707 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1708 if (MOZ_UNLIKELY(!treeOp
)) {
1709 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1712 nsIContentHandle
* fragHandle
= AllocateContentHandle();
1713 opGetDocumentFragmentForTemplate
operation(aTemplate
, fragHandle
);
1714 treeOp
->Init(mozilla::AsVariant(operation
));
1718 void nsHtml5TreeBuilder::setDocumentFragmentForTemplate(
1719 nsIContentHandle
* aTemplate
, nsIContentHandle
* aFragment
) {
1721 nsHtml5TreeOperation::SetDocumentFragmentForTemplate(
1722 static_cast<nsIContent
*>(aTemplate
),
1723 static_cast<nsIContent
*>(aFragment
));
1727 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1728 if (MOZ_UNLIKELY(!treeOp
)) {
1729 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1732 opSetDocumentFragmentForTemplate
operation(aTemplate
, aFragment
);
1733 treeOp
->Init(mozilla::AsVariant(operation
));
1736 nsIContentHandle
* nsHtml5TreeBuilder::getShadowRootFromHost(
1737 nsIContentHandle
* aHost
, nsIContentHandle
* aTemplateNode
,
1738 nsHtml5String aShadowRootMode
, bool aShadowRootIsClonable
,
1739 bool aShadowRootIsSerializable
, bool aShadowRootDelegatesFocus
) {
1740 mozilla::dom::ShadowRootMode mode
;
1741 if (aShadowRootMode
.LowerCaseEqualsASCII("open")) {
1742 mode
= mozilla::dom::ShadowRootMode::Open
;
1743 } else if (aShadowRootMode
.LowerCaseEqualsASCII("closed")) {
1744 mode
= mozilla::dom::ShadowRootMode::Closed
;
1750 nsIContent
* root
= nsContentUtils::AttachDeclarativeShadowRoot(
1751 static_cast<nsIContent
*>(aHost
), mode
, aShadowRootIsClonable
,
1752 aShadowRootIsSerializable
, aShadowRootDelegatesFocus
);
1754 nsContentUtils::LogSimpleConsoleError(
1755 u
"Failed to attach Declarative Shadow DOM."_ns
, "DOM"_ns
,
1756 mBuilder
->GetDocument()->IsInPrivateBrowsing(),
1757 mBuilder
->GetDocument()->IsInChromeDocShell());
1762 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement(mozilla::fallible
);
1763 if (MOZ_UNLIKELY(!treeOp
)) {
1764 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY
);
1767 nsIContentHandle
* fragHandle
= AllocateContentHandle();
1768 opGetShadowRootFromHost
operation(
1769 aHost
, fragHandle
, aTemplateNode
, mode
, aShadowRootIsClonable
,
1770 aShadowRootIsSerializable
, aShadowRootDelegatesFocus
);
1771 treeOp
->Init(mozilla::AsVariant(operation
));
1775 nsIContentHandle
* nsHtml5TreeBuilder::getFormPointerForContext(
1776 nsIContentHandle
* aContext
) {
1777 MOZ_ASSERT(mBuilder
, "Must have builder.");
1782 MOZ_ASSERT(NS_IsMainThread());
1784 // aContext must always be an element that already exists
1786 nsIContent
* contextNode
= static_cast<nsIContent
*>(aContext
);
1787 nsIContent
* currentAncestor
= contextNode
;
1789 // We traverse the ancestors of the context node to find the nearest
1790 // form pointer. This traversal is why aContext must not be an emtpy handle.
1791 nsIContent
* nearestForm
= nullptr;
1792 while (currentAncestor
) {
1793 if (currentAncestor
->IsHTMLElement(nsGkAtoms::form
)) {
1794 nearestForm
= currentAncestor
;
1797 currentAncestor
= currentAncestor
->GetParent();
1809 void nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter
* aHighlighter
) {
1810 MOZ_ASSERT(!mBuilder
, "Must not view source with builder.");
1811 mViewSource
= aHighlighter
;
1814 void nsHtml5TreeBuilder::errDeepTree() {
1815 if (MOZ_UNLIKELY(mViewSource
)) {
1816 mViewSource
->AddErrorToCurrentRun("errDeepTree");
1817 } else if (!mBuilder
) {
1818 nsHtml5TreeOperation
* treeOp
= mOpQueue
.AppendElement();
1819 MOZ_ASSERT(treeOp
, "Tree op allocation failed.");
1820 opMaybeComplainAboutDeepTree
operation(tokenizer
->getLineNumber());
1821 treeOp
->Init(mozilla::AsVariant(operation
));
1825 void nsHtml5TreeBuilder::errStrayStartTag(nsAtom
* aName
) {
1826 if (MOZ_UNLIKELY(mViewSource
)) {
1827 mViewSource
->AddErrorToCurrentRun("errStrayStartTag2", aName
);
1831 void nsHtml5TreeBuilder::errStrayEndTag(nsAtom
* aName
) {
1832 if (MOZ_UNLIKELY(mViewSource
)) {
1833 mViewSource
->AddErrorToCurrentRun("errStrayEndTag", aName
);
1837 void nsHtml5TreeBuilder::errUnclosedElements(int32_t aIndex
, nsAtom
* aName
) {
1838 if (MOZ_UNLIKELY(mViewSource
)) {
1839 mViewSource
->AddErrorToCurrentRun("errUnclosedElements", aName
);
1843 void nsHtml5TreeBuilder::errUnclosedElementsImplied(int32_t aIndex
,
1845 if (MOZ_UNLIKELY(mViewSource
)) {
1846 mViewSource
->AddErrorToCurrentRun("errUnclosedElementsImplied", aName
);
1850 void nsHtml5TreeBuilder::errUnclosedElementsCell(int32_t aIndex
) {
1851 if (MOZ_UNLIKELY(mViewSource
)) {
1852 mViewSource
->AddErrorToCurrentRun("errUnclosedElementsCell");
1856 void nsHtml5TreeBuilder::errStrayDoctype() {
1857 if (MOZ_UNLIKELY(mViewSource
)) {
1858 mViewSource
->AddErrorToCurrentRun("errStrayDoctype");
1862 void nsHtml5TreeBuilder::errAlmostStandardsDoctype() {
1863 if (MOZ_UNLIKELY(mViewSource
) && !forceNoQuirks
) {
1864 mViewSource
->AddErrorToCurrentRun("errAlmostStandardsDoctype");
1868 void nsHtml5TreeBuilder::errQuirkyDoctype() {
1869 if (MOZ_UNLIKELY(mViewSource
) && !forceNoQuirks
) {
1870 mViewSource
->AddErrorToCurrentRun("errQuirkyDoctype");
1874 void nsHtml5TreeBuilder::errNonSpaceInTrailer() {
1875 if (MOZ_UNLIKELY(mViewSource
)) {
1876 mViewSource
->AddErrorToCurrentRun("errNonSpaceInTrailer");
1880 void nsHtml5TreeBuilder::errNonSpaceAfterFrameset() {
1881 if (MOZ_UNLIKELY(mViewSource
)) {
1882 mViewSource
->AddErrorToCurrentRun("errNonSpaceAfterFrameset");
1886 void nsHtml5TreeBuilder::errNonSpaceInFrameset() {
1887 if (MOZ_UNLIKELY(mViewSource
)) {
1888 mViewSource
->AddErrorToCurrentRun("errNonSpaceInFrameset");
1892 void nsHtml5TreeBuilder::errNonSpaceAfterBody() {
1893 if (MOZ_UNLIKELY(mViewSource
)) {
1894 mViewSource
->AddErrorToCurrentRun("errNonSpaceAfterBody");
1898 void nsHtml5TreeBuilder::errNonSpaceInColgroupInFragment() {
1899 if (MOZ_UNLIKELY(mViewSource
)) {
1900 mViewSource
->AddErrorToCurrentRun("errNonSpaceInColgroupInFragment");
1904 void nsHtml5TreeBuilder::errNonSpaceInNoscriptInHead() {
1905 if (MOZ_UNLIKELY(mViewSource
)) {
1906 mViewSource
->AddErrorToCurrentRun("errNonSpaceInNoscriptInHead");
1910 void nsHtml5TreeBuilder::errFooBetweenHeadAndBody(nsAtom
* aName
) {
1911 if (MOZ_UNLIKELY(mViewSource
)) {
1912 mViewSource
->AddErrorToCurrentRun("errFooBetweenHeadAndBody", aName
);
1916 void nsHtml5TreeBuilder::errStartTagWithoutDoctype() {
1917 if (MOZ_UNLIKELY(mViewSource
) && !forceNoQuirks
) {
1918 mViewSource
->AddErrorToCurrentRun("errStartTagWithoutDoctype");
1922 void nsHtml5TreeBuilder::errNoSelectInTableScope() {
1923 if (MOZ_UNLIKELY(mViewSource
)) {
1924 mViewSource
->AddErrorToCurrentRun("errNoSelectInTableScope");
1928 void nsHtml5TreeBuilder::errStartSelectWhereEndSelectExpected() {
1929 if (MOZ_UNLIKELY(mViewSource
)) {
1930 mViewSource
->AddErrorToCurrentRun("errStartSelectWhereEndSelectExpected");
1934 void nsHtml5TreeBuilder::errStartTagWithSelectOpen(nsAtom
* aName
) {
1935 if (MOZ_UNLIKELY(mViewSource
)) {
1936 mViewSource
->AddErrorToCurrentRun("errStartTagWithSelectOpen", aName
);
1940 void nsHtml5TreeBuilder::errBadStartTagInNoscriptInHead(nsAtom
* aName
) {
1941 if (MOZ_UNLIKELY(mViewSource
)) {
1942 mViewSource
->AddErrorToCurrentRun("errBadStartTagInNoscriptInHead", aName
);
1946 void nsHtml5TreeBuilder::errImage() {
1947 if (MOZ_UNLIKELY(mViewSource
)) {
1948 mViewSource
->AddErrorToCurrentRun("errImage");
1952 void nsHtml5TreeBuilder::errIsindex() {
1953 if (MOZ_UNLIKELY(mViewSource
)) {
1954 mViewSource
->AddErrorToCurrentRun("errIsindex");
1958 void nsHtml5TreeBuilder::errFooSeenWhenFooOpen(nsAtom
* aName
) {
1959 if (MOZ_UNLIKELY(mViewSource
)) {
1960 mViewSource
->AddErrorToCurrentRun("errFooSeenWhenFooOpen2", aName
);
1964 void nsHtml5TreeBuilder::errHeadingWhenHeadingOpen() {
1965 if (MOZ_UNLIKELY(mViewSource
)) {
1966 mViewSource
->AddErrorToCurrentRun("errHeadingWhenHeadingOpen");
1970 void nsHtml5TreeBuilder::errFramesetStart() {
1971 if (MOZ_UNLIKELY(mViewSource
)) {
1972 mViewSource
->AddErrorToCurrentRun("errFramesetStart");
1976 void nsHtml5TreeBuilder::errNoCellToClose() {
1977 if (MOZ_UNLIKELY(mViewSource
)) {
1978 mViewSource
->AddErrorToCurrentRun("errNoCellToClose");
1982 void nsHtml5TreeBuilder::errStartTagInTable(nsAtom
* aName
) {
1983 if (MOZ_UNLIKELY(mViewSource
)) {
1984 mViewSource
->AddErrorToCurrentRun("errStartTagInTable", aName
);
1988 void nsHtml5TreeBuilder::errFormWhenFormOpen() {
1989 if (MOZ_UNLIKELY(mViewSource
)) {
1990 mViewSource
->AddErrorToCurrentRun("errFormWhenFormOpen");
1994 void nsHtml5TreeBuilder::errTableSeenWhileTableOpen() {
1995 if (MOZ_UNLIKELY(mViewSource
)) {
1996 mViewSource
->AddErrorToCurrentRun("errTableSeenWhileTableOpen");
2000 void nsHtml5TreeBuilder::errStartTagInTableBody(nsAtom
* aName
) {
2001 if (MOZ_UNLIKELY(mViewSource
)) {
2002 mViewSource
->AddErrorToCurrentRun("errStartTagInTableBody", aName
);
2006 void nsHtml5TreeBuilder::errEndTagSeenWithoutDoctype() {
2007 if (MOZ_UNLIKELY(mViewSource
) && !forceNoQuirks
) {
2008 mViewSource
->AddErrorToCurrentRun("errEndTagSeenWithoutDoctype");
2012 void nsHtml5TreeBuilder::errEndTagAfterBody() {
2013 if (MOZ_UNLIKELY(mViewSource
)) {
2014 mViewSource
->AddErrorToCurrentRun("errEndTagAfterBody");
2018 void nsHtml5TreeBuilder::errEndTagSeenWithSelectOpen(nsAtom
* aName
) {
2019 if (MOZ_UNLIKELY(mViewSource
)) {
2020 mViewSource
->AddErrorToCurrentRun("errEndTagSeenWithSelectOpen", aName
);
2024 void nsHtml5TreeBuilder::errGarbageInColgroup() {
2025 if (MOZ_UNLIKELY(mViewSource
)) {
2026 mViewSource
->AddErrorToCurrentRun("errGarbageInColgroup");
2030 void nsHtml5TreeBuilder::errEndTagBr() {
2031 if (MOZ_UNLIKELY(mViewSource
)) {
2032 mViewSource
->AddErrorToCurrentRun("errEndTagBr");
2036 void nsHtml5TreeBuilder::errNoElementToCloseButEndTagSeen(nsAtom
* aName
) {
2037 if (MOZ_UNLIKELY(mViewSource
)) {
2038 mViewSource
->AddErrorToCurrentRun("errNoElementToCloseButEndTagSeen",
2043 void nsHtml5TreeBuilder::errHtmlStartTagInForeignContext(nsAtom
* aName
) {
2044 if (MOZ_UNLIKELY(mViewSource
)) {
2045 mViewSource
->AddErrorToCurrentRun("errHtmlStartTagInForeignContext", aName
);
2049 void nsHtml5TreeBuilder::errNoTableRowToClose() {
2050 if (MOZ_UNLIKELY(mViewSource
)) {
2051 mViewSource
->AddErrorToCurrentRun("errNoTableRowToClose");
2055 void nsHtml5TreeBuilder::errNonSpaceInTable() {
2056 if (MOZ_UNLIKELY(mViewSource
)) {
2057 mViewSource
->AddErrorToCurrentRun("errNonSpaceInTable");
2061 void nsHtml5TreeBuilder::errUnclosedChildrenInRuby() {
2062 if (MOZ_UNLIKELY(mViewSource
)) {
2063 mViewSource
->AddErrorToCurrentRun("errUnclosedChildrenInRuby");
2067 void nsHtml5TreeBuilder::errStartTagSeenWithoutRuby(nsAtom
* aName
) {
2068 if (MOZ_UNLIKELY(mViewSource
)) {
2069 mViewSource
->AddErrorToCurrentRun("errStartTagSeenWithoutRuby", aName
);
2073 void nsHtml5TreeBuilder::errSelfClosing() {
2074 if (MOZ_UNLIKELY(mViewSource
)) {
2075 mViewSource
->AddErrorToCurrentSlash("errSelfClosing");
2079 void nsHtml5TreeBuilder::errNoCheckUnclosedElementsOnStack() {
2080 if (MOZ_UNLIKELY(mViewSource
)) {
2081 mViewSource
->AddErrorToCurrentRun("errNoCheckUnclosedElementsOnStack");
2085 void nsHtml5TreeBuilder::errEndTagDidNotMatchCurrentOpenElement(
2086 nsAtom
* aName
, nsAtom
* aOther
) {
2087 if (MOZ_UNLIKELY(mViewSource
)) {
2088 mViewSource
->AddErrorToCurrentRun("errEndTagDidNotMatchCurrentOpenElement",
2093 void nsHtml5TreeBuilder::errEndTagViolatesNestingRules(nsAtom
* aName
) {
2094 if (MOZ_UNLIKELY(mViewSource
)) {
2095 mViewSource
->AddErrorToCurrentRun("errEndTagViolatesNestingRules", aName
);
2099 void nsHtml5TreeBuilder::errEndWithUnclosedElements(nsAtom
* aName
) {
2100 if (MOZ_UNLIKELY(mViewSource
)) {
2101 mViewSource
->AddErrorToCurrentRun("errEndWithUnclosedElements", aName
);
2105 void nsHtml5TreeBuilder::errListUnclosedStartTags(int32_t aIgnored
) {
2106 if (MOZ_UNLIKELY(mViewSource
)) {
2107 mViewSource
->AddErrorToCurrentRun("errListUnclosedStartTags");