Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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 "nsHtml5Tokenizer.h"
8 #include "nsHtml5AttributeName.h"
9 #include "nsString.h"
10 #include "nsThreadUtils.h"
11 #include "nsHtml5ViewSourceUtils.h"
12 #include "mozilla/Preferences.h"
14 using namespace mozilla;
16 // The old code had a limit of 16 tokens. 1300 is a number picked my measuring
17 // the size of 16 tokens on cnn.com.
18 #define NS_HTML5_HIGHLIGHTER_PRE_BREAK_THRESHOLD 1300
20 char16_t nsHtml5Highlighter::sComment[] =
21 { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 };
23 char16_t nsHtml5Highlighter::sCdata[] =
24 { 'c', 'd', 'a', 't', 'a', 0 };
26 char16_t nsHtml5Highlighter::sEntity[] =
27 { 'e', 'n', 't', 'i', 't', 'y', 0 };
29 char16_t nsHtml5Highlighter::sEndTag[] =
30 { 'e', 'n', 'd', '-', 't', 'a', 'g', 0 };
32 char16_t nsHtml5Highlighter::sStartTag[] =
33 { 's', 't', 'a', 'r', 't', '-', '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[] =
39 { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-',
40 'v', 'a', 'l', 'u', 'e', 0 };
42 char16_t nsHtml5Highlighter::sDoctype[] =
43 { 'd', 'o', 'c', 't', 'y', 'p', 'e', 0 };
45 char16_t nsHtml5Highlighter::sPi[] =
46 { 'p', 'i', 0 };
48 nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink)
49 : mState(NS_HTML5TOKENIZER_DATA)
50 , mCStart(INT32_MAX)
51 , mPos(0)
52 , mLineNumber(1)
53 , mInlinesOpen(0)
54 , mInCharacters(false)
55 , mBuffer(nullptr)
56 , mSyntaxHighlight(Preferences::GetBool("view_source.syntax_highlight",
57 true))
58 , mOpSink(aOpSink)
59 , mCurrentRun(nullptr)
60 , mAmpersand(nullptr)
61 , mSlash(nullptr)
62 , mHandles(new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH])
63 , mHandlesUsed(0)
64 {
65 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
66 }
68 nsHtml5Highlighter::~nsHtml5Highlighter()
69 {
70 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
71 }
73 void
74 nsHtml5Highlighter::Start(const nsAutoString& aTitle)
75 {
76 // Doctype
77 mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString());
79 mOpQueue.AppendElement()->Init(STANDARDS_MODE);
81 nsIContent** root = CreateElement(nsHtml5Atoms::html, nullptr);
82 mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root);
83 mStack.AppendElement(root);
85 Push(nsGkAtoms::head, nullptr);
87 Push(nsGkAtoms::title, nullptr);
88 // XUL will add the "Source of: " prefix.
89 uint32_t length = aTitle.Length();
90 if (length > INT32_MAX) {
91 length = INT32_MAX;
92 }
93 AppendCharacters(aTitle.get(), 0, (int32_t)length);
94 Pop(); // title
96 Push(nsGkAtoms::link, nsHtml5ViewSourceUtils::NewLinkAttributes());
98 mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode());
100 Pop(); // link
102 Pop(); // head
104 Push(nsGkAtoms::body, nsHtml5ViewSourceUtils::NewBodyAttributes());
106 nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0);
107 nsString* preId = new nsString(NS_LITERAL_STRING("line1"));
108 preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId);
109 Push(nsGkAtoms::pre, preAttrs);
111 StartCharacters();
113 mOpQueue.AppendElement()->Init(eTreeOpStartLayout);
114 }
116 int32_t
117 nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume, int32_t aPos)
118 {
119 mPos = aPos;
120 switch (mState) {
121 case NS_HTML5TOKENIZER_SCRIPT_DATA:
122 case NS_HTML5TOKENIZER_RAWTEXT:
123 case NS_HTML5TOKENIZER_RCDATA:
124 case NS_HTML5TOKENIZER_DATA:
125 // We can transition on < and on &. Either way, we don't yet know the
126 // role of the token, so open a span without class.
127 if (aState == NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE) {
128 StartSpan();
129 // Start another span for highlighting the ampersand
130 StartSpan();
131 mAmpersand = CurrentNode();
132 } else {
133 EndCharactersAndStartMarkupRun();
134 }
135 break;
136 case NS_HTML5TOKENIZER_TAG_OPEN:
137 switch (aState) {
138 case NS_HTML5TOKENIZER_TAG_NAME:
139 StartSpan(sStartTag);
140 break;
141 case NS_HTML5TOKENIZER_DATA:
142 FinishTag(); // DATA
143 break;
144 case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION:
145 AddClass(sPi);
146 break;
147 }
148 break;
149 case NS_HTML5TOKENIZER_TAG_NAME:
150 switch (aState) {
151 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
152 EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME
153 break;
154 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
155 EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME
156 StartSpan(); // for highlighting the slash
157 mSlash = CurrentNode();
158 break;
159 default:
160 FinishTag();
161 break;
162 }
163 break;
164 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
165 switch (aState) {
166 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
167 StartSpan(sAttributeName);
168 break;
169 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
170 StartSpan(); // for highlighting the slash
171 mSlash = CurrentNode();
172 break;
173 default:
174 FinishTag();
175 break;
176 }
177 break;
178 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
179 switch (aState) {
180 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
181 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
182 EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
183 break;
184 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
185 EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
186 StartSpan(); // for highlighting the slash
187 mSlash = CurrentNode();
188 break;
189 default:
190 FinishTag();
191 break;
192 }
193 break;
194 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
195 switch (aState) {
196 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
197 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
198 FlushCurrent();
199 StartA();
200 break;
201 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED:
202 StartA();
203 break;
204 default:
205 FinishTag();
206 break;
207 }
208 break;
209 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
210 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
211 switch (aState) {
212 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
213 EndSpanOrA();
214 break;
215 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
216 StartSpan();
217 StartSpan(); // for ampersand itself
218 mAmpersand = CurrentNode();
219 break;
220 default:
221 NS_NOTREACHED("Impossible transition.");
222 break;
223 }
224 break;
225 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
226 switch (aState) {
227 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
228 break;
229 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
230 StartSpan(); // for highlighting the slash
231 mSlash = CurrentNode();
232 break;
233 default:
234 FinishTag();
235 break;
236 }
237 break;
238 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
239 EndSpanOrA(); // end the slash highlight
240 switch (aState) {
241 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
242 break;
243 default:
244 FinishTag();
245 break;
246 }
247 break;
248 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED:
249 switch (aState) {
250 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
251 EndSpanOrA();
252 break;
253 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
254 StartSpan();
255 StartSpan(); // for ampersand itself
256 mAmpersand = CurrentNode();
257 break;
258 default:
259 FinishTag();
260 break;
261 }
262 break;
263 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
264 switch (aState) {
265 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
266 StartSpan(); // for highlighting the slash
267 mSlash = CurrentNode();
268 break;
269 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
270 break;
271 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
272 StartSpan(sAttributeName);
273 break;
274 default:
275 FinishTag();
276 break;
277 }
278 break;
279 // most comment states are omitted, because they don't matter to
280 // highlighting
281 case NS_HTML5TOKENIZER_COMMENT_START:
282 case NS_HTML5TOKENIZER_COMMENT_END:
283 case NS_HTML5TOKENIZER_COMMENT_END_BANG:
284 case NS_HTML5TOKENIZER_COMMENT_START_DASH:
285 case NS_HTML5TOKENIZER_BOGUS_COMMENT:
286 case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN:
287 if (aState == NS_HTML5TOKENIZER_DATA) {
288 AddClass(sComment);
289 FinishTag();
290 }
291 break;
292 // most cdata states are omitted, because they don't matter to
293 // highlighting
294 case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB:
295 if (aState == NS_HTML5TOKENIZER_DATA) {
296 AddClass(sCdata);
297 FinishTag();
298 }
299 break;
300 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
301 EndSpanOrA(); // the span for the ampersand
302 switch (aState) {
303 case NS_HTML5TOKENIZER_CONSUME_NCR:
304 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
305 break;
306 default:
307 // not actually a character reference
308 EndSpanOrA();
309 break;
310 }
311 break;
312 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
313 if (aState == NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL) {
314 break;
315 }
316 // not actually a character reference
317 EndSpanOrA();
318 break;
319 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL:
320 if (!aReconsume) {
321 FlushCurrent();
322 }
323 EndSpanOrA();
324 break;
325 case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
326 case NS_HTML5TOKENIZER_HEX_NCR_LOOP:
327 switch (aState) {
328 case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE:
329 AddClass(sEntity);
330 FlushCurrent();
331 break;
332 case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE_RECONSUME:
333 AddClass(sEntity);
334 break;
335 }
336 EndSpanOrA();
337 break;
338 case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN:
339 switch (aState) {
340 case NS_HTML5TOKENIZER_DATA:
341 FinishTag();
342 break;
343 case NS_HTML5TOKENIZER_TAG_NAME:
344 StartSpan(sEndTag);
345 break;
346 }
347 break;
348 case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN:
349 if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) {
350 FlushCurrent();
351 StartSpan(); // don't know if it is "end-tag" yet :-(
352 break;
353 }
354 EndSpanOrA();
355 StartCharacters();
356 break;
357 case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME:
358 switch (aState) {
359 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
360 AddClass(sEndTag);
361 EndSpanOrA();
362 break;
363 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
364 AddClass(sEndTag);
365 EndSpanOrA();
366 StartSpan(); // for highlighting the slash
367 mSlash = CurrentNode();
368 break;
369 case NS_HTML5TOKENIZER_DATA: // yes, as a result of emitting the token
370 AddClass(sEndTag);
371 FinishTag();
372 break;
373 default:
374 FinishTag();
375 break;
376 }
377 break;
378 case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN:
379 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
380 if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) {
381 FlushCurrent();
382 StartSpan(); // don't know if it is "end-tag" yet :-(
383 break;
384 }
385 FinishTag();
386 break;
387 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH:
388 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED:
389 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH:
390 if (aState == NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) {
391 EndCharactersAndStartMarkupRun();
392 }
393 break;
394 // Lots of double escape states omitted, because they don't highlight.
395 // Likewise, only doctype states that can emit the doctype are of
396 // interest. Otherwise, the transition out of bogus comment deals.
397 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME:
398 case NS_HTML5TOKENIZER_DOCTYPE_NAME:
399 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME:
400 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD:
401 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
402 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
403 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
404 case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
405 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
406 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
407 case NS_HTML5TOKENIZER_BOGUS_DOCTYPE:
408 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD:
409 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
410 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
411 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
412 if (aState == NS_HTML5TOKENIZER_DATA) {
413 AddClass(sDoctype);
414 FinishTag();
415 }
416 break;
417 case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION_QUESTION_MARK:
418 if (aState == NS_HTML5TOKENIZER_DATA) {
419 FinishTag();
420 }
421 break;
422 default:
423 break;
424 }
425 mState = aState;
426 return aState;
427 }
429 void
430 nsHtml5Highlighter::End()
431 {
432 switch (mState) {
433 case NS_HTML5TOKENIZER_COMMENT_END:
434 case NS_HTML5TOKENIZER_COMMENT_END_BANG:
435 case NS_HTML5TOKENIZER_COMMENT_START_DASH:
436 case NS_HTML5TOKENIZER_BOGUS_COMMENT:
437 case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN:
438 AddClass(sComment);
439 break;
440 case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB:
441 AddClass(sCdata);
442 break;
443 case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
444 case NS_HTML5TOKENIZER_HEX_NCR_LOOP:
445 // XXX need tokenizer help here
446 break;
447 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME:
448 case NS_HTML5TOKENIZER_DOCTYPE_NAME:
449 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME:
450 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD:
451 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
452 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
453 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
454 case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
455 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
456 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
457 case NS_HTML5TOKENIZER_BOGUS_DOCTYPE:
458 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD:
459 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
460 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
461 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
462 AddClass(sDoctype);
463 break;
464 default:
465 break;
466 }
467 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
468 NS_ASSERTION(treeOp, "Tree op allocation failed.");
469 treeOp->Init(eTreeOpStreamEnded);
470 FlushOps();
471 }
473 void
474 nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer)
475 {
476 NS_PRECONDITION(!mBuffer, "Old buffer still here!");
477 mBuffer = aBuffer;
478 mCStart = aBuffer->getStart();
479 }
481 void
482 nsHtml5Highlighter::DropBuffer(int32_t aPos)
483 {
484 NS_PRECONDITION(mBuffer, "No buffer to drop!");
485 mPos = aPos;
486 FlushChars();
487 mBuffer = nullptr;
488 }
490 void
491 nsHtml5Highlighter::StartSpan()
492 {
493 FlushChars();
494 Push(nsGkAtoms::span, nullptr);
495 ++mInlinesOpen;
496 }
498 void
499 nsHtml5Highlighter::StartSpan(const char16_t* aClass)
500 {
501 StartSpan();
502 AddClass(aClass);
503 }
505 void
506 nsHtml5Highlighter::EndSpanOrA()
507 {
508 FlushChars();
509 Pop();
510 --mInlinesOpen;
511 }
513 void
514 nsHtml5Highlighter::StartCharacters()
515 {
516 NS_PRECONDITION(!mInCharacters, "Already in characters!");
517 FlushChars();
518 Push(nsGkAtoms::span, nullptr);
519 mCurrentRun = CurrentNode();
520 mInCharacters = true;
521 }
523 void
524 nsHtml5Highlighter::EndCharactersAndStartMarkupRun()
525 {
526 NS_PRECONDITION(mInCharacters, "Not in characters!");
527 FlushChars();
528 Pop();
529 mInCharacters = false;
530 // Now start markup run
531 StartSpan();
532 mCurrentRun = CurrentNode();
533 }
535 void
536 nsHtml5Highlighter::StartA()
537 {
538 FlushChars();
539 Push(nsGkAtoms::a, nullptr);
540 AddClass(sAttributeValue);
541 ++mInlinesOpen;
542 }
544 void
545 nsHtml5Highlighter::FinishTag()
546 {
547 while (mInlinesOpen > 1) {
548 EndSpanOrA();
549 }
550 FlushCurrent(); // >
551 EndSpanOrA(); // DATA
552 NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!");
553 StartCharacters();
554 }
556 void
557 nsHtml5Highlighter::FlushChars()
558 {
559 if (mCStart < mPos) {
560 char16_t* buf = mBuffer->getBuffer();
561 int32_t i = mCStart;
562 while (i < mPos) {
563 char16_t c = buf[i];
564 switch (c) {
565 case '\r':
566 // The input this code sees has been normalized so that there are
567 // CR breaks and LF breaks but no CRLF breaks. Overwrite CR with LF
568 // to show consistent LF line breaks to layout. It is OK to mutate
569 // the input data, because there are no reparses in the View Source
570 // case, so we won't need the original data in the buffer anymore.
571 buf[i] = '\n';
572 // fall through
573 case '\n': {
574 ++i;
575 if (mCStart < i) {
576 int32_t len = i - mCStart;
577 AppendCharacters(buf, mCStart, len);
578 mCStart = i;
579 }
580 ++mLineNumber;
581 Push(nsGkAtoms::span, nullptr);
582 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
583 NS_ASSERTION(treeOp, "Tree op allocation failed.");
584 treeOp->InitAddLineNumberId(CurrentNode(), mLineNumber);
585 Pop();
586 break;
587 }
588 default:
589 ++i;
590 break;
591 }
592 }
593 if (mCStart < mPos) {
594 int32_t len = mPos - mCStart;
595 AppendCharacters(buf, mCStart, len);
596 mCStart = mPos;
597 }
598 }
599 }
601 void
602 nsHtml5Highlighter::FlushCurrent()
603 {
604 mPos++;
605 FlushChars();
606 }
608 bool
609 nsHtml5Highlighter::FlushOps()
610 {
611 bool hasOps = !mOpQueue.IsEmpty();
612 if (hasOps) {
613 mOpSink->MoveOpsFrom(mOpQueue);
614 }
615 return hasOps;
616 }
618 void
619 nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName,
620 nsString* aValue)
621 {
622 if (!(nsHtml5AttributeName::ATTR_HREF == aName ||
623 nsHtml5AttributeName::ATTR_SRC == aName ||
624 nsHtml5AttributeName::ATTR_ACTION == aName ||
625 nsHtml5AttributeName::ATTR_CITE == aName ||
626 nsHtml5AttributeName::ATTR_BACKGROUND == aName ||
627 nsHtml5AttributeName::ATTR_LONGDESC == aName ||
628 nsHtml5AttributeName::ATTR_XLINK_HREF == aName ||
629 nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) {
630 return;
631 }
632 AddViewSourceHref(*aValue);
633 }
635 void
636 nsHtml5Highlighter::CompletedNamedCharacterReference()
637 {
638 AddClass(sEntity);
639 }
641 nsIContent**
642 nsHtml5Highlighter::AllocateContentHandle()
643 {
644 if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) {
645 mOldHandles.AppendElement(mHandles.forget());
646 mHandles = new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH];
647 mHandlesUsed = 0;
648 }
649 #ifdef DEBUG
650 mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD;
651 #endif
652 return &mHandles[mHandlesUsed++];
653 }
655 nsIContent**
656 nsHtml5Highlighter::CreateElement(nsIAtom* aName,
657 nsHtml5HtmlAttributes* aAttributes)
658 {
659 NS_PRECONDITION(aName, "Got null name.");
660 nsIContent** content = AllocateContentHandle();
661 mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML,
662 aName,
663 aAttributes,
664 content,
665 true);
666 return content;
667 }
669 nsIContent**
670 nsHtml5Highlighter::CurrentNode()
671 {
672 NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack.");
673 return mStack[mStack.Length() - 1];
674 }
676 void
677 nsHtml5Highlighter::Push(nsIAtom* aName,
678 nsHtml5HtmlAttributes* aAttributes)
679 {
680 NS_PRECONDITION(mStack.Length() >= 1, "Pushing without root.");
681 nsIContent** elt = CreateElement(aName, aAttributes); // Don't inline below!
682 mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode());
683 mStack.AppendElement(elt);
684 }
686 void
687 nsHtml5Highlighter::Pop()
688 {
689 NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short.");
690 mStack.RemoveElementAt(mStack.Length() - 1);
691 }
693 void
694 nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
695 int32_t aStart,
696 int32_t aLength)
697 {
698 NS_PRECONDITION(aBuffer, "Null buffer");
700 char16_t* bufferCopy = new char16_t[aLength];
701 memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t));
703 mOpQueue.AppendElement()->Init(eTreeOpAppendText,
704 bufferCopy,
705 aLength,
706 CurrentNode());
707 }
710 void
711 nsHtml5Highlighter::AddClass(const char16_t* aClass)
712 {
713 if (!mSyntaxHighlight) {
714 return;
715 }
716 mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
717 }
719 void
720 nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue)
721 {
722 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
723 memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(char16_t));
724 bufferCopy[aValue.Length()] = 0;
726 mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref,
727 bufferCopy,
728 aValue.Length(),
729 CurrentNode());
730 }
732 void
733 nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
734 {
735 if (!mSyntaxHighlight) {
736 return;
737 }
738 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
739 NS_ASSERTION(treeOp, "Tree op allocation failed.");
740 treeOp->Init(CurrentNode(), aMsgId);
741 }
743 void
744 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
745 {
746 if (!mSyntaxHighlight) {
747 return;
748 }
749 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
750 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
751 NS_ASSERTION(treeOp, "Tree op allocation failed.");
752 treeOp->Init(mCurrentRun, aMsgId);
753 }
755 void
756 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
757 nsIAtom* aName)
758 {
759 if (!mSyntaxHighlight) {
760 return;
761 }
762 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
763 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
764 NS_ASSERTION(treeOp, "Tree op allocation failed.");
765 treeOp->Init(mCurrentRun, aMsgId, aName);
766 }
768 void
769 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
770 nsIAtom* aName,
771 nsIAtom* aOther)
772 {
773 if (!mSyntaxHighlight) {
774 return;
775 }
776 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
777 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
778 NS_ASSERTION(treeOp, "Tree op allocation failed.");
779 treeOp->Init(mCurrentRun, aMsgId, aName, aOther);
780 }
782 void
783 nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
784 {
785 if (!mSyntaxHighlight) {
786 return;
787 }
788 NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
789 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
790 NS_ASSERTION(treeOp, "Tree op allocation failed.");
791 treeOp->Init(mAmpersand, aMsgId);
792 }
794 void
795 nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
796 {
797 if (!mSyntaxHighlight) {
798 return;
799 }
800 NS_PRECONDITION(mSlash, "Adding error to slash without one!");
801 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
802 NS_ASSERTION(treeOp, "Tree op allocation failed.");
803 treeOp->Init(mSlash, aMsgId);
804 }