Bug 1247796. Use keyboardFocusIndicatorColor for ActiveBorder system color keyword...
[gecko.git] / parser / html / nsHtml5TreeBuilderCppSupplement.h
blob3e59df6627b75c016ec974a341cfa7f468054f97
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 "nsError.h"
8 #include "nsIPresShell.h"
9 #include "nsNodeUtils.h"
10 #include "nsIFrame.h"
11 #include "mozilla/Likely.h"
12 #include "mozilla/UniquePtr.h"
14 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder)
15 : scriptingEnabled(false)
16 , fragment(false)
17 , contextName(nullptr)
18 , contextNamespace(kNameSpaceID_None)
19 , contextNode(nullptr)
20 , formPointer(nullptr)
21 , headPointer(nullptr)
22 , mBuilder(aBuilder)
23 , mViewSource(nullptr)
24 , mOpSink(nullptr)
25 , mHandles(nullptr)
26 , mHandlesUsed(0)
27 , mSpeculativeLoadStage(nullptr)
28 , mBroken(NS_OK)
29 , mCurrentHtmlScriptIsAsyncOrDefer(false)
30 , mPreventScriptExecution(false)
31 #ifdef DEBUG
32 , mActive(false)
33 #endif
35 MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
38 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
39 nsHtml5TreeOpStage* aStage)
40 : scriptingEnabled(false)
41 , fragment(false)
42 , contextName(nullptr)
43 , contextNamespace(kNameSpaceID_None)
44 , contextNode(nullptr)
45 , formPointer(nullptr)
46 , headPointer(nullptr)
47 , mBuilder(nullptr)
48 , mViewSource(nullptr)
49 , mOpSink(aOpSink)
50 , mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
51 , mHandlesUsed(0)
52 , mSpeculativeLoadStage(aStage)
53 , mBroken(NS_OK)
54 , mCurrentHtmlScriptIsAsyncOrDefer(false)
55 , mPreventScriptExecution(false)
56 #ifdef DEBUG
57 , mActive(false)
58 #endif
60 MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
63 nsHtml5TreeBuilder::~nsHtml5TreeBuilder()
65 MOZ_COUNT_DTOR(nsHtml5TreeBuilder);
66 NS_ASSERTION(!mActive, "nsHtml5TreeBuilder deleted without ever calling end() on it!");
67 mOpQueue.Clear();
70 nsIContentHandle*
71 nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
72 nsHtml5HtmlAttributes* aAttributes,
73 nsIContentHandle* aIntendedParent)
75 NS_PRECONDITION(aAttributes, "Got null attributes.");
76 NS_PRECONDITION(aName, "Got null name.");
77 NS_PRECONDITION(aNamespace == kNameSpaceID_XHTML ||
78 aNamespace == kNameSpaceID_SVG ||
79 aNamespace == kNameSpaceID_MathML,
80 "Bogus namespace.");
82 if (mBuilder) {
83 nsCOMPtr<nsIAtom> name = nsHtml5TreeOperation::Reget(aName);
85 nsIContent* intendedParent = aIntendedParent ?
86 static_cast<nsIContent*>(aIntendedParent) : nullptr;
88 // intendedParent == nullptr is a special case where the
89 // intended parent is the document.
90 nsNodeInfoManager* nodeInfoManager = intendedParent ?
91 intendedParent->OwnerDoc()->NodeInfoManager() :
92 mBuilder->GetNodeInfoManager();
94 nsIContent* elem =
95 nsHtml5TreeOperation::CreateElement(aNamespace,
96 name,
97 aAttributes,
98 mozilla::dom::FROM_PARSER_FRAGMENT,
99 nodeInfoManager,
100 mBuilder);
101 if (MOZ_UNLIKELY(aAttributes != tokenizer->GetAttributes() &&
102 aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES)) {
103 delete aAttributes;
105 return elem;
108 nsIContentHandle* content = AllocateContentHandle();
109 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
110 NS_ASSERTION(treeOp, "Tree op allocation failed.");
111 treeOp->Init(aNamespace,
112 aName,
113 aAttributes,
114 content,
115 aIntendedParent,
116 !!mSpeculativeLoadStage);
117 // mSpeculativeLoadStage is non-null only in the off-the-main-thread
118 // tree builder, which handles the network stream
120 // Start wall of code for speculative loading and line numbers
122 if (mSpeculativeLoadStage) {
123 switch (aNamespace) {
124 case kNameSpaceID_XHTML:
125 if (nsHtml5Atoms::img == aName) {
126 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
127 nsString* srcset =
128 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
129 nsString* crossOrigin =
130 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
131 nsString* referrerPolicy =
132 aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY);
133 nsString* sizes =
134 aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
135 mSpeculativeLoadQueue.AppendElement()->
136 InitImage(url ? *url : NullString(),
137 crossOrigin ? *crossOrigin : NullString(),
138 referrerPolicy ? *referrerPolicy : NullString(),
139 srcset ? *srcset : NullString(),
140 sizes ? *sizes : NullString());
141 } else if (nsHtml5Atoms::source == aName) {
142 nsString* srcset =
143 aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
144 // Sources without srcset cannot be selected. The source could also be
145 // for a media element, but in that context doesn't use srcset. See
146 // comments in nsHtml5SpeculativeLoad.h about <picture> preloading
147 if (srcset) {
148 nsString* sizes =
149 aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
150 nsString* type =
151 aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
152 nsString* media =
153 aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA);
154 mSpeculativeLoadQueue.AppendElement()->
155 InitPictureSource(*srcset,
156 sizes ? *sizes : NullString(),
157 type ? *type : NullString(),
158 media ? *media : NullString());
160 } else if (nsHtml5Atoms::script == aName) {
161 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
162 NS_ASSERTION(treeOp, "Tree op allocation failed.");
163 treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
165 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
166 if (url) {
167 nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
168 nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
169 nsString* crossOrigin =
170 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
171 nsString* integrity =
172 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
173 mSpeculativeLoadQueue.AppendElement()->
174 InitScript(*url,
175 (charset) ? *charset : EmptyString(),
176 (type) ? *type : EmptyString(),
177 (crossOrigin) ? *crossOrigin : NullString(),
178 (integrity) ? *integrity : NullString(),
179 mode == NS_HTML5TREE_BUILDER_IN_HEAD);
180 mCurrentHtmlScriptIsAsyncOrDefer =
181 aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
182 aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
184 } else if (nsHtml5Atoms::link == aName) {
185 nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL);
186 // Not splitting on space here is bogus but the old parser didn't even
187 // do a case-insensitive check.
188 if (rel) {
189 if (rel->LowerCaseEqualsASCII("stylesheet")) {
190 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
191 if (url) {
192 nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
193 nsString* crossOrigin =
194 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
195 nsString* integrity =
196 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
197 mSpeculativeLoadQueue.AppendElement()->
198 InitStyle(*url,
199 (charset) ? *charset : EmptyString(),
200 (crossOrigin) ? *crossOrigin : NullString(),
201 (integrity) ? *integrity : NullString());
203 } else if (rel->LowerCaseEqualsASCII("preconnect")) {
204 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
205 if (url) {
206 nsString* crossOrigin =
207 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
208 mSpeculativeLoadQueue.AppendElement()->
209 InitPreconnect(*url, (crossOrigin) ? *crossOrigin : NullString());
213 } else if (nsHtml5Atoms::video == aName) {
214 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
215 if (url) {
216 mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString(),
217 NullString(),
218 NullString(),
219 NullString());
221 } else if (nsHtml5Atoms::style == aName) {
222 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
223 NS_ASSERTION(treeOp, "Tree op allocation failed.");
224 treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber());
225 } else if (nsHtml5Atoms::html == aName) {
226 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
227 if (url) {
228 mSpeculativeLoadQueue.AppendElement()->InitManifest(*url);
229 } else {
230 mSpeculativeLoadQueue.AppendElement()->InitManifest(EmptyString());
232 } else if (nsHtml5Atoms::base == aName) {
233 nsString* url =
234 aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
235 if (url) {
236 mSpeculativeLoadQueue.AppendElement()->InitBase(*url);
238 } else if (nsHtml5Atoms::meta == aName) {
239 if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString(
240 "content-security-policy",
241 aAttributes->getValue(nsHtml5AttributeName::ATTR_HTTP_EQUIV))) {
242 nsString* csp = aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
243 if (csp) {
244 mSpeculativeLoadQueue.AppendElement()->InitMetaCSP(*csp);
247 else if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString(
248 "referrer",
249 aAttributes->getValue(nsHtml5AttributeName::ATTR_NAME))) {
250 nsString* referrerPolicy = aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
251 if (referrerPolicy) {
252 mSpeculativeLoadQueue.AppendElement()->InitMetaReferrerPolicy(*referrerPolicy);
256 break;
257 case kNameSpaceID_SVG:
258 if (nsHtml5Atoms::image == aName) {
259 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
260 if (url) {
261 mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString(),
262 NullString(),
263 NullString(),
264 NullString());
266 } else if (nsHtml5Atoms::script == aName) {
267 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
268 NS_ASSERTION(treeOp, "Tree op allocation failed.");
269 treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
271 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
272 if (url) {
273 nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
274 nsString* crossOrigin =
275 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
276 nsString* integrity =
277 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
278 mSpeculativeLoadQueue.AppendElement()->
279 InitScript(*url,
280 EmptyString(),
281 (type) ? *type : EmptyString(),
282 (crossOrigin) ? *crossOrigin : NullString(),
283 (integrity) ? *integrity : NullString(),
284 mode == NS_HTML5TREE_BUILDER_IN_HEAD);
286 } else if (nsHtml5Atoms::style == aName) {
287 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
288 NS_ASSERTION(treeOp, "Tree op allocation failed.");
289 treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber());
291 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
292 if (url) {
293 nsString* crossOrigin =
294 aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
295 nsString* integrity =
296 aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
297 mSpeculativeLoadQueue.AppendElement()->
298 InitStyle(*url, EmptyString(),
299 (crossOrigin) ? *crossOrigin : NullString(),
300 (integrity) ? *integrity : NullString());
303 break;
305 } else if (aNamespace != kNameSpaceID_MathML) {
306 // No speculative loader--just line numbers and defer/async check
307 if (nsHtml5Atoms::style == aName) {
308 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
309 NS_ASSERTION(treeOp, "Tree op allocation failed.");
310 treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber());
311 } else if (nsHtml5Atoms::script == aName) {
312 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
313 NS_ASSERTION(treeOp, "Tree op allocation failed.");
314 treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
315 if (aNamespace == kNameSpaceID_XHTML) {
316 mCurrentHtmlScriptIsAsyncOrDefer =
317 aAttributes->contains(nsHtml5AttributeName::ATTR_SRC) &&
318 (aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
319 aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER));
321 } else if (aNamespace == kNameSpaceID_XHTML) {
322 if (nsHtml5Atoms::html == aName) {
323 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
324 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
325 NS_ASSERTION(treeOp, "Tree op allocation failed.");
326 if (url) {
327 treeOp->Init(eTreeOpProcessOfflineManifest, *url);
328 } else {
329 treeOp->Init(eTreeOpProcessOfflineManifest, EmptyString());
331 } else if (nsHtml5Atoms::base == aName && mViewSource) {
332 nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
333 if (url) {
334 mViewSource->AddBase(*url);
340 // End wall of code for speculative loading
342 return content;
345 nsIContentHandle*
346 nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
347 nsHtml5HtmlAttributes* aAttributes,
348 nsIContentHandle* aFormElement,
349 nsIContentHandle* aIntendedParent)
351 nsIContentHandle* content = createElement(aNamespace, aName, aAttributes,
352 aIntendedParent);
353 if (aFormElement) {
354 if (mBuilder) {
355 nsHtml5TreeOperation::SetFormElement(static_cast<nsIContent*>(content),
356 static_cast<nsIContent*>(aFormElement));
357 } else {
358 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
359 NS_ASSERTION(treeOp, "Tree op allocation failed.");
360 treeOp->Init(eTreeOpSetFormElement, content, aFormElement);
363 return content;
366 nsIContentHandle*
367 nsHtml5TreeBuilder::createHtmlElementSetAsRoot(nsHtml5HtmlAttributes* aAttributes)
369 nsIContentHandle* content = createElement(kNameSpaceID_XHTML,
370 nsHtml5Atoms::html,
371 aAttributes,
372 nullptr);
373 if (mBuilder) {
374 nsresult rv = nsHtml5TreeOperation::AppendToDocument(static_cast<nsIContent*>(content),
375 mBuilder);
376 if (NS_FAILED(rv)) {
377 MarkAsBrokenAndRequestSuspension(rv);
379 } else {
380 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
381 NS_ASSERTION(treeOp, "Tree op allocation failed.");
382 treeOp->Init(eTreeOpAppendToDocument, content);
384 return content;
387 nsIContentHandle*
388 nsHtml5TreeBuilder::createAndInsertFosterParentedElement(int32_t aNamespace, nsIAtom* aName,
389 nsHtml5HtmlAttributes* aAttributes,
390 nsIContentHandle* aFormElement,
391 nsIContentHandle* aTable,
392 nsIContentHandle* aStackParent)
394 NS_PRECONDITION(aTable, "Null table");
395 NS_PRECONDITION(aStackParent, "Null stack parent");
397 if (mBuilder) {
398 // Get the foster parent to use as the intended parent when creating
399 // the child element.
400 nsIContent* fosterParent = nsHtml5TreeOperation::GetFosterParent(
401 static_cast<nsIContent*>(aTable),
402 static_cast<nsIContent*>(aStackParent));
404 nsIContentHandle* child = createElement(aNamespace, aName, aAttributes,
405 aFormElement, fosterParent);
407 insertFosterParentedChild(child, aTable, aStackParent);
409 return child;
412 // Tree op to get the foster parent that we use as the intended parent
413 // when creating the child element.
414 nsHtml5TreeOperation* fosterParentTreeOp = mOpQueue.AppendElement();
415 NS_ASSERTION(fosterParentTreeOp, "Tree op allocation failed.");
416 nsIContentHandle* fosterParentHandle = AllocateContentHandle();
417 fosterParentTreeOp->Init(eTreeOpGetFosterParent, aTable,
418 aStackParent, fosterParentHandle);
420 // Create the element with the correct intended parent.
421 nsIContentHandle* child = createElement(aNamespace, aName, aAttributes,
422 aFormElement, fosterParentHandle);
424 // Insert the child into the foster parent.
425 insertFosterParentedChild(child, aTable, aStackParent);
427 return child;
430 void
431 nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement)
433 NS_PRECONDITION(aElement, "Null element");
435 if (mBuilder) {
436 nsHtml5TreeOperation::Detach(static_cast<nsIContent*>(aElement),
437 mBuilder);
438 return;
441 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
442 NS_ASSERTION(treeOp, "Tree op allocation failed.");
443 treeOp->Init(eTreeOpDetach, aElement);
446 void
447 nsHtml5TreeBuilder::appendElement(nsIContentHandle* aChild, nsIContentHandle* aParent)
449 NS_PRECONDITION(aChild, "Null child");
450 NS_PRECONDITION(aParent, "Null parent");
451 if (deepTreeSurrogateParent) {
452 return;
455 if (mBuilder) {
456 nsresult rv = nsHtml5TreeOperation::Append(static_cast<nsIContent*>(aChild),
457 static_cast<nsIContent*>(aParent),
458 mBuilder);
459 if (NS_FAILED(rv)) {
460 MarkAsBrokenAndRequestSuspension(rv);
462 return;
465 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
466 NS_ASSERTION(treeOp, "Tree op allocation failed.");
467 treeOp->Init(eTreeOpAppend, aChild, aParent);
470 void
471 nsHtml5TreeBuilder::appendChildrenToNewParent(nsIContentHandle* aOldParent, nsIContentHandle* aNewParent)
473 NS_PRECONDITION(aOldParent, "Null old parent");
474 NS_PRECONDITION(aNewParent, "Null new parent");
476 if (mBuilder) {
477 nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent(
478 static_cast<nsIContent*>(aOldParent),
479 static_cast<nsIContent*>(aNewParent),
480 mBuilder);
481 if (NS_FAILED(rv)) {
482 MarkAsBrokenAndRequestSuspension(rv);
484 return;
487 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
488 NS_ASSERTION(treeOp, "Tree op allocation failed.");
489 treeOp->Init(eTreeOpAppendChildrenToNewParent, aOldParent, aNewParent);
492 void
493 nsHtml5TreeBuilder::insertFosterParentedCharacters(char16_t* aBuffer, int32_t aStart, int32_t aLength, nsIContentHandle* aTable, nsIContentHandle* aStackParent)
495 NS_PRECONDITION(aBuffer, "Null buffer");
496 NS_PRECONDITION(aTable, "Null table");
497 NS_PRECONDITION(aStackParent, "Null stack parent");
498 MOZ_ASSERT(!aStart, "aStart must always be zero.");
500 if (mBuilder) {
501 nsresult rv = nsHtml5TreeOperation::FosterParentText(
502 static_cast<nsIContent*>(aStackParent),
503 aBuffer, // XXX aStart always ignored???
504 aLength,
505 static_cast<nsIContent*>(aTable),
506 mBuilder);
507 if (NS_FAILED(rv)) {
508 MarkAsBrokenAndRequestSuspension(rv);
510 return;
513 char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
514 if (!bufferCopy) {
515 // Just assigning mBroken instead of generating tree op. The caller
516 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
517 mBroken = NS_ERROR_OUT_OF_MEMORY;
518 requestSuspension();
519 return;
522 memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
524 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
525 NS_ASSERTION(treeOp, "Tree op allocation failed.");
526 treeOp->Init(eTreeOpFosterParentText, bufferCopy, aLength, aStackParent, aTable);
529 void
530 nsHtml5TreeBuilder::insertFosterParentedChild(nsIContentHandle* aChild, nsIContentHandle* aTable, nsIContentHandle* aStackParent)
532 NS_PRECONDITION(aChild, "Null child");
533 NS_PRECONDITION(aTable, "Null table");
534 NS_PRECONDITION(aStackParent, "Null stack parent");
536 if (mBuilder) {
537 nsresult rv = nsHtml5TreeOperation::FosterParent(
538 static_cast<nsIContent*>(aChild),
539 static_cast<nsIContent*>(aStackParent),
540 static_cast<nsIContent*>(aTable),
541 mBuilder);
542 if (NS_FAILED(rv)) {
543 MarkAsBrokenAndRequestSuspension(rv);
545 return;
548 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
549 NS_ASSERTION(treeOp, "Tree op allocation failed.");
550 treeOp->Init(eTreeOpFosterParent, aChild, aStackParent, aTable);
553 void
554 nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength)
556 NS_PRECONDITION(aBuffer, "Null buffer");
557 NS_PRECONDITION(aParent, "Null parent");
558 MOZ_ASSERT(!aStart, "aStart must always be zero.");
560 if (mBuilder) {
561 nsresult rv = nsHtml5TreeOperation::AppendText(
562 aBuffer, // XXX aStart always ignored???
563 aLength,
564 static_cast<nsIContent*>(deepTreeSurrogateParent ?
565 deepTreeSurrogateParent : aParent),
566 mBuilder);
567 if (NS_FAILED(rv)) {
568 MarkAsBrokenAndRequestSuspension(rv);
570 return;
573 char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
574 if (!bufferCopy) {
575 // Just assigning mBroken instead of generating tree op. The caller
576 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
577 mBroken = NS_ERROR_OUT_OF_MEMORY;
578 requestSuspension();
579 return;
582 memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
584 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
585 NS_ASSERTION(treeOp, "Tree op allocation failed.");
586 treeOp->Init(eTreeOpAppendText, bufferCopy, aLength,
587 deepTreeSurrogateParent ? deepTreeSurrogateParent : aParent);
590 void
591 nsHtml5TreeBuilder::appendIsindexPrompt(nsIContentHandle* aParent)
593 NS_PRECONDITION(aParent, "Null parent");
595 if (mBuilder) {
596 nsresult rv = nsHtml5TreeOperation::AppendIsindexPrompt(
597 static_cast<nsIContent*>(aParent),
598 mBuilder);
599 if (NS_FAILED(rv)) {
600 MarkAsBrokenAndRequestSuspension(rv);
602 return;
605 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
606 NS_ASSERTION(treeOp, "Tree op allocation failed.");
607 treeOp->Init(eTreeOpAppendIsindexPrompt, aParent);
610 void
611 nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength)
613 NS_PRECONDITION(aBuffer, "Null buffer");
614 NS_PRECONDITION(aParent, "Null parent");
615 MOZ_ASSERT(!aStart, "aStart must always be zero.");
617 if (deepTreeSurrogateParent) {
618 return;
621 if (mBuilder) {
622 nsresult rv = nsHtml5TreeOperation::AppendComment(
623 static_cast<nsIContent*>(aParent),
624 aBuffer, // XXX aStart always ignored???
625 aLength,
626 mBuilder);
627 if (NS_FAILED(rv)) {
628 MarkAsBrokenAndRequestSuspension(rv);
630 return;
633 char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
634 if (!bufferCopy) {
635 // Just assigning mBroken instead of generating tree op. The caller
636 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
637 mBroken = NS_ERROR_OUT_OF_MEMORY;
638 requestSuspension();
639 return;
642 memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
644 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
645 NS_ASSERTION(treeOp, "Tree op allocation failed.");
646 treeOp->Init(eTreeOpAppendComment, bufferCopy, aLength, aParent);
649 void
650 nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer, int32_t aStart, int32_t aLength)
652 NS_PRECONDITION(aBuffer, "Null buffer");
653 MOZ_ASSERT(!aStart, "aStart must always be zero.");
655 if (mBuilder) {
656 nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument(
657 aBuffer, // XXX aStart always ignored???
658 aLength,
659 mBuilder);
660 if (NS_FAILED(rv)) {
661 MarkAsBrokenAndRequestSuspension(rv);
663 return;
666 char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
667 if (!bufferCopy) {
668 // Just assigning mBroken instead of generating tree op. The caller
669 // of tokenizeBuffer() will call MarkAsBroken() as appropriate.
670 mBroken = NS_ERROR_OUT_OF_MEMORY;
671 requestSuspension();
672 return;
675 memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
677 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
678 NS_ASSERTION(treeOp, "Tree op allocation failed.");
679 treeOp->Init(eTreeOpAppendCommentToDocument, bufferCopy, aLength);
682 void
683 nsHtml5TreeBuilder::addAttributesToElement(nsIContentHandle* aElement, nsHtml5HtmlAttributes* aAttributes)
685 NS_PRECONDITION(aElement, "Null element");
686 NS_PRECONDITION(aAttributes, "Null attributes");
688 if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
689 return;
692 if (mBuilder) {
693 MOZ_ASSERT(aAttributes == tokenizer->GetAttributes(),
694 "Using attribute other than the tokenizer's to add to body or html.");
695 nsresult rv = nsHtml5TreeOperation::AddAttributes(
696 static_cast<nsIContent*>(aElement),
697 aAttributes,
698 mBuilder);
699 if (NS_FAILED(rv)) {
700 MarkAsBrokenAndRequestSuspension(rv);
702 return;
705 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
706 NS_ASSERTION(treeOp, "Tree op allocation failed.");
707 treeOp->Init(aElement, aAttributes);
710 void
711 nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement)
713 NS_PRECONDITION(aElement, "Null element");
715 if (mBuilder) {
716 nsHtml5TreeOperation::MarkMalformedIfScript(
717 static_cast<nsIContent*>(aElement));
718 return;
721 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
722 NS_ASSERTION(treeOp, "Tree op allocation failed.");
723 treeOp->Init(eTreeOpMarkMalformedIfScript, aElement);
726 void
727 nsHtml5TreeBuilder::start(bool fragment)
729 mCurrentHtmlScriptIsAsyncOrDefer = false;
730 deepTreeSurrogateParent = nullptr;
731 #ifdef DEBUG
732 mActive = true;
733 #endif
736 void
737 nsHtml5TreeBuilder::end()
739 mOpQueue.Clear();
740 #ifdef DEBUG
741 mActive = false;
742 #endif
745 void
746 nsHtml5TreeBuilder::appendDoctypeToDocument(nsIAtom* aName, nsString* aPublicId, nsString* aSystemId)
748 NS_PRECONDITION(aName, "Null name");
750 if (mBuilder) {
751 nsCOMPtr<nsIAtom> name = nsHtml5TreeOperation::Reget(aName);
752 nsresult rv =
753 nsHtml5TreeOperation::AppendDoctypeToDocument(name,
754 *aPublicId,
755 *aSystemId,
756 mBuilder);
757 if (NS_FAILED(rv)) {
758 MarkAsBrokenAndRequestSuspension(rv);
760 return;
763 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
764 NS_ASSERTION(treeOp, "Tree op allocation failed.");
765 treeOp->Init(aName, *aPublicId, *aSystemId);
766 // nsXMLContentSink can flush here, but what's the point?
767 // It can also interrupt here, but we can't.
770 void
771 nsHtml5TreeBuilder::elementPushed(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement)
773 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!");
774 NS_ASSERTION(aName, "Element doesn't have local name!");
775 NS_ASSERTION(aElement, "No element!");
777 * The frame constructor uses recursive algorithms, so it can't deal with
778 * arbitrarily deep trees. This is especially a problem on Windows where
779 * the permitted depth of the runtime stack is rather small.
781 * The following is a protection against author incompetence--not against
782 * malice. There are other ways to make the DOM deep anyway.
784 * The basic idea is that when the tree builder stack gets too deep,
785 * append operations no longer append to the node that the HTML parsing
786 * algorithm says they should but instead text nodes are append to the last
787 * element that was seen before a magic tree builder stack threshold was
788 * reached and element and comment nodes aren't appended to the DOM at all.
790 * However, for security reasons, non-child descendant text nodes inside an
791 * SVG script or style element should not become children. Also, non-cell
792 * table elements shouldn't be used as surrogate parents for user experience
793 * reasons.
795 if (!deepTreeSurrogateParent && currentPtr >= MAX_REFLOW_DEPTH &&
796 !(aName == nsHtml5Atoms::script ||
797 aName == nsHtml5Atoms::table ||
798 aName == nsHtml5Atoms::thead ||
799 aName == nsHtml5Atoms::tfoot ||
800 aName == nsHtml5Atoms::tbody ||
801 aName == nsHtml5Atoms::tr ||
802 aName == nsHtml5Atoms::colgroup ||
803 aName == nsHtml5Atoms::style)) {
804 deepTreeSurrogateParent = aElement;
806 if (aNamespace != kNameSpaceID_XHTML) {
807 return;
809 if (aName == nsHtml5Atoms::body || aName == nsHtml5Atoms::frameset) {
810 if (mBuilder) {
811 // InnerHTML and DOMParser shouldn't start layout anyway
812 return;
814 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
815 NS_ASSERTION(treeOp, "Tree op allocation failed.");
816 treeOp->Init(eTreeOpStartLayout);
817 return;
819 if (aName == nsHtml5Atoms::input ||
820 aName == nsHtml5Atoms::button) {
821 if (mBuilder) {
822 nsHtml5TreeOperation::DoneCreatingElement(static_cast<nsIContent*>(aElement));
823 } else {
824 mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
826 return;
828 if (aName == nsHtml5Atoms::audio ||
829 aName == nsHtml5Atoms::video ||
830 aName == nsHtml5Atoms::menuitem) {
831 if (mBuilder) {
832 nsHtml5TreeOperation::DoneCreatingElement(static_cast<nsIContent*>(aElement));
833 } else {
834 mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
836 return;
838 if (mSpeculativeLoadStage && aName == nsHtml5Atoms::picture) {
839 // mSpeculativeLoadStage is non-null only in the off-the-main-thread
840 // tree builder, which handles the network stream
842 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
843 mSpeculativeLoadQueue.AppendElement()->InitOpenPicture();
847 void
848 nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement)
850 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!");
851 NS_ASSERTION(aName, "Element doesn't have local name!");
852 NS_ASSERTION(aElement, "No element!");
853 if (deepTreeSurrogateParent && currentPtr <= MAX_REFLOW_DEPTH) {
854 deepTreeSurrogateParent = nullptr;
856 if (aNamespace == kNameSpaceID_MathML) {
857 return;
859 // we now have only SVG and HTML
860 if (aName == nsHtml5Atoms::script) {
861 if (mPreventScriptExecution) {
862 if (mBuilder) {
863 nsHtml5TreeOperation::PreventScriptExecution(static_cast<nsIContent*>(aElement));
864 return;
866 mOpQueue.AppendElement()->Init(eTreeOpPreventScriptExecution, aElement);
867 return;
869 if (mBuilder) {
870 return;
872 if (mCurrentHtmlScriptIsAsyncOrDefer) {
873 NS_ASSERTION(aNamespace == kNameSpaceID_XHTML,
874 "Only HTML scripts may be async/defer.");
875 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
876 NS_ASSERTION(treeOp, "Tree op allocation failed.");
877 treeOp->Init(eTreeOpRunScriptAsyncDefer, aElement);
878 mCurrentHtmlScriptIsAsyncOrDefer = false;
879 return;
881 requestSuspension();
882 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
883 NS_ASSERTION(treeOp, "Tree op allocation failed.");
884 treeOp->InitScript(aElement);
885 return;
887 if (aName == nsHtml5Atoms::title) {
888 if (mBuilder) {
889 nsHtml5TreeOperation::DoneAddingChildren(static_cast<nsIContent*>(aElement));
890 return;
892 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
893 NS_ASSERTION(treeOp, "Tree op allocation failed.");
894 treeOp->Init(eTreeOpDoneAddingChildren, aElement);
895 return;
897 if (aName == nsHtml5Atoms::style || (aNamespace == kNameSpaceID_XHTML && aName == nsHtml5Atoms::link)) {
898 if (mBuilder) {
899 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
900 "Scripts must be blocked.");
901 mBuilder->UpdateStyleSheet(static_cast<nsIContent*>(aElement));
902 return;
904 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
905 NS_ASSERTION(treeOp, "Tree op allocation failed.");
906 treeOp->Init(eTreeOpUpdateStyleSheet, aElement);
907 return;
909 if (aNamespace == kNameSpaceID_SVG) {
910 if (aName == nsHtml5Atoms::svg) {
911 if (mBuilder) {
912 nsHtml5TreeOperation::SvgLoad(static_cast<nsIContent*>(aElement));
913 return;
915 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
916 NS_ASSERTION(treeOp, "Tree op allocation failed.");
917 treeOp->Init(eTreeOpSvgLoad, aElement);
919 return;
921 // we now have only HTML
922 // Some HTML nodes need DoneAddingChildren() called to initialize
923 // properly (e.g. form state restoration).
924 // XXX expose ElementName group here and do switch
925 if (aName == nsHtml5Atoms::object ||
926 aName == nsHtml5Atoms::applet ||
927 aName == nsHtml5Atoms::select ||
928 aName == nsHtml5Atoms::textarea ||
929 aName == nsHtml5Atoms::output) {
930 if (mBuilder) {
931 nsHtml5TreeOperation::DoneAddingChildren(static_cast<nsIContent*>(aElement));
932 return;
934 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
935 NS_ASSERTION(treeOp, "Tree op allocation failed.");
936 treeOp->Init(eTreeOpDoneAddingChildren, aElement);
937 return;
939 if (aName == nsHtml5Atoms::meta && !fragment && !mBuilder) {
940 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
941 NS_ASSERTION(treeOp, "Tree op allocation failed.");
942 treeOp->Init(eTreeOpProcessMeta, aElement);
943 return;
945 if (mSpeculativeLoadStage && aName == nsHtml5Atoms::picture) {
946 // mSpeculativeLoadStage is non-null only in the off-the-main-thread
947 // tree builder, which handles the network stream
949 // See comments in nsHtml5SpeculativeLoad.h about <picture> preloading
950 mSpeculativeLoadQueue.AppendElement()->InitEndPicture();
952 return;
955 void
956 nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf, int32_t aStart, int32_t aLength)
958 MOZ_ASSERT(charBufferLen + aLength <= charBuffer.length,
959 "About to memcpy past the end of the buffer!");
960 memcpy(charBuffer + charBufferLen, aBuf + aStart, sizeof(char16_t) * aLength);
961 charBufferLen += aLength;
964 bool
965 nsHtml5TreeBuilder::EnsureBufferSpace(size_t aLength)
967 // TODO: Unify nsHtml5Tokenizer::strBuf and nsHtml5TreeBuilder::charBuffer
968 // so that this method becomes unnecessary.
969 size_t worstCase = size_t(charBufferLen) + aLength;
970 if (worstCase > INT32_MAX) {
971 // Since we index into the buffer using int32_t due to the Java heritage
972 // of the code, let's treat this as OOM.
973 return false;
975 if (!charBuffer) {
976 // Add one to round to the next power of two to avoid immediate
977 // reallocation once there are a few characters in the buffer.
978 charBuffer = jArray<char16_t,int32_t>::newFallibleJArray(mozilla::RoundUpPow2(worstCase + 1));
979 if (!charBuffer) {
980 return false;
982 } else if (worstCase > size_t(charBuffer.length)) {
983 jArray<char16_t,int32_t> newBuf = jArray<char16_t,int32_t>::newFallibleJArray(mozilla::RoundUpPow2(worstCase));
984 if (!newBuf) {
985 return false;
987 memcpy(newBuf, charBuffer, sizeof(char16_t) * charBufferLen);
988 charBuffer = newBuf;
990 return true;
993 nsIContentHandle*
994 nsHtml5TreeBuilder::AllocateContentHandle()
996 if (MOZ_UNLIKELY(mBuilder)) {
997 MOZ_ASSERT_UNREACHABLE("Must never allocate a handle with builder.");
998 return nullptr;
1000 if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) {
1001 mOldHandles.AppendElement(Move(mHandles));
1002 mHandles = MakeUnique<nsIContent*[]>(NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH);
1003 mHandlesUsed = 0;
1005 #ifdef DEBUG
1006 mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD;
1007 #endif
1008 return &mHandles[mHandlesUsed++];
1011 bool
1012 nsHtml5TreeBuilder::HasScript()
1014 uint32_t len = mOpQueue.Length();
1015 if (!len) {
1016 return false;
1018 return mOpQueue.ElementAt(len - 1).IsRunScript();
1021 bool
1022 nsHtml5TreeBuilder::Flush(bool aDiscretionary)
1024 if (MOZ_UNLIKELY(mBuilder)) {
1025 MOZ_ASSERT_UNREACHABLE("Must never flush with builder.");
1026 return false;
1028 if (NS_SUCCEEDED(mBroken)) {
1029 if (!aDiscretionary ||
1030 !(charBufferLen &&
1031 currentPtr >= 0 &&
1032 stack[currentPtr]->isFosterParenting())) {
1033 // Don't flush text on discretionary flushes if the current element on
1034 // the stack is a foster-parenting element and there's pending text,
1035 // because flushing in that case would make the tree shape dependent on
1036 // where the flush points fall.
1037 flushCharacters();
1039 FlushLoads();
1041 if (mOpSink) {
1042 bool hasOps = !mOpQueue.IsEmpty();
1043 if (hasOps) {
1044 // If the builder is broken and mOpQueue is not empty, there must be
1045 // one op and it must be eTreeOpMarkAsBroken.
1046 if (NS_FAILED(mBroken)) {
1047 MOZ_ASSERT(mOpQueue.Length() == 1,
1048 "Tree builder is broken with a non-empty op queue whose length isn't 1.");
1049 MOZ_ASSERT(mOpQueue[0].IsMarkAsBroken(),
1050 "Tree builder is broken but the op in queue is not marked as broken.");
1052 mOpSink->MoveOpsFrom(mOpQueue);
1054 return hasOps;
1056 // no op sink: throw away ops
1057 mOpQueue.Clear();
1058 return false;
1061 void
1062 nsHtml5TreeBuilder::FlushLoads()
1064 if (MOZ_UNLIKELY(mBuilder)) {
1065 MOZ_ASSERT_UNREACHABLE("Must never flush loads with builder.");
1066 return;
1068 if (!mSpeculativeLoadQueue.IsEmpty()) {
1069 mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
1073 void
1074 nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset,
1075 int32_t aCharsetSource)
1077 if (mBuilder) {
1078 mBuilder->SetDocumentCharsetAndSource(aCharset, aCharsetSource);
1079 } else if (mSpeculativeLoadStage) {
1080 mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset(
1081 aCharset, aCharsetSource);
1082 } else {
1083 mOpQueue.AppendElement()->Init(
1084 eTreeOpSetDocumentCharset, aCharset, aCharsetSource);
1088 void
1089 nsHtml5TreeBuilder::StreamEnded()
1091 MOZ_ASSERT(!mBuilder, "Must not call StreamEnded with builder.");
1092 MOZ_ASSERT(!fragment, "Must not parse fragments off the main thread.");
1093 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1094 NS_ASSERTION(treeOp, "Tree op allocation failed.");
1095 treeOp->Init(eTreeOpStreamEnded);
1098 void
1099 nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset,
1100 int32_t aCharsetSource,
1101 int32_t aLineNumber)
1103 if (MOZ_UNLIKELY(mBuilder)) {
1104 MOZ_ASSERT_UNREACHABLE("Must never switch charset with builder.");
1105 return;
1107 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1108 NS_ASSERTION(treeOp, "Tree op allocation failed.");
1109 treeOp->Init(eTreeOpNeedsCharsetSwitchTo,
1110 aCharset,
1111 aCharsetSource,
1112 aLineNumber);
1115 void
1116 nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId,
1117 bool aError,
1118 int32_t aLineNumber)
1120 if (MOZ_UNLIKELY(mBuilder)) {
1121 MOZ_ASSERT_UNREACHABLE("Must never complain about charset with builder.");
1122 return;
1124 mOpQueue.AppendElement()->Init(aMsgId, aError, aLineNumber);
1127 void
1128 nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine)
1130 if (MOZ_UNLIKELY(mBuilder)) {
1131 MOZ_ASSERT_UNREACHABLE("Must never use snapshots with builder.");
1132 return;
1134 NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
1135 NS_PRECONDITION(aSnapshot, "Got null snapshot.");
1136 mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
1139 void
1140 nsHtml5TreeBuilder::DropHandles()
1142 MOZ_ASSERT(!mBuilder, "Must not drop handles with builder.");
1143 mOldHandles.Clear();
1144 mHandlesUsed = 0;
1147 void
1148 nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv)
1150 if (MOZ_UNLIKELY(mBuilder)) {
1151 MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
1152 return;
1154 mBroken = aRv;
1155 mOpQueue.Clear(); // Previous ops don't matter anymore
1156 mOpQueue.AppendElement()->Init(aRv);
1159 void
1160 nsHtml5TreeBuilder::MarkAsBrokenFromPortability(nsresult aRv)
1162 if (mBuilder) {
1163 MarkAsBrokenAndRequestSuspension(aRv);
1164 return;
1166 mBroken = aRv;
1167 requestSuspension();
1170 void
1171 nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle)
1173 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1174 startTag(nsHtml5ElementName::ELT_TITLE,
1175 nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
1176 false);
1178 // XUL will add the "Source of: " prefix.
1179 uint32_t length = aTitle.Length();
1180 if (length > INT32_MAX) {
1181 length = INT32_MAX;
1183 characters(aTitle.get(), 0, (int32_t)length);
1184 endTag(nsHtml5ElementName::ELT_TITLE);
1186 startTag(nsHtml5ElementName::ELT_LINK,
1187 nsHtml5ViewSourceUtils::NewLinkAttributes(),
1188 false);
1190 startTag(nsHtml5ElementName::ELT_BODY,
1191 nsHtml5ViewSourceUtils::NewBodyAttributes(),
1192 false);
1194 StartPlainTextBody();
1197 void
1198 nsHtml5TreeBuilder::StartPlainText()
1200 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1201 startTag(nsHtml5ElementName::ELT_LINK,
1202 nsHtml5PlainTextUtils::NewLinkAttributes(),
1203 false);
1205 StartPlainTextBody();
1208 void
1209 nsHtml5TreeBuilder::StartPlainTextBody()
1211 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1212 startTag(nsHtml5ElementName::ELT_PRE,
1213 nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
1214 false);
1215 needToDropLF = false;
1218 // DocumentModeHandler
1219 void
1220 nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
1222 if (mBuilder) {
1223 mBuilder->SetDocumentMode(m);
1224 return;
1226 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1227 NS_ASSERTION(treeOp, "Tree op allocation failed.");
1228 treeOp->Init(m);
1231 nsIContentHandle*
1232 nsHtml5TreeBuilder::getDocumentFragmentForTemplate(nsIContentHandle* aTemplate)
1234 if (mBuilder) {
1235 return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(static_cast<nsIContent*>(aTemplate));
1237 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
1238 NS_ASSERTION(treeOp, "Tree op allocation failed.");
1239 nsIContentHandle* fragHandle = AllocateContentHandle();
1240 treeOp->Init(eTreeOpGetDocumentFragmentForTemplate, aTemplate, fragHandle);
1241 return fragHandle;
1244 nsIContentHandle*
1245 nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext)
1247 MOZ_ASSERT(mBuilder, "Must have builder.");
1248 if (!aContext) {
1249 return nullptr;
1252 MOZ_ASSERT(NS_IsMainThread());
1254 // aContext must always be an element that already exists
1255 // in the document.
1256 nsIContent* contextNode = static_cast<nsIContent*>(aContext);
1257 nsIContent* currentAncestor = contextNode;
1259 // We traverse the ancestors of the context node to find the nearest
1260 // form pointer. This traversal is why aContext must not be an emtpy handle.
1261 nsIContent* nearestForm = nullptr;
1262 while (currentAncestor) {
1263 if (currentAncestor->IsHTMLElement(nsGkAtoms::form)) {
1264 nearestForm = currentAncestor;
1265 break;
1267 currentAncestor = currentAncestor->GetParent();
1270 if (!nearestForm) {
1271 return nullptr;
1274 return nearestForm;
1277 // Error reporting
1279 void
1280 nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter* aHighlighter)
1282 MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
1283 mViewSource = aHighlighter;
1286 void
1287 nsHtml5TreeBuilder::errStrayStartTag(nsIAtom* aName)
1289 if (MOZ_UNLIKELY(mViewSource)) {
1290 mViewSource->AddErrorToCurrentRun("errStrayStartTag2", aName);
1294 void
1295 nsHtml5TreeBuilder::errStrayEndTag(nsIAtom* aName)
1297 if (MOZ_UNLIKELY(mViewSource)) {
1298 mViewSource->AddErrorToCurrentRun("errStrayEndTag", aName);
1302 void
1303 nsHtml5TreeBuilder::errUnclosedElements(int32_t aIndex, nsIAtom* aName)
1305 if (MOZ_UNLIKELY(mViewSource)) {
1306 mViewSource->AddErrorToCurrentRun("errUnclosedElements", aName);
1310 void
1311 nsHtml5TreeBuilder::errUnclosedElementsImplied(int32_t aIndex, nsIAtom* aName)
1313 if (MOZ_UNLIKELY(mViewSource)) {
1314 mViewSource->AddErrorToCurrentRun("errUnclosedElementsImplied",
1315 aName);
1319 void
1320 nsHtml5TreeBuilder::errUnclosedElementsCell(int32_t aIndex)
1322 if (MOZ_UNLIKELY(mViewSource)) {
1323 mViewSource->AddErrorToCurrentRun("errUnclosedElementsCell");
1327 void
1328 nsHtml5TreeBuilder::errStrayDoctype()
1330 if (MOZ_UNLIKELY(mViewSource)) {
1331 mViewSource->AddErrorToCurrentRun("errStrayDoctype");
1335 void
1336 nsHtml5TreeBuilder::errAlmostStandardsDoctype()
1338 if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) {
1339 mViewSource->AddErrorToCurrentRun("errAlmostStandardsDoctype");
1343 void
1344 nsHtml5TreeBuilder::errQuirkyDoctype()
1346 if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) {
1347 mViewSource->AddErrorToCurrentRun("errQuirkyDoctype");
1351 void
1352 nsHtml5TreeBuilder::errNonSpaceInTrailer()
1354 if (MOZ_UNLIKELY(mViewSource)) {
1355 mViewSource->AddErrorToCurrentRun("errNonSpaceInTrailer");
1359 void
1360 nsHtml5TreeBuilder::errNonSpaceAfterFrameset()
1362 if (MOZ_UNLIKELY(mViewSource)) {
1363 mViewSource->AddErrorToCurrentRun("errNonSpaceAfterFrameset");
1367 void
1368 nsHtml5TreeBuilder::errNonSpaceInFrameset()
1370 if (MOZ_UNLIKELY(mViewSource)) {
1371 mViewSource->AddErrorToCurrentRun("errNonSpaceInFrameset");
1375 void
1376 nsHtml5TreeBuilder::errNonSpaceAfterBody()
1378 if (MOZ_UNLIKELY(mViewSource)) {
1379 mViewSource->AddErrorToCurrentRun("errNonSpaceAfterBody");
1383 void
1384 nsHtml5TreeBuilder::errNonSpaceInColgroupInFragment()
1386 if (MOZ_UNLIKELY(mViewSource)) {
1387 mViewSource->AddErrorToCurrentRun("errNonSpaceInColgroupInFragment");
1391 void
1392 nsHtml5TreeBuilder::errNonSpaceInNoscriptInHead()
1394 if (MOZ_UNLIKELY(mViewSource)) {
1395 mViewSource->AddErrorToCurrentRun("errNonSpaceInNoscriptInHead");
1399 void
1400 nsHtml5TreeBuilder::errFooBetweenHeadAndBody(nsIAtom* aName)
1402 if (MOZ_UNLIKELY(mViewSource)) {
1403 mViewSource->AddErrorToCurrentRun("errFooBetweenHeadAndBody", aName);
1407 void
1408 nsHtml5TreeBuilder::errStartTagWithoutDoctype()
1410 if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) {
1411 mViewSource->AddErrorToCurrentRun("errStartTagWithoutDoctype");
1415 void
1416 nsHtml5TreeBuilder::errNoSelectInTableScope()
1418 if (MOZ_UNLIKELY(mViewSource)) {
1419 mViewSource->AddErrorToCurrentRun("errNoSelectInTableScope");
1423 void
1424 nsHtml5TreeBuilder::errStartSelectWhereEndSelectExpected()
1426 if (MOZ_UNLIKELY(mViewSource)) {
1427 mViewSource->AddErrorToCurrentRun(
1428 "errStartSelectWhereEndSelectExpected");
1432 void
1433 nsHtml5TreeBuilder::errStartTagWithSelectOpen(nsIAtom* aName)
1435 if (MOZ_UNLIKELY(mViewSource)) {
1436 mViewSource->AddErrorToCurrentRun("errStartTagWithSelectOpen", aName);
1440 void
1441 nsHtml5TreeBuilder::errBadStartTagInHead(nsIAtom* aName)
1443 if (MOZ_UNLIKELY(mViewSource)) {
1444 mViewSource->AddErrorToCurrentRun("errBadStartTagInHead2", aName);
1448 void
1449 nsHtml5TreeBuilder::errImage()
1451 if (MOZ_UNLIKELY(mViewSource)) {
1452 mViewSource->AddErrorToCurrentRun("errImage");
1456 void
1457 nsHtml5TreeBuilder::errIsindex()
1459 if (MOZ_UNLIKELY(mViewSource)) {
1460 mViewSource->AddErrorToCurrentRun("errIsindex");
1464 void
1465 nsHtml5TreeBuilder::errFooSeenWhenFooOpen(nsIAtom* aName)
1467 if (MOZ_UNLIKELY(mViewSource)) {
1468 mViewSource->AddErrorToCurrentRun("errFooSeenWhenFooOpen", aName);
1472 void
1473 nsHtml5TreeBuilder::errHeadingWhenHeadingOpen()
1475 if (MOZ_UNLIKELY(mViewSource)) {
1476 mViewSource->AddErrorToCurrentRun("errHeadingWhenHeadingOpen");
1480 void
1481 nsHtml5TreeBuilder::errFramesetStart()
1483 if (MOZ_UNLIKELY(mViewSource)) {
1484 mViewSource->AddErrorToCurrentRun("errFramesetStart");
1488 void
1489 nsHtml5TreeBuilder::errNoCellToClose()
1491 if (MOZ_UNLIKELY(mViewSource)) {
1492 mViewSource->AddErrorToCurrentRun("errNoCellToClose");
1496 void
1497 nsHtml5TreeBuilder::errStartTagInTable(nsIAtom* aName)
1499 if (MOZ_UNLIKELY(mViewSource)) {
1500 mViewSource->AddErrorToCurrentRun("errStartTagInTable", aName);
1504 void
1505 nsHtml5TreeBuilder::errFormWhenFormOpen()
1507 if (MOZ_UNLIKELY(mViewSource)) {
1508 mViewSource->AddErrorToCurrentRun("errFormWhenFormOpen");
1512 void
1513 nsHtml5TreeBuilder::errTableSeenWhileTableOpen()
1515 if (MOZ_UNLIKELY(mViewSource)) {
1516 mViewSource->AddErrorToCurrentRun("errTableSeenWhileTableOpen");
1520 void
1521 nsHtml5TreeBuilder::errStartTagInTableBody(nsIAtom* aName)
1523 if (MOZ_UNLIKELY(mViewSource)) {
1524 mViewSource->AddErrorToCurrentRun("errStartTagInTableBody", aName);
1528 void
1529 nsHtml5TreeBuilder::errEndTagSeenWithoutDoctype()
1531 if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) {
1532 mViewSource->AddErrorToCurrentRun("errEndTagSeenWithoutDoctype");
1536 void
1537 nsHtml5TreeBuilder::errEndTagAfterBody()
1539 if (MOZ_UNLIKELY(mViewSource)) {
1540 mViewSource->AddErrorToCurrentRun("errEndTagAfterBody");
1544 void
1545 nsHtml5TreeBuilder::errEndTagSeenWithSelectOpen(nsIAtom* aName)
1547 if (MOZ_UNLIKELY(mViewSource)) {
1548 mViewSource->AddErrorToCurrentRun("errEndTagSeenWithSelectOpen",
1549 aName);
1553 void
1554 nsHtml5TreeBuilder::errGarbageInColgroup()
1556 if (MOZ_UNLIKELY(mViewSource)) {
1557 mViewSource->AddErrorToCurrentRun("errGarbageInColgroup");
1561 void
1562 nsHtml5TreeBuilder::errEndTagBr()
1564 if (MOZ_UNLIKELY(mViewSource)) {
1565 mViewSource->AddErrorToCurrentRun("errEndTagBr");
1569 void
1570 nsHtml5TreeBuilder::errNoElementToCloseButEndTagSeen(nsIAtom* aName)
1572 if (MOZ_UNLIKELY(mViewSource)) {
1573 mViewSource->AddErrorToCurrentRun(
1574 "errNoElementToCloseButEndTagSeen", aName);
1578 void
1579 nsHtml5TreeBuilder::errHtmlStartTagInForeignContext(nsIAtom* aName)
1581 if (MOZ_UNLIKELY(mViewSource)) {
1582 mViewSource->AddErrorToCurrentRun("errHtmlStartTagInForeignContext",
1583 aName);
1587 void
1588 nsHtml5TreeBuilder::errTableClosedWhileCaptionOpen()
1590 if (MOZ_UNLIKELY(mViewSource)) {
1591 mViewSource->AddErrorToCurrentRun("errTableClosedWhileCaptionOpen");
1595 void
1596 nsHtml5TreeBuilder::errNoTableRowToClose()
1598 if (MOZ_UNLIKELY(mViewSource)) {
1599 mViewSource->AddErrorToCurrentRun("errNoTableRowToClose");
1603 void
1604 nsHtml5TreeBuilder::errNonSpaceInTable()
1606 if (MOZ_UNLIKELY(mViewSource)) {
1607 mViewSource->AddErrorToCurrentRun("errNonSpaceInTable");
1611 void
1612 nsHtml5TreeBuilder::errUnclosedChildrenInRuby()
1614 if (MOZ_UNLIKELY(mViewSource)) {
1615 mViewSource->AddErrorToCurrentRun("errUnclosedChildrenInRuby");
1619 void
1620 nsHtml5TreeBuilder::errStartTagSeenWithoutRuby(nsIAtom* aName)
1622 if (MOZ_UNLIKELY(mViewSource)) {
1623 mViewSource->AddErrorToCurrentRun("errStartTagSeenWithoutRuby",
1624 aName);
1628 void
1629 nsHtml5TreeBuilder::errSelfClosing()
1631 if (MOZ_UNLIKELY(mViewSource)) {
1632 mViewSource->AddErrorToCurrentSlash("errSelfClosing");
1636 void
1637 nsHtml5TreeBuilder::errNoCheckUnclosedElementsOnStack()
1639 if (MOZ_UNLIKELY(mViewSource)) {
1640 mViewSource->AddErrorToCurrentRun(
1641 "errNoCheckUnclosedElementsOnStack");
1645 void
1646 nsHtml5TreeBuilder::errEndTagDidNotMatchCurrentOpenElement(nsIAtom* aName,
1647 nsIAtom* aOther)
1649 if (MOZ_UNLIKELY(mViewSource)) {
1650 mViewSource->AddErrorToCurrentRun(
1651 "errEndTagDidNotMatchCurrentOpenElement", aName, aOther);
1655 void
1656 nsHtml5TreeBuilder::errEndTagViolatesNestingRules(nsIAtom* aName)
1658 if (MOZ_UNLIKELY(mViewSource)) {
1659 mViewSource->AddErrorToCurrentRun("errEndTagViolatesNestingRules", aName);
1663 void
1664 nsHtml5TreeBuilder::errEndWithUnclosedElements(nsIAtom* aName)
1666 if (MOZ_UNLIKELY(mViewSource)) {
1667 mViewSource->AddErrorToCurrentRun("errEndWithUnclosedElements", aName);