Backed out 2 changesets (bug 1886730) for causing detekt & klint failures CLOSED...
[gecko.git] / parser / html / nsHtml5Highlighter.cpp
blob45c84b743a319e2a08441dbd1816d9fb19350ff2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsHtml5Highlighter.h"
6 #include "ErrorList.h"
7 #include "nsDebug.h"
8 #include "nsHtml5AttributeName.h"
9 #include "nsHtml5Tokenizer.h"
10 #include "nsHtml5ViewSourceUtils.h"
11 #include "nsString.h"
12 #include "nsThreadUtils.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/Preferences.h"
17 using namespace mozilla;
19 // The old code had a limit of 16 tokens. 1300 is a number picked my measuring
20 // the size of 16 tokens on cnn.com.
21 #define NS_HTML5_HIGHLIGHTER_PRE_BREAK_THRESHOLD 1300
23 char16_t nsHtml5Highlighter::sComment[] = {'c', 'o', 'm', 'm',
24 'e', 'n', 't', 0};
26 char16_t nsHtml5Highlighter::sCdata[] = {'c', 'd', 'a', 't', 'a', 0};
28 char16_t nsHtml5Highlighter::sEntity[] = {'e', 'n', 't', 'i', 't', 'y', 0};
30 char16_t nsHtml5Highlighter::sEndTag[] = {'e', 'n', 'd', '-', 't', 'a', 'g', 0};
32 char16_t nsHtml5Highlighter::sStartTag[] = {'s', 't', 'a', 'r', 't',
33 '-', 't', 'a', 'g', 0};
35 char16_t nsHtml5Highlighter::sAttributeName[] = {
36 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', 'n', 'a', 'm', 'e', 0};
38 char16_t nsHtml5Highlighter::sAttributeValue[] = {'a', 't', 't', 'r', 'i', 'b',
39 'u', 't', 'e', '-', 'v', 'a',
40 'l', 'u', 'e', 0};
42 char16_t nsHtml5Highlighter::sDoctype[] = {'d', 'o', 'c', 't',
43 'y', 'p', 'e', 0};
45 char16_t nsHtml5Highlighter::sPi[] = {'p', 'i', 0};
47 nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink)
48 : mState(nsHtml5Tokenizer::DATA),
49 mCStart(INT32_MAX),
50 mPos(0),
51 mLineNumber(1),
52 mInlinesOpen(0),
53 mInCharacters(false),
54 mBuffer(nullptr),
55 mOpSink(aOpSink),
56 mCurrentRun(nullptr),
57 mAmpersand(nullptr),
58 mSlash(nullptr),
59 mHandles(
60 MakeUnique<nsIContent*[]>(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH)),
61 mHandlesUsed(0),
62 mSeenBase(false) {
63 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
66 nsHtml5Highlighter::~nsHtml5Highlighter() {
67 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
70 void nsHtml5Highlighter::SetOpSink(nsAHtml5TreeOpSink* aOpSink) {
71 mOpSink = aOpSink;
74 void nsHtml5Highlighter::Rewind() {
75 mState = 0;
76 mCStart = INT32_MAX;
77 mPos = 0;
78 mLineNumber = 1;
79 mInlinesOpen = 0;
80 mInCharacters = false;
81 mBuffer = nullptr;
82 mOpQueue.Clear();
83 mCurrentRun = nullptr;
84 mAmpersand = nullptr;
85 mSlash = nullptr;
86 // Pop until we have three elements on the stack:
87 // html, body, and pre.
88 while (mStack.Length() > 3) {
89 Pop();
91 mSeenBase = false;
94 void nsHtml5Highlighter::Start(const nsAutoString& aTitle) {
95 // Doctype
96 opAppendDoctypeToDocument operation(nsGkAtoms::html, u""_ns, u""_ns);
97 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
99 mOpQueue.AppendElement()->Init(mozilla::AsVariant(STANDARDS_MODE));
101 // <html> uses NS_NewHTMLSharedElement creator
102 nsIContent** root =
103 CreateElement(nsGkAtoms::html, nullptr, nullptr, NS_NewHTMLSharedElement);
104 opAppendToDocument appendOp(root);
105 mOpQueue.AppendElement()->Init(mozilla::AsVariant(appendOp));
106 mStack.AppendElement(root);
108 // <head> uses NS_NewHTMLSharedElement creator
109 Push(nsGkAtoms::head, nullptr, NS_NewHTMLSharedElement);
111 Push(nsGkAtoms::meta, nsHtml5ViewSourceUtils::NewMetaViewportAttributes(),
112 NS_NewHTMLMetaElement);
113 Pop(); // meta
115 Push(nsGkAtoms::title, nullptr, NS_NewHTMLTitleElement);
116 // XUL will add the "Source of: " prefix.
117 uint32_t length = aTitle.Length();
118 if (length > INT32_MAX) {
119 length = INT32_MAX;
121 AppendCharacters(aTitle.BeginReading(), 0, (int32_t)length);
122 Pop(); // title
124 Push(nsGkAtoms::link, nsHtml5ViewSourceUtils::NewLinkAttributes(),
125 NS_NewHTMLLinkElement);
127 opUpdateStyleSheet updateOp(CurrentNode());
128 mOpQueue.AppendElement()->Init(mozilla::AsVariant(updateOp));
130 Pop(); // link
132 Pop(); // head
134 Push(nsGkAtoms::body, nsHtml5ViewSourceUtils::NewBodyAttributes(),
135 NS_NewHTMLBodyElement);
137 nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0);
138 nsHtml5String preId = nsHtml5Portability::newStringFromLiteral("line1");
139 preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId, -1);
140 Push(nsGkAtoms::pre, preAttrs, NS_NewHTMLPreElement);
142 // Don't call StartCharacters here in order to be able to put it in
143 // a speculation.
145 mOpQueue.AppendElement()->Init(mozilla::AsVariant(opStartLayout()));
148 void nsHtml5Highlighter::UpdateCharsetSource(nsCharsetSource aCharsetSource) {
149 opUpdateCharsetSource operation(aCharsetSource);
150 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
153 int32_t nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume,
154 int32_t aPos) {
155 mPos = aPos;
156 switch (mState) {
157 case nsHtml5Tokenizer::SCRIPT_DATA:
158 case nsHtml5Tokenizer::RAWTEXT:
159 case nsHtml5Tokenizer::RCDATA:
160 case nsHtml5Tokenizer::DATA:
161 // We can transition on < and on &. Either way, we don't yet know the
162 // role of the token, so open a span without class.
163 if (aState == nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE) {
164 StartSpan();
165 // Start another span for highlighting the ampersand
166 StartSpan();
167 mAmpersand = CurrentNode();
168 } else {
169 EndCharactersAndStartMarkupRun();
171 break;
172 case nsHtml5Tokenizer::TAG_OPEN:
173 switch (aState) {
174 case nsHtml5Tokenizer::TAG_NAME:
175 StartSpan(sStartTag);
176 break;
177 case nsHtml5Tokenizer::DATA:
178 FinishTag(); // DATA
179 break;
180 case nsHtml5Tokenizer::PROCESSING_INSTRUCTION:
181 AddClass(sPi);
182 break;
184 break;
185 case nsHtml5Tokenizer::TAG_NAME:
186 switch (aState) {
187 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
188 EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
189 break;
190 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
191 EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
192 StartSpan(); // for highlighting the slash
193 mSlash = CurrentNode();
194 break;
195 default:
196 FinishTag();
197 break;
199 break;
200 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
201 switch (aState) {
202 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
203 StartSpan(sAttributeName);
204 break;
205 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
206 StartSpan(); // for highlighting the slash
207 mSlash = CurrentNode();
208 break;
209 default:
210 FinishTag();
211 break;
213 break;
214 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
215 switch (aState) {
216 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
217 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
218 EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
219 break;
220 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
221 EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
222 StartSpan(); // for highlighting the slash
223 mSlash = CurrentNode();
224 break;
225 default:
226 FinishTag();
227 break;
229 break;
230 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
231 switch (aState) {
232 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
233 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
234 FlushCurrent();
235 StartA();
236 break;
237 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
238 StartA();
239 break;
240 default:
241 FinishTag();
242 break;
244 break;
245 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
246 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
247 switch (aState) {
248 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
249 EndSpanOrA();
250 break;
251 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
252 StartSpan();
253 StartSpan(); // for ampersand itself
254 mAmpersand = CurrentNode();
255 break;
256 default:
257 MOZ_ASSERT_UNREACHABLE("Impossible transition.");
258 break;
260 break;
261 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
262 switch (aState) {
263 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
264 break;
265 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
266 StartSpan(); // for highlighting the slash
267 mSlash = CurrentNode();
268 break;
269 default:
270 FinishTag();
271 break;
273 break;
274 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
275 EndSpanOrA(); // end the slash highlight
276 switch (aState) {
277 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
278 break;
279 default:
280 FinishTag();
281 break;
283 break;
284 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
285 switch (aState) {
286 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
287 EndSpanOrA();
288 break;
289 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
290 StartSpan();
291 StartSpan(); // for ampersand itself
292 mAmpersand = CurrentNode();
293 break;
294 default:
295 FinishTag();
296 break;
298 break;
299 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
300 switch (aState) {
301 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
302 StartSpan(); // for highlighting the slash
303 mSlash = CurrentNode();
304 break;
305 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
306 break;
307 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
308 StartSpan(sAttributeName);
309 break;
310 default:
311 FinishTag();
312 break;
314 break;
315 // most comment states are omitted, because they don't matter to
316 // highlighting
317 case nsHtml5Tokenizer::COMMENT_START:
318 case nsHtml5Tokenizer::COMMENT_END:
319 case nsHtml5Tokenizer::COMMENT_END_BANG:
320 case nsHtml5Tokenizer::COMMENT_START_DASH:
321 case nsHtml5Tokenizer::BOGUS_COMMENT:
322 case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
323 case nsHtml5Tokenizer::COMMENT_LESSTHAN_BANG_DASH_DASH:
324 if (aState == nsHtml5Tokenizer::DATA) {
325 AddClass(sComment);
326 FinishTag();
328 break;
329 // most cdata states are omitted, because they don't matter to
330 // highlighting
331 case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
332 if (aState == nsHtml5Tokenizer::DATA) {
333 AddClass(sCdata);
334 FinishTag();
336 break;
337 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
338 EndSpanOrA(); // the span for the ampersand
339 switch (aState) {
340 case nsHtml5Tokenizer::CONSUME_NCR:
341 case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
342 break;
343 default:
344 // not actually a character reference
345 EndSpanOrA();
346 break;
348 break;
349 case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
350 if (aState == nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL) {
351 break;
353 // not actually a character reference
354 EndSpanOrA();
355 break;
356 case nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL:
357 if (!aReconsume) {
358 FlushCurrent();
360 EndSpanOrA();
361 break;
362 case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
363 case nsHtml5Tokenizer::HEX_NCR_LOOP:
364 switch (aState) {
365 case nsHtml5Tokenizer::HANDLE_NCR_VALUE:
366 AddClass(sEntity);
367 FlushCurrent();
368 break;
369 case nsHtml5Tokenizer::HANDLE_NCR_VALUE_RECONSUME:
370 AddClass(sEntity);
371 break;
373 EndSpanOrA();
374 break;
375 case nsHtml5Tokenizer::CLOSE_TAG_OPEN:
376 switch (aState) {
377 case nsHtml5Tokenizer::DATA:
378 FinishTag();
379 break;
380 case nsHtml5Tokenizer::TAG_NAME:
381 StartSpan(sEndTag);
382 break;
384 break;
385 case nsHtml5Tokenizer::RAWTEXT_RCDATA_LESS_THAN_SIGN:
386 if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
387 FlushCurrent();
388 StartSpan(); // don't know if it is "end-tag" yet :-(
389 break;
391 EndSpanOrA();
392 StartCharacters();
393 break;
394 case nsHtml5Tokenizer::NON_DATA_END_TAG_NAME:
395 switch (aState) {
396 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
397 AddClass(sEndTag);
398 EndSpanOrA();
399 break;
400 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
401 AddClass(sEndTag);
402 EndSpanOrA();
403 StartSpan(); // for highlighting the slash
404 mSlash = CurrentNode();
405 break;
406 case nsHtml5Tokenizer::DATA: // yes, as a result of emitting the token
407 AddClass(sEndTag);
408 FinishTag();
409 break;
410 default:
411 FinishTag();
412 break;
414 break;
415 case nsHtml5Tokenizer::SCRIPT_DATA_LESS_THAN_SIGN:
416 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
417 if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
418 FlushCurrent();
419 StartSpan(); // don't know if it is "end-tag" yet :-(
420 break;
422 FinishTag();
423 break;
424 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH_DASH:
425 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED:
426 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH:
427 if (aState == nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) {
428 EndCharactersAndStartMarkupRun();
430 break;
431 // Lots of double escape states omitted, because they don't highlight.
432 // Likewise, only doctype states that can emit the doctype are of
433 // interest. Otherwise, the transition out of bogus comment deals.
434 case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
435 case nsHtml5Tokenizer::DOCTYPE_NAME:
436 case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
437 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
438 case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
439 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
440 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
441 case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
442 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
443 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
444 case nsHtml5Tokenizer::BOGUS_DOCTYPE:
445 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
446 case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
447 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
448 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
449 if (aState == nsHtml5Tokenizer::DATA) {
450 AddClass(sDoctype);
451 FinishTag();
453 break;
454 case nsHtml5Tokenizer::PROCESSING_INSTRUCTION_QUESTION_MARK:
455 if (aState == nsHtml5Tokenizer::DATA) {
456 FinishTag();
458 break;
459 default:
460 break;
462 mState = aState;
463 return aState;
466 [[nodiscard]] bool nsHtml5Highlighter::End() {
467 switch (mState) {
468 case nsHtml5Tokenizer::COMMENT_END:
469 case nsHtml5Tokenizer::COMMENT_END_BANG:
470 case nsHtml5Tokenizer::COMMENT_START_DASH:
471 case nsHtml5Tokenizer::BOGUS_COMMENT:
472 case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
473 AddClass(sComment);
474 break;
475 case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
476 AddClass(sCdata);
477 break;
478 case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
479 case nsHtml5Tokenizer::HEX_NCR_LOOP:
480 // XXX need tokenizer help here
481 break;
482 case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
483 case nsHtml5Tokenizer::DOCTYPE_NAME:
484 case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
485 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
486 case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
487 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
488 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
489 case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
490 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
491 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
492 case nsHtml5Tokenizer::BOGUS_DOCTYPE:
493 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
494 case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
495 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
496 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
497 AddClass(sDoctype);
498 break;
499 default:
500 break;
502 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
503 NS_ASSERTION(treeOp, "Tree op allocation failed.");
504 treeOp->Init(mozilla::AsVariant(opStreamEnded()));
505 return FlushOps().isOk();
508 void nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer) {
509 MOZ_ASSERT(!mBuffer, "Old buffer still here!");
510 mBuffer = aBuffer;
511 mCStart = aBuffer->getStart();
514 void nsHtml5Highlighter::DropBuffer(int32_t aPos) {
515 MOZ_ASSERT(mBuffer, "No buffer to drop!");
516 mPos = aPos;
517 FlushChars();
518 mBuffer = nullptr;
521 void nsHtml5Highlighter::StartSpan() {
522 FlushChars();
523 Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
524 ++mInlinesOpen;
527 void nsHtml5Highlighter::StartSpan(const char16_t* aClass) {
528 StartSpan();
529 AddClass(aClass);
532 void nsHtml5Highlighter::EndSpanOrA() {
533 FlushChars();
534 Pop();
535 --mInlinesOpen;
538 void nsHtml5Highlighter::StartCharacters() {
539 MOZ_ASSERT(!mInCharacters, "Already in characters!");
540 FlushChars();
541 Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
542 mCurrentRun = CurrentNode();
543 mInCharacters = true;
546 void nsHtml5Highlighter::EndCharactersAndStartMarkupRun() {
547 MOZ_ASSERT(mInCharacters, "Not in characters!");
548 FlushChars();
549 Pop();
550 mInCharacters = false;
551 // Now start markup run
552 StartSpan();
553 mCurrentRun = CurrentNode();
556 void nsHtml5Highlighter::StartA() {
557 FlushChars();
558 Push(nsGkAtoms::a, nullptr, NS_NewHTMLAnchorElement);
559 AddClass(sAttributeValue);
560 ++mInlinesOpen;
563 void nsHtml5Highlighter::FinishTag() {
564 while (mInlinesOpen > 1) {
565 EndSpanOrA();
567 FlushCurrent(); // >
568 EndSpanOrA(); // DATA
569 NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!");
570 StartCharacters();
573 void nsHtml5Highlighter::FlushChars() {
574 if (mCStart < mPos) {
575 char16_t* buf = mBuffer->getBuffer();
576 int32_t i = mCStart;
577 while (i < mPos) {
578 char16_t c = buf[i];
579 switch (c) {
580 case '\r':
581 // The input this code sees has been normalized so that there are
582 // CR breaks and LF breaks but no CRLF breaks. Overwrite CR with LF
583 // to show consistent LF line breaks to layout. It is OK to mutate
584 // the input data, because there are no reparses in the View Source
585 // case, so we won't need the original data in the buffer anymore.
586 buf[i] = '\n';
587 [[fallthrough]];
588 case '\n': {
589 ++i;
590 if (mCStart < i) {
591 int32_t len = i - mCStart;
592 AppendCharacters(buf, mCStart, len);
593 mCStart = i;
595 ++mLineNumber;
596 Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
597 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
598 NS_ASSERTION(treeOp, "Tree op allocation failed.");
599 opAddLineNumberId operation(CurrentNode(), mLineNumber);
600 treeOp->Init(mozilla::AsVariant(operation));
601 Pop();
602 break;
604 default:
605 ++i;
606 break;
609 if (mCStart < mPos) {
610 int32_t len = mPos - mCStart;
611 AppendCharacters(buf, mCStart, len);
612 mCStart = mPos;
617 void nsHtml5Highlighter::FlushCurrent() {
618 mPos++;
619 FlushChars();
622 bool nsHtml5Highlighter::ShouldFlushOps() {
623 // Arbitrary threshold that doesn't have an exact justification.
624 // The general idea is to flush much, much sooner than reaching
625 // the maximum size of `nsTArray`.
626 return mOpQueue.Length() > 100000;
629 mozilla::Result<bool, nsresult> nsHtml5Highlighter::FlushOps() {
630 bool hasOps = !mOpQueue.IsEmpty();
631 if (hasOps) {
632 if (!mOpSink->MoveOpsFrom(mOpQueue)) {
633 return Err(NS_ERROR_OUT_OF_MEMORY);
636 return hasOps;
639 void nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName,
640 nsHtml5String aValue) {
641 if (!(nsHtml5AttributeName::ATTR_HREF == aName ||
642 nsHtml5AttributeName::ATTR_SRC == aName ||
643 nsHtml5AttributeName::ATTR_ACTION == aName ||
644 nsHtml5AttributeName::ATTR_CITE == aName ||
645 nsHtml5AttributeName::ATTR_BACKGROUND == aName ||
646 nsHtml5AttributeName::ATTR_LONGDESC == aName ||
647 nsHtml5AttributeName::ATTR_XLINK_HREF == aName ||
648 nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) {
649 return;
651 AddViewSourceHref(aValue);
654 void nsHtml5Highlighter::CompletedNamedCharacterReference() {
655 AddClass(sEntity);
658 nsIContent** nsHtml5Highlighter::AllocateContentHandle() {
659 if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) {
660 mOldHandles.AppendElement(std::move(mHandles));
661 mHandles =
662 MakeUnique<nsIContent*[]>(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH);
663 mHandlesUsed = 0;
665 #ifdef DEBUG
666 mHandles[mHandlesUsed] = reinterpret_cast<nsIContent*>(uintptr_t(0xC0DEDBAD));
667 #endif
668 return &mHandles[mHandlesUsed++];
671 nsIContent** nsHtml5Highlighter::CreateElement(
672 nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
673 nsIContent** aIntendedParent,
674 mozilla::dom::HTMLContentCreatorFunction aCreator) {
675 MOZ_ASSERT(aName, "Got null name.");
676 nsIContent** content = AllocateContentHandle();
677 opCreateHTMLElement opeation(content, aName, aAttributes, aCreator,
678 aIntendedParent,
679 mozilla::dom::FROM_PARSER_NETWORK);
680 mOpQueue.AppendElement()->Init(mozilla::AsVariant(opeation));
681 return content;
684 nsIContent** nsHtml5Highlighter::CurrentNode() {
685 MOZ_ASSERT(mStack.Length() >= 1, "Must have something on stack.");
686 return mStack[mStack.Length() - 1];
689 void nsHtml5Highlighter::Push(
690 nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
691 mozilla::dom::HTMLContentCreatorFunction aCreator) {
692 MOZ_ASSERT(mStack.Length() >= 1, "Pushing without root.");
693 nsIContent** elt = CreateElement(aName, aAttributes, CurrentNode(),
694 aCreator); // Don't inline below!
695 opAppend operation(elt, CurrentNode(), mozilla::dom::FROM_PARSER_NETWORK);
696 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
697 mStack.AppendElement(elt);
700 void nsHtml5Highlighter::Pop() {
701 MOZ_ASSERT(mStack.Length() >= 2, "Popping when stack too short.");
702 mStack.RemoveLastElement();
705 void nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
706 int32_t aStart, int32_t aLength) {
707 MOZ_ASSERT(aBuffer, "Null buffer");
709 char16_t* bufferCopy = new char16_t[aLength];
710 memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t));
712 opAppendText operation(CurrentNode(), bufferCopy, aLength);
713 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
716 void nsHtml5Highlighter::AddClass(const char16_t* aClass) {
717 opAddClass operation(CurrentNode(), (char16_t*)aClass);
718 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
721 void nsHtml5Highlighter::AddViewSourceHref(nsHtml5String aValue) {
722 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
723 aValue.CopyToBuffer(bufferCopy);
724 bufferCopy[aValue.Length()] = 0;
726 opAddViewSourceHref operation(CurrentNode(), bufferCopy, aValue.Length());
727 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
730 void nsHtml5Highlighter::AddBase(nsHtml5String aValue) {
731 if (mSeenBase) {
732 return;
734 mSeenBase = true;
735 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
736 aValue.CopyToBuffer(bufferCopy);
737 bufferCopy[aValue.Length()] = 0;
739 opAddViewSourceBase operation(bufferCopy, aValue.Length());
740 mOpQueue.AppendElement()->Init(mozilla::AsVariant(operation));
743 void nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId) {
744 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
745 NS_ASSERTION(treeOp, "Tree op allocation failed.");
746 opAddErrorType operation(CurrentNode(), (char*)aMsgId);
747 treeOp->Init(mozilla::AsVariant(operation));
750 void nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId) {
751 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
752 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
753 NS_ASSERTION(treeOp, "Tree op allocation failed.");
754 opAddErrorType operation(mCurrentRun, (char*)aMsgId);
755 treeOp->Init(mozilla::AsVariant(operation));
758 void nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
759 nsAtom* aName) {
760 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
761 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
762 NS_ASSERTION(treeOp, "Tree op allocation failed.");
763 opAddErrorType operation(mCurrentRun, (char*)aMsgId, aName);
764 treeOp->Init(mozilla::AsVariant(operation));
767 void nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, nsAtom* aName,
768 nsAtom* aOther) {
769 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
770 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
771 NS_ASSERTION(treeOp, "Tree op allocation failed.");
772 opAddErrorType operation(mCurrentRun, (char*)aMsgId, aName, aOther);
773 treeOp->Init(mozilla::AsVariant(operation));
776 void nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId) {
777 MOZ_ASSERT(mAmpersand, "Adding error to ampersand without one!");
778 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
779 NS_ASSERTION(treeOp, "Tree op allocation failed.");
780 opAddErrorType operation(mAmpersand, (char*)aMsgId);
781 treeOp->Init(mozilla::AsVariant(operation));
784 void nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId) {
785 MOZ_ASSERT(mSlash, "Adding error to slash without one!");
786 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
787 NS_ASSERTION(treeOp, "Tree op allocation failed.");
788 opAddErrorType operation(mSlash, (char*)aMsgId);
789 treeOp->Init(mozilla::AsVariant(operation));