Bug 1927094 - optimize lineScrollAmount so it doesn't iterate over all tabs, r=mconley
[gecko.git] / parser / html / nsHtml5TreeBuilderCppSupplement.h
blob729e22171dee40c866fe17e0e2c4c0ebb31bf7a0
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 "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)
25 : mode(0),
26 originalMode(0),
27 framesetOk(false),
28 tokenizer(nullptr),
29 scriptingEnabled(false),
30 needToDropLF(false),
31 fragment(false),
32 contextName(nullptr),
33 contextNamespace(kNameSpaceID_None),
34 contextNode(nullptr),
35 templateModePtr(0),
36 stackNodesIdx(0),
37 numStackNodes(0),
38 currentPtr(0),
39 listPtr(0),
40 formPointer(nullptr),
41 headPointer(nullptr),
42 charBufferLen(0),
43 quirks(false),
44 forceNoQuirks(false),
45 allowDeclarativeShadowRoots(false),
46 keepBuffer(false),
47 mBuilder(aBuilder),
48 mViewSource(nullptr),
49 mOpSink(nullptr),
50 mHandles(nullptr),
51 mHandlesUsed(0),
52 mSpeculativeLoadStage(nullptr),
53 mBroken(NS_OK),
54 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
55 mPreventScriptExecution(false),
56 mGenerateSpeculativeLoads(false),
57 mHasSeenImportMap(false)
58 #ifdef DEBUG
60 mActive(false)
61 #endif
63 MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
66 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
67 nsHtml5TreeOpStage* aStage,
68 bool aGenerateSpeculativeLoads)
69 : mode(0),
70 originalMode(0),
71 framesetOk(false),
72 tokenizer(nullptr),
73 scriptingEnabled(false),
74 needToDropLF(false),
75 fragment(false),
76 contextName(nullptr),
77 contextNamespace(kNameSpaceID_None),
78 contextNode(nullptr),
79 templateModePtr(0),
80 stackNodesIdx(0),
81 numStackNodes(0),
82 currentPtr(0),
83 listPtr(0),
84 formPointer(nullptr),
85 headPointer(nullptr),
86 charBufferLen(0),
87 quirks(false),
88 forceNoQuirks(false),
89 allowDeclarativeShadowRoots(false),
90 keepBuffer(false),
91 mBuilder(nullptr),
92 mViewSource(nullptr),
93 mOpSink(aOpSink),
94 mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]),
95 mHandlesUsed(0),
96 mSpeculativeLoadStage(aStage),
97 mBroken(NS_OK),
98 mCurrentHtmlScriptCannotDocumentWriteOrBlock(false),
99 mPreventScriptExecution(false),
100 mGenerateSpeculativeLoads(aGenerateSpeculativeLoads),
101 mHasSeenImportMap(false)
102 #ifdef DEBUG
104 mActive(false)
105 #endif
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!");
116 mOpQueue.Clear();
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,
140 "Bogus namespace.");
142 if (mBuilder) {
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();
152 nsIContent* elem;
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);
161 } else {
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)) {
168 delete aAttributes;
170 return elem;
173 nsIContentHandle* content = AllocateContentHandle();
174 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
175 if (MOZ_UNLIKELY(!treeOp)) {
176 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
177 return nullptr;
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));
192 } else {
193 // kNameSpaceID_MathML
194 opCreateMathMLElement operation(content, aName, aAttributes,
195 aIntendedParent);
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")) {
211 nsHtml5String url =
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
233 if (srcset) {
234 nsHtml5String sizes =
235 aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
236 nsHtml5String type =
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);
249 return nullptr;
251 opSetScriptLineAndColumnNumberAndFreeze operation(
252 content, tokenizer->getLineNumber(),
253 // NOTE: tokenizer->getColumnNumber() points '>'.
254 tokenizer->getColumnNumber() + 1);
255 treeOp->Init(mozilla::AsVariant(operation));
257 nsHtml5String type =
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");
264 bool async = false;
265 bool defer = false;
266 bool nomodule =
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
287 // the speculation.
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
312 if (importmap) {
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
322 // parser thread.
323 // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312
324 mHasSeenImportMap = true;
326 nsHtml5String url =
327 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
328 if (url) {
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
356 // importmap.
357 mCurrentHtmlScriptCannotDocumentWriteOrBlock =
358 isModule || importmap || async || defer || nomodule;
359 } else if (nsGkAtoms::link == aName) {
360 nsHtml5String rel =
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.
364 if (rel) {
365 if (rel.LowerCaseEqualsASCII("stylesheet")) {
366 nsHtml5String url =
367 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
368 if (url) {
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")) {
388 nsHtml5String url =
389 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
390 if (url) {
391 nsHtml5String crossOrigin = aAttributes->getValue(
392 nsHtml5AttributeName::ATTR_CROSSORIGIN);
393 mSpeculativeLoadQueue.AppendElement()->InitPreconnect(
394 url, crossOrigin);
396 } else if (rel.LowerCaseEqualsASCII("preload")) {
397 nsHtml5String url =
398 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
399 if (url) {
400 nsHtml5String as =
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")) {
425 nsHtml5String type =
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,
431 false, false, true);
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) {
456 nsHtml5String url =
457 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
458 if (url && url.Length() != 0) {
459 nsHtml5String as =
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;
467 nsHtml5String type =
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,
486 false, false, true);
491 } else if (nsGkAtoms::video == aName) {
492 nsHtml5String url =
493 aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
494 if (url) {
495 // Fetch priority is not supported for video. Nullptr will map to
496 // the auto state
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,
502 fetchPriority);
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);
511 return nullptr;
513 opSetStyleLineNumber operation(content, tokenizer->getLineNumber());
514 treeOp->Init(mozilla::AsVariant(operation));
515 } else if (nsGkAtoms::html == aName) {
516 nsHtml5String url =
517 aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
518 mSpeculativeLoadQueue.AppendElement()->InitManifest(url);
519 } else if (nsGkAtoms::base == aName) {
520 nsHtml5String url =
521 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
522 if (url) {
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))) {
530 nsHtml5String csp =
531 aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
532 if (csp) {
533 mSpeculativeLoadQueue.AppendElement()->InitMetaCSP(csp);
535 } else if (nsHtml5Portability::
536 lowerCaseLiteralEqualsIgnoreAsciiCaseString(
537 "referrer",
538 aAttributes->getValue(
539 nsHtml5AttributeName::ATTR_NAME))) {
540 nsHtml5String referrerPolicy =
541 aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
542 if (referrerPolicy) {
543 mSpeculativeLoadQueue.AppendElement()->InitMetaReferrerPolicy(
544 referrerPolicy);
548 break;
549 case kNameSpaceID_SVG:
550 if (nsGkAtoms::image == aName) {
551 nsHtml5String url =
552 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
553 if (!url) {
554 url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
556 if (url) {
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,
565 fetchPriority);
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);
573 return nullptr;
575 opSetScriptLineAndColumnNumberAndFreeze operation(
576 content, tokenizer->getLineNumber(),
577 // NOTE: tokenizer->getColumnNumber() points '>'.
578 tokenizer->getColumnNumber() + 1);
579 treeOp->Init(mozilla::AsVariant(operation));
581 nsHtml5String type =
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");
588 bool async = false;
589 bool defer = false;
591 if (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
601 // parser thread.
602 // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312
603 mHasSeenImportMap = true;
605 nsHtml5String url =
606 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
607 if (!url) {
608 url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
610 if (url) {
611 async = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC);
612 defer = aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
613 if ((isModule && !mHasSeenImportMap) || (!isModule && !importmap)) {
614 nsHtml5String type =
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);
652 return nullptr;
654 opSetStyleLineNumber operation(content, tokenizer->getLineNumber());
655 treeOp->Init(mozilla::AsVariant(operation));
657 break;
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);
665 return nullptr;
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);
673 return nullptr;
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`.
694 nsHtml5String type =
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) {
713 nsHtml5String url =
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);
720 return nullptr;
722 if (url) {
723 nsString
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));
728 } else {
729 opProcessOfflineManifest operation(ToNewUnicode(u""_ns));
730 treeOp->Init(mozilla::AsVariant(operation));
732 } else if (nsGkAtoms::base == aName && mViewSource) {
733 nsHtml5String url =
734 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
735 if (url) {
736 mViewSource->AddBase(url);
742 // End wall of code for speculative loading
744 return content;
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);
753 if (aFormElement) {
754 if (mBuilder) {
755 nsHtml5TreeOperation::SetFormElement(
756 static_cast<nsIContent*>(content),
757 static_cast<nsIContent*>(aFormElement),
758 static_cast<nsIContent*>(aIntendedParent));
759 } else {
760 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
761 if (MOZ_UNLIKELY(!treeOp)) {
762 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
763 return nullptr;
765 opSetFormElement operation(content, aFormElement, aIntendedParent);
766 treeOp->Init(mozilla::AsVariant(operation));
769 return content;
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);
779 if (mBuilder) {
780 nsresult rv = nsHtml5TreeOperation::AppendToDocument(
781 static_cast<nsIContent*>(content), mBuilder);
782 if (NS_FAILED(rv)) {
783 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
785 } else {
786 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
787 if (MOZ_UNLIKELY(!treeOp)) {
788 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
789 return nullptr;
791 opAppendToDocument operation(content);
792 treeOp->Init(mozilla::AsVariant(operation));
794 return content;
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");
804 if (mBuilder) {
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);
816 return child;
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);
835 return child;
838 void nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement) {
839 MOZ_ASSERT(aElement, "Null element");
841 if (mBuilder) {
842 nsHtml5TreeOperation::Detach(static_cast<nsIContent*>(aElement), mBuilder);
843 return;
846 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
847 if (MOZ_UNLIKELY(!treeOp)) {
848 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
849 return;
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");
860 if (mBuilder) {
861 nsresult rv = nsHtml5TreeOperation::Append(
862 static_cast<nsIContent*>(aChild), static_cast<nsIContent*>(aParent),
863 mozilla::dom::FROM_PARSER_FRAGMENT, mBuilder);
864 if (NS_FAILED(rv)) {
865 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
867 return;
870 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
871 if (MOZ_UNLIKELY(!treeOp)) {
872 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
873 return;
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");
888 if (mBuilder) {
889 nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent(
890 static_cast<nsIContent*>(aOldParent),
891 static_cast<nsIContent*>(aNewParent), mBuilder);
892 if (NS_FAILED(rv)) {
893 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
895 return;
898 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
899 if (MOZ_UNLIKELY(!treeOp)) {
900 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
901 return;
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.");
915 if (mBuilder) {
916 nsresult rv = nsHtml5TreeOperation::FosterParentText(
917 static_cast<nsIContent*>(aStackParent),
918 aBuffer, // XXX aStart always ignored???
919 aLength, static_cast<nsIContent*>(aTable), mBuilder);
920 if (NS_FAILED(rv)) {
921 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
923 return;
926 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
927 if (!bufferCopy) {
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;
931 requestSuspension();
932 return;
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);
940 return;
942 opFosterParentText operation(aStackParent, bufferCopy.release(), aTable,
943 aLength);
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");
954 if (mBuilder) {
955 nsresult rv = nsHtml5TreeOperation::FosterParent(
956 static_cast<nsIContent*>(aChild),
957 static_cast<nsIContent*>(aStackParent),
958 static_cast<nsIContent*>(aTable), mBuilder);
959 if (NS_FAILED(rv)) {
960 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
962 return;
965 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
966 if (MOZ_UNLIKELY(!treeOp)) {
967 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
968 return;
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,
976 int32_t aLength) {
977 MOZ_ASSERT(aBuffer, "Null buffer");
978 MOZ_ASSERT(aParent, "Null parent");
979 MOZ_ASSERT(!aStart, "aStart must always be zero.");
981 if (mBuilder) {
982 nsresult rv = nsHtml5TreeOperation::AppendText(
983 aBuffer, // XXX aStart always ignored???
984 aLength, static_cast<nsIContent*>(aParent), mBuilder);
985 if (NS_FAILED(rv)) {
986 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
988 return;
991 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
992 if (!bufferCopy) {
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;
996 requestSuspension();
997 return;
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);
1013 return;
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,
1021 int32_t aLength) {
1022 MOZ_ASSERT(aBuffer, "Null buffer");
1023 MOZ_ASSERT(aParent, "Null parent");
1024 MOZ_ASSERT(!aStart, "aStart must always be zero.");
1026 if (mBuilder) {
1027 nsresult rv = nsHtml5TreeOperation::AppendComment(
1028 static_cast<nsIContent*>(aParent),
1029 aBuffer, // XXX aStart always ignored???
1030 aLength, mBuilder);
1031 if (NS_FAILED(rv)) {
1032 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1034 return;
1037 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
1038 if (!bufferCopy) {
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();
1043 return;
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);
1051 return;
1053 opAppendComment operation(aParent, bufferCopy.release(), aLength);
1054 treeOp->Init(mozilla::AsVariant(operation));
1057 void nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer,
1058 int32_t aStart,
1059 int32_t aLength) {
1060 MOZ_ASSERT(aBuffer, "Null buffer");
1061 MOZ_ASSERT(!aStart, "aStart must always be zero.");
1063 if (mBuilder) {
1064 nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument(
1065 aBuffer, // XXX aStart always ignored???
1066 aLength, mBuilder);
1067 if (NS_FAILED(rv)) {
1068 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1070 return;
1073 auto bufferCopy = mozilla::MakeUniqueFallible<char16_t[]>(aLength);
1074 if (!bufferCopy) {
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();
1079 return;
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);
1087 return;
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) {
1099 return;
1102 if (mBuilder) {
1103 MOZ_ASSERT(
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);
1111 return;
1114 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1115 if (MOZ_UNLIKELY(!treeOp)) {
1116 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1117 return;
1119 opAddAttributes opeation(aElement, aAttributes);
1120 treeOp->Init(mozilla::AsVariant(opeation));
1123 void nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement) {
1124 MOZ_ASSERT(aElement, "Null element");
1126 if (mBuilder) {
1127 nsHtml5TreeOperation::MarkMalformedIfScript(
1128 static_cast<nsIContent*>(aElement));
1129 return;
1132 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1133 if (MOZ_UNLIKELY(!treeOp)) {
1134 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1135 return;
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);
1145 #ifdef DEBUG
1146 mActive = true;
1147 #endif
1150 void nsHtml5TreeBuilder::end() {
1151 mOpQueue.Clear();
1152 #ifdef DEBUG
1153 mActive = false;
1154 #endif
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);
1165 if (mBuilder) {
1166 nsresult rv = nsHtml5TreeOperation::AppendDoctypeToDocument(
1167 aName, publicId, systemId, mBuilder);
1168 if (NS_FAILED(rv)) {
1169 MarkAsBrokenAndRequestSuspensionWithBuilder(rv);
1171 return;
1174 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1175 if (MOZ_UNLIKELY(!treeOp)) {
1176 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1177 return;
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
1210 * reasons.
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)) {
1223 isInSVGOddPCData++;
1227 if (aNamespace != kNameSpaceID_XHTML) {
1228 return;
1230 if (aName == nsGkAtoms::body || aName == nsGkAtoms::frameset) {
1231 if (mBuilder) {
1232 // InnerHTML and DOMParser shouldn't start layout anyway
1233 return;
1235 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1236 if (MOZ_UNLIKELY(!treeOp)) {
1237 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1238 return;
1240 treeOp->Init(mozilla::AsVariant(opStartLayout()));
1241 return;
1243 if (nsIContent::RequiresDoneCreatingElement(kNameSpaceID_XHTML, aName)) {
1244 if (mBuilder) {
1245 nsHtml5TreeOperation::DoneCreatingElement(
1246 static_cast<nsIContent*>(aElement));
1247 } else {
1248 opDoneCreatingElement operation(aElement);
1249 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1251 return;
1253 if (mGenerateSpeculativeLoads && aName == nsGkAtoms::picture) {
1254 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1255 mSpeculativeLoadQueue.AppendElement()->InitOpenPicture();
1256 return;
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) {
1274 return;
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)) {
1281 isInSVGOddPCData--;
1284 // we now have only SVG and HTML
1285 if (aName == nsGkAtoms::script) {
1286 if (mPreventScriptExecution) {
1287 if (mBuilder) {
1288 nsHtml5TreeOperation::PreventScriptExecution(
1289 static_cast<nsIContent*>(aElement));
1290 return;
1292 opPreventScriptExecution operation(aElement);
1293 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
1294 return;
1296 if (mBuilder) {
1297 return;
1299 if (mCurrentHtmlScriptCannotDocumentWriteOrBlock) {
1300 NS_ASSERTION(
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);
1306 return;
1308 opRunScriptThatCannotDocumentWriteOrBlock operation(aElement);
1309 treeOp->Init(mozilla::AsVariant(operation));
1310 mCurrentHtmlScriptCannotDocumentWriteOrBlock = false;
1311 return;
1313 requestSuspension();
1314 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1315 if (MOZ_UNLIKELY(!treeOp)) {
1316 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1317 return;
1319 opRunScriptThatMayDocumentWriteOrBlock operation(aElement, nullptr);
1320 treeOp->Init(mozilla::AsVariant(operation));
1321 return;
1323 // Some nodes need DoneAddingChildren() called to initialize
1324 // properly (e.g. form state restoration).
1325 if (nsIContent::RequiresDoneAddingChildren(aNamespace, aName)) {
1326 if (mBuilder) {
1327 nsHtml5TreeOperation::DoneAddingChildren(
1328 static_cast<nsIContent*>(aElement));
1329 return;
1331 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1332 if (MOZ_UNLIKELY(!treeOp)) {
1333 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1334 return;
1336 opDoneAddingChildren operation(aElement);
1337 treeOp->Init(mozilla::AsVariant(operation));
1338 if (aNamespace == kNameSpaceID_XHTML && aName == nsGkAtoms::head) {
1339 if (tokenizer->TemplatePushedOrHeadPopped()) {
1340 requestSuspension();
1343 return;
1345 if (aName == nsGkAtoms::style ||
1346 (aNamespace == kNameSpaceID_XHTML && aName == nsGkAtoms::link)) {
1347 if (mBuilder) {
1348 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
1349 "Scripts must be blocked.");
1350 mBuilder->UpdateStyleSheet(static_cast<nsIContent*>(aElement));
1351 return;
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);
1364 return;
1366 opUpdateStyleSheet operation(aElement);
1367 treeOp->Init(mozilla::AsVariant(operation));
1368 return;
1370 if (aNamespace == kNameSpaceID_SVG) {
1371 if (aName == nsGkAtoms::svg) {
1372 if (!scriptingEnabled || mPreventScriptExecution) {
1373 return;
1375 if (mBuilder) {
1376 nsHtml5TreeOperation::SvgLoad(static_cast<nsIContent*>(aElement));
1377 return;
1379 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1380 if (MOZ_UNLIKELY(!treeOp)) {
1381 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1382 return;
1384 opSvgLoad operation(aElement);
1385 treeOp->Init(mozilla::AsVariant(operation));
1387 return;
1390 if (mGenerateSpeculativeLoads && aName == nsGkAtoms::picture) {
1391 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
1392 mSpeculativeLoadQueue.AppendElement()->InitEndPicture();
1393 return;
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()) {
1418 return false;
1420 if (worstCase.value() > MAX_POWER_OF_TWO_IN_INT32) {
1421 return false;
1423 if (!charBuffer) {
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.
1427 worstCase += 1;
1429 charBuffer = jArray<char16_t, int32_t>::newFallibleJArray(
1430 mozilla::RoundUpPow2(worstCase.value()));
1431 if (!charBuffer) {
1432 return false;
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()));
1438 if (!newBuf) {
1439 return false;
1441 memcpy(newBuf, charBuffer, sizeof(char16_t) * size_t(charBufferLen));
1442 charBuffer = newBuf;
1444 return true;
1447 nsIContentHandle* nsHtml5TreeBuilder::AllocateContentHandle() {
1448 if (MOZ_UNLIKELY(mBuilder)) {
1449 MOZ_ASSERT_UNREACHABLE("Must never allocate a handle with builder.");
1450 return nullptr;
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);
1456 mHandlesUsed = 0;
1458 #ifdef DEBUG
1459 mHandles[mHandlesUsed] = reinterpret_cast<nsIContent*>(uintptr_t(0xC0DEDBAD));
1460 #endif
1461 return &mHandles[mHandlesUsed++];
1464 bool nsHtml5TreeBuilder::HasScriptThatMayDocumentWriteOrBlock() {
1465 uint32_t len = mOpQueue.Length();
1466 if (!len) {
1467 return false;
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.");
1475 return false;
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.
1484 flushCharacters();
1486 FlushLoads();
1488 if (mOpSink) {
1489 bool hasOps = !mOpQueue.IsEmpty();
1490 if (hasOps) {
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 "
1496 "length isn't 1.");
1497 MOZ_ASSERT(mOpQueue[0].IsMarkAsBroken(),
1498 "Tree builder is broken but the op in queue is not marked "
1499 "as broken.");
1501 if (!mOpSink->MoveOpsFrom(mOpQueue)) {
1502 return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
1505 return hasOps;
1507 // no op sink: throw away ops
1508 mOpQueue.Clear();
1509 return false;
1512 void nsHtml5TreeBuilder::FlushLoads() {
1513 if (MOZ_UNLIKELY(mBuilder)) {
1514 MOZ_ASSERT_UNREACHABLE("Must never flush loads with builder.");
1515 return;
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)?");
1537 if (mViewSource) {
1538 mViewSource->UpdateCharsetSource(aCharsetSource);
1539 return;
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);
1551 return;
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.");
1561 return;
1563 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1564 if (MOZ_UNLIKELY(!treeOp)) {
1565 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1566 return;
1568 opCharsetSwitchTo opeation(aEncoding, aCharsetSource, aLineNumber);
1569 treeOp->Init(mozilla::AsVariant(opeation));
1572 void nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId,
1573 bool aError,
1574 int32_t aLineNumber) {
1575 if (MOZ_UNLIKELY(mBuilder)) {
1576 MOZ_ASSERT_UNREACHABLE("Must never complain about charset with builder.");
1577 return;
1580 if (mSpeculativeLoadStage) {
1581 mSpeculativeLoadQueue.AppendElement()->InitMaybeComplainAboutCharset(
1582 aMsgId, aError, aLineNumber);
1583 } else {
1584 opMaybeComplainAboutCharset opeartion(const_cast<char*>(aMsgId), aError,
1585 aLineNumber);
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.");
1593 return;
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.");
1604 return;
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();
1615 mHandlesUsed = 0;
1618 void nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv) {
1619 if (MOZ_UNLIKELY(mBuilder)) {
1620 MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
1621 return;
1623 mBroken = aRv;
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) {
1630 if (mBuilder) {
1631 MarkAsBrokenAndRequestSuspensionWithBuilder(aRv);
1632 return;
1634 mBroken = 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) {
1650 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,
1679 false);
1680 needToDropLF = false;
1683 // DocumentModeHandler
1684 void nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m) {
1685 if (mBuilder) {
1686 mBuilder->SetDocumentMode(m);
1687 return;
1689 if (mSpeculativeLoadStage) {
1690 mSpeculativeLoadQueue.AppendElement()->InitSetDocumentMode(m);
1691 return;
1693 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1694 if (MOZ_UNLIKELY(!treeOp)) {
1695 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1696 return;
1698 treeOp->Init(mozilla::AsVariant(m));
1701 nsIContentHandle* nsHtml5TreeBuilder::getDocumentFragmentForTemplate(
1702 nsIContentHandle* aTemplate) {
1703 if (mBuilder) {
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);
1710 return nullptr;
1712 nsIContentHandle* fragHandle = AllocateContentHandle();
1713 opGetDocumentFragmentForTemplate operation(aTemplate, fragHandle);
1714 treeOp->Init(mozilla::AsVariant(operation));
1715 return fragHandle;
1718 void nsHtml5TreeBuilder::setDocumentFragmentForTemplate(
1719 nsIContentHandle* aTemplate, nsIContentHandle* aFragment) {
1720 if (mBuilder) {
1721 nsHtml5TreeOperation::SetDocumentFragmentForTemplate(
1722 static_cast<nsIContent*>(aTemplate),
1723 static_cast<nsIContent*>(aFragment));
1724 return;
1727 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1728 if (MOZ_UNLIKELY(!treeOp)) {
1729 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1730 return;
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;
1745 } else {
1746 return nullptr;
1749 if (mBuilder) {
1750 nsIContent* root = nsContentUtils::AttachDeclarativeShadowRoot(
1751 static_cast<nsIContent*>(aHost), mode, aShadowRootIsClonable,
1752 aShadowRootIsSerializable, aShadowRootDelegatesFocus);
1753 if (!root) {
1754 nsContentUtils::LogSimpleConsoleError(
1755 u"Failed to attach Declarative Shadow DOM."_ns, "DOM"_ns,
1756 mBuilder->GetDocument()->IsInPrivateBrowsing(),
1757 mBuilder->GetDocument()->IsInChromeDocShell());
1759 return root;
1762 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
1763 if (MOZ_UNLIKELY(!treeOp)) {
1764 MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
1765 return nullptr;
1767 nsIContentHandle* fragHandle = AllocateContentHandle();
1768 opGetShadowRootFromHost operation(
1769 aHost, fragHandle, aTemplateNode, mode, aShadowRootIsClonable,
1770 aShadowRootIsSerializable, aShadowRootDelegatesFocus);
1771 treeOp->Init(mozilla::AsVariant(operation));
1772 return fragHandle;
1775 nsIContentHandle* nsHtml5TreeBuilder::getFormPointerForContext(
1776 nsIContentHandle* aContext) {
1777 MOZ_ASSERT(mBuilder, "Must have builder.");
1778 if (!aContext) {
1779 return nullptr;
1782 MOZ_ASSERT(NS_IsMainThread());
1784 // aContext must always be an element that already exists
1785 // in the document.
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;
1795 break;
1797 currentAncestor = currentAncestor->GetParent();
1800 if (!nearestForm) {
1801 return nullptr;
1804 return nearestForm;
1807 // Error reporting
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,
1844 nsAtom* aName) {
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",
2039 aName);
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",
2089 aName, aOther);
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");