Bug 1492908 [wpt PR 13122] - Update wpt metadata, a=testonly
[gecko.git] / parser / html / nsHtml5Highlighter.cpp
blobb9f15b0865659f34d5e64836a5a0ab09ab08b544
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 "nsDebug.h"
7 #include "nsHtml5AttributeName.h"
8 #include "nsHtml5Tokenizer.h"
9 #include "nsHtml5ViewSourceUtils.h"
10 #include "nsString.h"
11 #include "nsThreadUtils.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/Preferences.h"
16 using namespace mozilla;
18 // The old code had a limit of 16 tokens. 1300 is a number picked my measuring
19 // the size of 16 tokens on cnn.com.
20 #define NS_HTML5_HIGHLIGHTER_PRE_BREAK_THRESHOLD 1300
22 char16_t nsHtml5Highlighter::sComment[] = {
23 'c', 'o', 'm', 'm', '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[] = {
31 'e', 'n', 'd', '-', 't', 'a', 'g', 0
34 char16_t nsHtml5Highlighter::sStartTag[] = { 's', 't', 'a', 'r', 't',
35 '-', 't', 'a', 'g', 0 };
37 char16_t nsHtml5Highlighter::sAttributeName[] = { 'a', 't', 't', 'r', 'i',
38 'b', 'u', 't', 'e', '-',
39 'n', 'a', 'm', 'e', 0 };
41 char16_t nsHtml5Highlighter::sAttributeValue[] = { 'a', 't', 't', 'r', 'i', 'b',
42 'u', 't', 'e', '-', 'v', 'a',
43 'l', 'u', 'e', 0 };
45 char16_t nsHtml5Highlighter::sDoctype[] = {
46 'd', 'o', 'c', 't', 'y', 'p', 'e', 0
49 char16_t nsHtml5Highlighter::sPi[] = { 'p', 'i', 0 };
51 nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink)
52 : mState(nsHtml5Tokenizer::DATA)
53 , mCStart(INT32_MAX)
54 , mPos(0)
55 , mLineNumber(1)
56 , mInlinesOpen(0)
57 , mInCharacters(false)
58 , mBuffer(nullptr)
59 , mOpSink(aOpSink)
60 , mCurrentRun(nullptr)
61 , mAmpersand(nullptr)
62 , mSlash(nullptr)
63 , mHandles(
64 MakeUnique<nsIContent* []>(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH))
65 , mHandlesUsed(0)
66 , mSeenBase(false)
68 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
71 nsHtml5Highlighter::~nsHtml5Highlighter()
73 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
76 void
77 nsHtml5Highlighter::Start(const nsAutoString& aTitle)
79 // Doctype
80 mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString());
82 mOpQueue.AppendElement()->Init(STANDARDS_MODE);
84 // <html> uses NS_NewHTMLSharedElement creator
85 nsIContent** root =
86 CreateElement(nsGkAtoms::html, nullptr, nullptr, NS_NewHTMLSharedElement);
87 mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root);
88 mStack.AppendElement(root);
90 // <head> uses NS_NewHTMLSharedElement creator
91 Push(nsGkAtoms::head, nullptr, NS_NewHTMLSharedElement);
93 Push(nsGkAtoms::meta,
94 nsHtml5ViewSourceUtils::NewMetaViewportAttributes(),
95 NS_NewHTMLMetaElement);
96 Pop(); // meta
98 Push(nsGkAtoms::title, nullptr, NS_NewHTMLTitleElement);
99 // XUL will add the "Source of: " prefix.
100 uint32_t length = aTitle.Length();
101 if (length > INT32_MAX) {
102 length = INT32_MAX;
104 AppendCharacters(aTitle.BeginReading(), 0, (int32_t)length);
105 Pop(); // title
107 Push(nsGkAtoms::link,
108 nsHtml5ViewSourceUtils::NewLinkAttributes(),
109 NS_NewHTMLLinkElement);
111 mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode());
113 Pop(); // link
115 Pop(); // head
117 Push(nsGkAtoms::body,
118 nsHtml5ViewSourceUtils::NewBodyAttributes(),
119 NS_NewHTMLBodyElement);
121 nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0);
122 nsHtml5String preId = nsHtml5Portability::newStringFromLiteral("line1");
123 preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId, -1);
124 Push(nsGkAtoms::pre, preAttrs, NS_NewHTMLPreElement);
126 StartCharacters();
128 mOpQueue.AppendElement()->Init(eTreeOpStartLayout);
131 int32_t
132 nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume, int32_t aPos)
134 mPos = aPos;
135 switch (mState) {
136 case nsHtml5Tokenizer::SCRIPT_DATA:
137 case nsHtml5Tokenizer::RAWTEXT:
138 case nsHtml5Tokenizer::RCDATA:
139 case nsHtml5Tokenizer::DATA:
140 // We can transition on < and on &. Either way, we don't yet know the
141 // role of the token, so open a span without class.
142 if (aState == nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE) {
143 StartSpan();
144 // Start another span for highlighting the ampersand
145 StartSpan();
146 mAmpersand = CurrentNode();
147 } else {
148 EndCharactersAndStartMarkupRun();
150 break;
151 case nsHtml5Tokenizer::TAG_OPEN:
152 switch (aState) {
153 case nsHtml5Tokenizer::TAG_NAME:
154 StartSpan(sStartTag);
155 break;
156 case nsHtml5Tokenizer::DATA:
157 FinishTag(); // DATA
158 break;
159 case nsHtml5Tokenizer::PROCESSING_INSTRUCTION:
160 AddClass(sPi);
161 break;
163 break;
164 case nsHtml5Tokenizer::TAG_NAME:
165 switch (aState) {
166 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
167 EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
168 break;
169 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
170 EndSpanOrA(); // nsHtml5Tokenizer::TAG_NAME
171 StartSpan(); // for highlighting the slash
172 mSlash = CurrentNode();
173 break;
174 default:
175 FinishTag();
176 break;
178 break;
179 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
180 switch (aState) {
181 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
182 StartSpan(sAttributeName);
183 break;
184 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
185 StartSpan(); // for highlighting the slash
186 mSlash = CurrentNode();
187 break;
188 default:
189 FinishTag();
190 break;
192 break;
193 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
194 switch (aState) {
195 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
196 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
197 EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
198 break;
199 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
200 EndSpanOrA(); // nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME
201 StartSpan(); // for highlighting the slash
202 mSlash = CurrentNode();
203 break;
204 default:
205 FinishTag();
206 break;
208 break;
209 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
210 switch (aState) {
211 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
212 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
213 FlushCurrent();
214 StartA();
215 break;
216 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
217 StartA();
218 break;
219 default:
220 FinishTag();
221 break;
223 break;
224 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_DOUBLE_QUOTED:
225 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_SINGLE_QUOTED:
226 switch (aState) {
227 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
228 EndSpanOrA();
229 break;
230 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
231 StartSpan();
232 StartSpan(); // for ampersand itself
233 mAmpersand = CurrentNode();
234 break;
235 default:
236 MOZ_ASSERT_UNREACHABLE("Impossible transition.");
237 break;
239 break;
240 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_VALUE_QUOTED:
241 switch (aState) {
242 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
243 break;
244 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
245 StartSpan(); // for highlighting the slash
246 mSlash = CurrentNode();
247 break;
248 default:
249 FinishTag();
250 break;
252 break;
253 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
254 EndSpanOrA(); // end the slash highlight
255 switch (aState) {
256 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
257 break;
258 default:
259 FinishTag();
260 break;
262 break;
263 case nsHtml5Tokenizer::ATTRIBUTE_VALUE_UNQUOTED:
264 switch (aState) {
265 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
266 EndSpanOrA();
267 break;
268 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
269 StartSpan();
270 StartSpan(); // for ampersand itself
271 mAmpersand = CurrentNode();
272 break;
273 default:
274 FinishTag();
275 break;
277 break;
278 case nsHtml5Tokenizer::AFTER_ATTRIBUTE_NAME:
279 switch (aState) {
280 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
281 StartSpan(); // for highlighting the slash
282 mSlash = CurrentNode();
283 break;
284 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_VALUE:
285 break;
286 case nsHtml5Tokenizer::ATTRIBUTE_NAME:
287 StartSpan(sAttributeName);
288 break;
289 default:
290 FinishTag();
291 break;
293 break;
294 // most comment states are omitted, because they don't matter to
295 // highlighting
296 case nsHtml5Tokenizer::COMMENT_START:
297 case nsHtml5Tokenizer::COMMENT_END:
298 case nsHtml5Tokenizer::COMMENT_END_BANG:
299 case nsHtml5Tokenizer::COMMENT_START_DASH:
300 case nsHtml5Tokenizer::BOGUS_COMMENT:
301 case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
302 if (aState == nsHtml5Tokenizer::DATA) {
303 AddClass(sComment);
304 FinishTag();
306 break;
307 // most cdata states are omitted, because they don't matter to
308 // highlighting
309 case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
310 if (aState == nsHtml5Tokenizer::DATA) {
311 AddClass(sCdata);
312 FinishTag();
314 break;
315 case nsHtml5Tokenizer::CONSUME_CHARACTER_REFERENCE:
316 EndSpanOrA(); // the span for the ampersand
317 switch (aState) {
318 case nsHtml5Tokenizer::CONSUME_NCR:
319 case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
320 break;
321 default:
322 // not actually a character reference
323 EndSpanOrA();
324 break;
326 break;
327 case nsHtml5Tokenizer::CHARACTER_REFERENCE_HILO_LOOKUP:
328 if (aState == nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL) {
329 break;
331 // not actually a character reference
332 EndSpanOrA();
333 break;
334 case nsHtml5Tokenizer::CHARACTER_REFERENCE_TAIL:
335 if (!aReconsume) {
336 FlushCurrent();
338 EndSpanOrA();
339 break;
340 case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
341 case nsHtml5Tokenizer::HEX_NCR_LOOP:
342 switch (aState) {
343 case nsHtml5Tokenizer::HANDLE_NCR_VALUE:
344 AddClass(sEntity);
345 FlushCurrent();
346 break;
347 case nsHtml5Tokenizer::HANDLE_NCR_VALUE_RECONSUME:
348 AddClass(sEntity);
349 break;
351 EndSpanOrA();
352 break;
353 case nsHtml5Tokenizer::CLOSE_TAG_OPEN:
354 switch (aState) {
355 case nsHtml5Tokenizer::DATA:
356 FinishTag();
357 break;
358 case nsHtml5Tokenizer::TAG_NAME:
359 StartSpan(sEndTag);
360 break;
362 break;
363 case nsHtml5Tokenizer::RAWTEXT_RCDATA_LESS_THAN_SIGN:
364 if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
365 FlushCurrent();
366 StartSpan(); // don't know if it is "end-tag" yet :-(
367 break;
369 EndSpanOrA();
370 StartCharacters();
371 break;
372 case nsHtml5Tokenizer::NON_DATA_END_TAG_NAME:
373 switch (aState) {
374 case nsHtml5Tokenizer::BEFORE_ATTRIBUTE_NAME:
375 AddClass(sEndTag);
376 EndSpanOrA();
377 break;
378 case nsHtml5Tokenizer::SELF_CLOSING_START_TAG:
379 AddClass(sEndTag);
380 EndSpanOrA();
381 StartSpan(); // for highlighting the slash
382 mSlash = CurrentNode();
383 break;
384 case nsHtml5Tokenizer::DATA: // yes, as a result of emitting the token
385 AddClass(sEndTag);
386 FinishTag();
387 break;
388 default:
389 FinishTag();
390 break;
392 break;
393 case nsHtml5Tokenizer::SCRIPT_DATA_LESS_THAN_SIGN:
394 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
395 if (aState == nsHtml5Tokenizer::NON_DATA_END_TAG_NAME) {
396 FlushCurrent();
397 StartSpan(); // don't know if it is "end-tag" yet :-(
398 break;
400 FinishTag();
401 break;
402 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH_DASH:
403 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED:
404 case nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_DASH:
405 if (aState == nsHtml5Tokenizer::SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) {
406 EndCharactersAndStartMarkupRun();
408 break;
409 // Lots of double escape states omitted, because they don't highlight.
410 // Likewise, only doctype states that can emit the doctype are of
411 // interest. Otherwise, the transition out of bogus comment deals.
412 case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
413 case nsHtml5Tokenizer::DOCTYPE_NAME:
414 case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
415 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
416 case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
417 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
418 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
419 case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
420 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
421 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
422 case nsHtml5Tokenizer::BOGUS_DOCTYPE:
423 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
424 case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
425 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
426 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
427 if (aState == nsHtml5Tokenizer::DATA) {
428 AddClass(sDoctype);
429 FinishTag();
431 break;
432 case nsHtml5Tokenizer::PROCESSING_INSTRUCTION_QUESTION_MARK:
433 if (aState == nsHtml5Tokenizer::DATA) {
434 FinishTag();
436 break;
437 default:
438 break;
440 mState = aState;
441 return aState;
444 void
445 nsHtml5Highlighter::End()
447 switch (mState) {
448 case nsHtml5Tokenizer::COMMENT_END:
449 case nsHtml5Tokenizer::COMMENT_END_BANG:
450 case nsHtml5Tokenizer::COMMENT_START_DASH:
451 case nsHtml5Tokenizer::BOGUS_COMMENT:
452 case nsHtml5Tokenizer::BOGUS_COMMENT_HYPHEN:
453 AddClass(sComment);
454 break;
455 case nsHtml5Tokenizer::CDATA_RSQB_RSQB:
456 AddClass(sCdata);
457 break;
458 case nsHtml5Tokenizer::DECIMAL_NRC_LOOP:
459 case nsHtml5Tokenizer::HEX_NCR_LOOP:
460 // XXX need tokenizer help here
461 break;
462 case nsHtml5Tokenizer::BEFORE_DOCTYPE_NAME:
463 case nsHtml5Tokenizer::DOCTYPE_NAME:
464 case nsHtml5Tokenizer::AFTER_DOCTYPE_NAME:
465 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_KEYWORD:
466 case nsHtml5Tokenizer::BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
467 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
468 case nsHtml5Tokenizer::AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
469 case nsHtml5Tokenizer::BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
470 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
471 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
472 case nsHtml5Tokenizer::BOGUS_DOCTYPE:
473 case nsHtml5Tokenizer::AFTER_DOCTYPE_SYSTEM_KEYWORD:
474 case nsHtml5Tokenizer::BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
475 case nsHtml5Tokenizer::DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
476 case nsHtml5Tokenizer::DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
477 AddClass(sDoctype);
478 break;
479 default:
480 break;
482 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
483 NS_ASSERTION(treeOp, "Tree op allocation failed.");
484 treeOp->Init(eTreeOpStreamEnded);
485 FlushOps();
488 void
489 nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer)
491 MOZ_ASSERT(!mBuffer, "Old buffer still here!");
492 mBuffer = aBuffer;
493 mCStart = aBuffer->getStart();
496 void
497 nsHtml5Highlighter::DropBuffer(int32_t aPos)
499 MOZ_ASSERT(mBuffer, "No buffer to drop!");
500 mPos = aPos;
501 FlushChars();
502 mBuffer = nullptr;
505 void
506 nsHtml5Highlighter::StartSpan()
508 FlushChars();
509 Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
510 ++mInlinesOpen;
513 void
514 nsHtml5Highlighter::StartSpan(const char16_t* aClass)
516 StartSpan();
517 AddClass(aClass);
520 void
521 nsHtml5Highlighter::EndSpanOrA()
523 FlushChars();
524 Pop();
525 --mInlinesOpen;
528 void
529 nsHtml5Highlighter::StartCharacters()
531 MOZ_ASSERT(!mInCharacters, "Already in characters!");
532 FlushChars();
533 Push(nsGkAtoms::span, nullptr, NS_NewHTMLSpanElement);
534 mCurrentRun = CurrentNode();
535 mInCharacters = true;
538 void
539 nsHtml5Highlighter::EndCharactersAndStartMarkupRun()
541 MOZ_ASSERT(mInCharacters, "Not in characters!");
542 FlushChars();
543 Pop();
544 mInCharacters = false;
545 // Now start markup run
546 StartSpan();
547 mCurrentRun = CurrentNode();
550 void
551 nsHtml5Highlighter::StartA()
553 FlushChars();
554 Push(nsGkAtoms::a, nullptr, NS_NewHTMLAnchorElement);
555 AddClass(sAttributeValue);
556 ++mInlinesOpen;
559 void
560 nsHtml5Highlighter::FinishTag()
562 while (mInlinesOpen > 1) {
563 EndSpanOrA();
565 FlushCurrent(); // >
566 EndSpanOrA(); // DATA
567 NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!");
568 StartCharacters();
571 void
572 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 MOZ_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 treeOp->InitAddLineNumberId(CurrentNode(), mLineNumber);
600 Pop();
601 break;
603 default:
604 ++i;
605 break;
608 if (mCStart < mPos) {
609 int32_t len = mPos - mCStart;
610 AppendCharacters(buf, mCStart, len);
611 mCStart = mPos;
616 void
617 nsHtml5Highlighter::FlushCurrent()
619 mPos++;
620 FlushChars();
623 bool
624 nsHtml5Highlighter::FlushOps()
626 bool hasOps = !mOpQueue.IsEmpty();
627 if (hasOps) {
628 mOpSink->MoveOpsFrom(mOpQueue);
630 return hasOps;
633 void
634 nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName,
635 nsHtml5String aValue)
637 if (!(nsHtml5AttributeName::ATTR_HREF == aName ||
638 nsHtml5AttributeName::ATTR_SRC == aName ||
639 nsHtml5AttributeName::ATTR_ACTION == aName ||
640 nsHtml5AttributeName::ATTR_CITE == aName ||
641 nsHtml5AttributeName::ATTR_BACKGROUND == aName ||
642 nsHtml5AttributeName::ATTR_LONGDESC == aName ||
643 nsHtml5AttributeName::ATTR_XLINK_HREF == aName ||
644 nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) {
645 return;
647 AddViewSourceHref(aValue);
650 void
651 nsHtml5Highlighter::CompletedNamedCharacterReference()
653 AddClass(sEntity);
656 nsIContent**
657 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**
672 nsHtml5Highlighter::CreateElement(
673 nsAtom* aName,
674 nsHtml5HtmlAttributes* aAttributes,
675 nsIContent** aIntendedParent,
676 mozilla::dom::HTMLContentCreatorFunction aCreator)
678 MOZ_ASSERT(aName, "Got null name.");
679 nsHtml5ContentCreatorFunction creator;
680 creator.html = aCreator;
681 nsIContent** content = AllocateContentHandle();
682 mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML,
683 aName,
684 aAttributes,
685 content,
686 aIntendedParent,
687 true,
688 creator);
689 return content;
692 nsIContent**
693 nsHtml5Highlighter::CurrentNode()
695 MOZ_ASSERT(mStack.Length() >= 1, "Must have something on stack.");
696 return mStack[mStack.Length() - 1];
699 void
700 nsHtml5Highlighter::Push(nsAtom* aName,
701 nsHtml5HtmlAttributes* aAttributes,
702 mozilla::dom::HTMLContentCreatorFunction aCreator)
704 MOZ_ASSERT(mStack.Length() >= 1, "Pushing without root.");
705 nsIContent** elt = CreateElement(aName,
706 aAttributes,
707 CurrentNode(),
708 aCreator); // Don't inline below!
709 mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode());
710 mStack.AppendElement(elt);
713 void
714 nsHtml5Highlighter::Pop()
716 MOZ_ASSERT(mStack.Length() >= 2, "Popping when stack too short.");
717 mStack.RemoveLastElement();
720 void
721 nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
722 int32_t aStart,
723 int32_t aLength)
725 MOZ_ASSERT(aBuffer, "Null buffer");
727 char16_t* bufferCopy = new char16_t[aLength];
728 memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t));
730 mOpQueue.AppendElement()->Init(
731 eTreeOpAppendText, bufferCopy, aLength, CurrentNode());
734 void
735 nsHtml5Highlighter::AddClass(const char16_t* aClass)
737 mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
740 void
741 nsHtml5Highlighter::AddViewSourceHref(nsHtml5String aValue)
743 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
744 aValue.CopyToBuffer(bufferCopy);
745 bufferCopy[aValue.Length()] = 0;
747 mOpQueue.AppendElement()->Init(
748 eTreeOpAddViewSourceHref, bufferCopy, aValue.Length(), CurrentNode());
751 void
752 nsHtml5Highlighter::AddBase(nsHtml5String aValue)
754 if (mSeenBase) {
755 return;
757 mSeenBase = true;
758 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
759 aValue.CopyToBuffer(bufferCopy);
760 bufferCopy[aValue.Length()] = 0;
762 mOpQueue.AppendElement()->Init(
763 eTreeOpAddViewSourceBase, bufferCopy, aValue.Length());
766 void
767 nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
769 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
770 NS_ASSERTION(treeOp, "Tree op allocation failed.");
771 treeOp->Init(CurrentNode(), aMsgId);
774 void
775 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
777 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
778 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
779 NS_ASSERTION(treeOp, "Tree op allocation failed.");
780 treeOp->Init(mCurrentRun, aMsgId);
783 void
784 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, nsAtom* aName)
786 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
787 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
788 NS_ASSERTION(treeOp, "Tree op allocation failed.");
789 treeOp->Init(mCurrentRun, aMsgId, aName);
792 void
793 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
794 nsAtom* aName,
795 nsAtom* aOther)
797 MOZ_ASSERT(mCurrentRun, "Adding error to run without one!");
798 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
799 NS_ASSERTION(treeOp, "Tree op allocation failed.");
800 treeOp->Init(mCurrentRun, aMsgId, aName, aOther);
803 void
804 nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
806 MOZ_ASSERT(mAmpersand, "Adding error to ampersand without one!");
807 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
808 NS_ASSERTION(treeOp, "Tree op allocation failed.");
809 treeOp->Init(mAmpersand, aMsgId);
812 void
813 nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
815 MOZ_ASSERT(mSlash, "Adding error to slash without one!");
816 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
817 NS_ASSERTION(treeOp, "Tree op allocation failed.");
818 treeOp->Init(mSlash, aMsgId);