Bumping manifests a=b2g-bump
[gecko.git] / parser / html / nsHtml5Highlighter.cpp
blob9ddfadd17f78d7fd32da03809b3f5e66b7b5b269
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 , mSeenBase(false)
66 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
69 nsHtml5Highlighter::~nsHtml5Highlighter()
71 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
74 void
75 nsHtml5Highlighter::Start(const nsAutoString& aTitle)
77 // Doctype
78 mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString());
80 mOpQueue.AppendElement()->Init(STANDARDS_MODE);
82 nsIContent** root = CreateElement(nsHtml5Atoms::html, nullptr, nullptr);
83 mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root);
84 mStack.AppendElement(root);
86 Push(nsGkAtoms::head, nullptr);
88 Push(nsGkAtoms::title, nullptr);
89 // XUL will add the "Source of: " prefix.
90 uint32_t length = aTitle.Length();
91 if (length > INT32_MAX) {
92 length = INT32_MAX;
94 AppendCharacters(aTitle.get(), 0, (int32_t)length);
95 Pop(); // title
97 Push(nsGkAtoms::link, nsHtml5ViewSourceUtils::NewLinkAttributes());
99 mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode());
101 Pop(); // link
103 Pop(); // head
105 Push(nsGkAtoms::body, nsHtml5ViewSourceUtils::NewBodyAttributes());
107 nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0);
108 nsString* preId = new nsString(NS_LITERAL_STRING("line1"));
109 preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId);
110 Push(nsGkAtoms::pre, preAttrs);
112 StartCharacters();
114 mOpQueue.AppendElement()->Init(eTreeOpStartLayout);
117 int32_t
118 nsHtml5Highlighter::Transition(int32_t aState, bool aReconsume, int32_t aPos)
120 mPos = aPos;
121 switch (mState) {
122 case NS_HTML5TOKENIZER_SCRIPT_DATA:
123 case NS_HTML5TOKENIZER_RAWTEXT:
124 case NS_HTML5TOKENIZER_RCDATA:
125 case NS_HTML5TOKENIZER_DATA:
126 // We can transition on < and on &. Either way, we don't yet know the
127 // role of the token, so open a span without class.
128 if (aState == NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE) {
129 StartSpan();
130 // Start another span for highlighting the ampersand
131 StartSpan();
132 mAmpersand = CurrentNode();
133 } else {
134 EndCharactersAndStartMarkupRun();
136 break;
137 case NS_HTML5TOKENIZER_TAG_OPEN:
138 switch (aState) {
139 case NS_HTML5TOKENIZER_TAG_NAME:
140 StartSpan(sStartTag);
141 break;
142 case NS_HTML5TOKENIZER_DATA:
143 FinishTag(); // DATA
144 break;
145 case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION:
146 AddClass(sPi);
147 break;
149 break;
150 case NS_HTML5TOKENIZER_TAG_NAME:
151 switch (aState) {
152 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
153 EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME
154 break;
155 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
156 EndSpanOrA(); // NS_HTML5TOKENIZER_TAG_NAME
157 StartSpan(); // for highlighting the slash
158 mSlash = CurrentNode();
159 break;
160 default:
161 FinishTag();
162 break;
164 break;
165 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
166 switch (aState) {
167 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
168 StartSpan(sAttributeName);
169 break;
170 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
171 StartSpan(); // for highlighting the slash
172 mSlash = CurrentNode();
173 break;
174 default:
175 FinishTag();
176 break;
178 break;
179 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
180 switch (aState) {
181 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
182 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
183 EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
184 break;
185 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
186 EndSpanOrA(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
187 StartSpan(); // for highlighting the slash
188 mSlash = CurrentNode();
189 break;
190 default:
191 FinishTag();
192 break;
194 break;
195 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
196 switch (aState) {
197 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
198 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
199 FlushCurrent();
200 StartA();
201 break;
202 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED:
203 StartA();
204 break;
205 default:
206 FinishTag();
207 break;
209 break;
210 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
211 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
212 switch (aState) {
213 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
214 EndSpanOrA();
215 break;
216 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
217 StartSpan();
218 StartSpan(); // for ampersand itself
219 mAmpersand = CurrentNode();
220 break;
221 default:
222 NS_NOTREACHED("Impossible transition.");
223 break;
225 break;
226 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
227 switch (aState) {
228 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
229 break;
230 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
231 StartSpan(); // for highlighting the slash
232 mSlash = CurrentNode();
233 break;
234 default:
235 FinishTag();
236 break;
238 break;
239 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
240 EndSpanOrA(); // end the slash highlight
241 switch (aState) {
242 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
243 break;
244 default:
245 FinishTag();
246 break;
248 break;
249 case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED:
250 switch (aState) {
251 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
252 EndSpanOrA();
253 break;
254 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
255 StartSpan();
256 StartSpan(); // for ampersand itself
257 mAmpersand = CurrentNode();
258 break;
259 default:
260 FinishTag();
261 break;
263 break;
264 case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
265 switch (aState) {
266 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
267 StartSpan(); // for highlighting the slash
268 mSlash = CurrentNode();
269 break;
270 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
271 break;
272 case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
273 StartSpan(sAttributeName);
274 break;
275 default:
276 FinishTag();
277 break;
279 break;
280 // most comment states are omitted, because they don't matter to
281 // highlighting
282 case NS_HTML5TOKENIZER_COMMENT_START:
283 case NS_HTML5TOKENIZER_COMMENT_END:
284 case NS_HTML5TOKENIZER_COMMENT_END_BANG:
285 case NS_HTML5TOKENIZER_COMMENT_START_DASH:
286 case NS_HTML5TOKENIZER_BOGUS_COMMENT:
287 case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN:
288 if (aState == NS_HTML5TOKENIZER_DATA) {
289 AddClass(sComment);
290 FinishTag();
292 break;
293 // most cdata states are omitted, because they don't matter to
294 // highlighting
295 case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB:
296 if (aState == NS_HTML5TOKENIZER_DATA) {
297 AddClass(sCdata);
298 FinishTag();
300 break;
301 case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
302 EndSpanOrA(); // the span for the ampersand
303 switch (aState) {
304 case NS_HTML5TOKENIZER_CONSUME_NCR:
305 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
306 break;
307 default:
308 // not actually a character reference
309 EndSpanOrA();
310 break;
312 break;
313 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
314 if (aState == NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL) {
315 break;
317 // not actually a character reference
318 EndSpanOrA();
319 break;
320 case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL:
321 if (!aReconsume) {
322 FlushCurrent();
324 EndSpanOrA();
325 break;
326 case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
327 case NS_HTML5TOKENIZER_HEX_NCR_LOOP:
328 switch (aState) {
329 case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE:
330 AddClass(sEntity);
331 FlushCurrent();
332 break;
333 case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE_RECONSUME:
334 AddClass(sEntity);
335 break;
337 EndSpanOrA();
338 break;
339 case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN:
340 switch (aState) {
341 case NS_HTML5TOKENIZER_DATA:
342 FinishTag();
343 break;
344 case NS_HTML5TOKENIZER_TAG_NAME:
345 StartSpan(sEndTag);
346 break;
348 break;
349 case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN:
350 if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) {
351 FlushCurrent();
352 StartSpan(); // don't know if it is "end-tag" yet :-(
353 break;
355 EndSpanOrA();
356 StartCharacters();
357 break;
358 case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME:
359 switch (aState) {
360 case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
361 AddClass(sEndTag);
362 EndSpanOrA();
363 break;
364 case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
365 AddClass(sEndTag);
366 EndSpanOrA();
367 StartSpan(); // for highlighting the slash
368 mSlash = CurrentNode();
369 break;
370 case NS_HTML5TOKENIZER_DATA: // yes, as a result of emitting the token
371 AddClass(sEndTag);
372 FinishTag();
373 break;
374 default:
375 FinishTag();
376 break;
378 break;
379 case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN:
380 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
381 if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) {
382 FlushCurrent();
383 StartSpan(); // don't know if it is "end-tag" yet :-(
384 break;
386 FinishTag();
387 break;
388 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH:
389 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED:
390 case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH:
391 if (aState == NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) {
392 EndCharactersAndStartMarkupRun();
394 break;
395 // Lots of double escape states omitted, because they don't highlight.
396 // Likewise, only doctype states that can emit the doctype are of
397 // interest. Otherwise, the transition out of bogus comment deals.
398 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME:
399 case NS_HTML5TOKENIZER_DOCTYPE_NAME:
400 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME:
401 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD:
402 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
403 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
404 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
405 case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
406 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
407 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
408 case NS_HTML5TOKENIZER_BOGUS_DOCTYPE:
409 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD:
410 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
411 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
412 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
413 if (aState == NS_HTML5TOKENIZER_DATA) {
414 AddClass(sDoctype);
415 FinishTag();
417 break;
418 case NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION_QUESTION_MARK:
419 if (aState == NS_HTML5TOKENIZER_DATA) {
420 FinishTag();
422 break;
423 default:
424 break;
426 mState = aState;
427 return aState;
430 void
431 nsHtml5Highlighter::End()
433 switch (mState) {
434 case NS_HTML5TOKENIZER_COMMENT_END:
435 case NS_HTML5TOKENIZER_COMMENT_END_BANG:
436 case NS_HTML5TOKENIZER_COMMENT_START_DASH:
437 case NS_HTML5TOKENIZER_BOGUS_COMMENT:
438 case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN:
439 AddClass(sComment);
440 break;
441 case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB:
442 AddClass(sCdata);
443 break;
444 case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
445 case NS_HTML5TOKENIZER_HEX_NCR_LOOP:
446 // XXX need tokenizer help here
447 break;
448 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME:
449 case NS_HTML5TOKENIZER_DOCTYPE_NAME:
450 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME:
451 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD:
452 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:
453 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
454 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
455 case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:
456 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
457 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER:
458 case NS_HTML5TOKENIZER_BOGUS_DOCTYPE:
459 case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD:
460 case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
461 case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
462 case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
463 AddClass(sDoctype);
464 break;
465 default:
466 break;
468 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
469 NS_ASSERTION(treeOp, "Tree op allocation failed.");
470 treeOp->Init(eTreeOpStreamEnded);
471 FlushOps();
474 void
475 nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer)
477 NS_PRECONDITION(!mBuffer, "Old buffer still here!");
478 mBuffer = aBuffer;
479 mCStart = aBuffer->getStart();
482 void
483 nsHtml5Highlighter::DropBuffer(int32_t aPos)
485 NS_PRECONDITION(mBuffer, "No buffer to drop!");
486 mPos = aPos;
487 FlushChars();
488 mBuffer = nullptr;
491 void
492 nsHtml5Highlighter::StartSpan()
494 FlushChars();
495 Push(nsGkAtoms::span, nullptr);
496 ++mInlinesOpen;
499 void
500 nsHtml5Highlighter::StartSpan(const char16_t* aClass)
502 StartSpan();
503 AddClass(aClass);
506 void
507 nsHtml5Highlighter::EndSpanOrA()
509 FlushChars();
510 Pop();
511 --mInlinesOpen;
514 void
515 nsHtml5Highlighter::StartCharacters()
517 NS_PRECONDITION(!mInCharacters, "Already in characters!");
518 FlushChars();
519 Push(nsGkAtoms::span, nullptr);
520 mCurrentRun = CurrentNode();
521 mInCharacters = true;
524 void
525 nsHtml5Highlighter::EndCharactersAndStartMarkupRun()
527 NS_PRECONDITION(mInCharacters, "Not in characters!");
528 FlushChars();
529 Pop();
530 mInCharacters = false;
531 // Now start markup run
532 StartSpan();
533 mCurrentRun = CurrentNode();
536 void
537 nsHtml5Highlighter::StartA()
539 FlushChars();
540 Push(nsGkAtoms::a, nullptr);
541 AddClass(sAttributeValue);
542 ++mInlinesOpen;
545 void
546 nsHtml5Highlighter::FinishTag()
548 while (mInlinesOpen > 1) {
549 EndSpanOrA();
551 FlushCurrent(); // >
552 EndSpanOrA(); // DATA
553 NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!");
554 StartCharacters();
557 void
558 nsHtml5Highlighter::FlushChars()
560 if (mCStart < mPos) {
561 char16_t* buf = mBuffer->getBuffer();
562 int32_t i = mCStart;
563 while (i < mPos) {
564 char16_t c = buf[i];
565 switch (c) {
566 case '\r':
567 // The input this code sees has been normalized so that there are
568 // CR breaks and LF breaks but no CRLF breaks. Overwrite CR with LF
569 // to show consistent LF line breaks to layout. It is OK to mutate
570 // the input data, because there are no reparses in the View Source
571 // case, so we won't need the original data in the buffer anymore.
572 buf[i] = '\n';
573 // fall through
574 case '\n': {
575 ++i;
576 if (mCStart < i) {
577 int32_t len = i - mCStart;
578 AppendCharacters(buf, mCStart, len);
579 mCStart = i;
581 ++mLineNumber;
582 Push(nsGkAtoms::span, nullptr);
583 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
584 NS_ASSERTION(treeOp, "Tree op allocation failed.");
585 treeOp->InitAddLineNumberId(CurrentNode(), mLineNumber);
586 Pop();
587 break;
589 default:
590 ++i;
591 break;
594 if (mCStart < mPos) {
595 int32_t len = mPos - mCStart;
596 AppendCharacters(buf, mCStart, len);
597 mCStart = mPos;
602 void
603 nsHtml5Highlighter::FlushCurrent()
605 mPos++;
606 FlushChars();
609 bool
610 nsHtml5Highlighter::FlushOps()
612 bool hasOps = !mOpQueue.IsEmpty();
613 if (hasOps) {
614 mOpSink->MoveOpsFrom(mOpQueue);
616 return hasOps;
619 void
620 nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName,
621 nsString* aValue)
623 if (!(nsHtml5AttributeName::ATTR_HREF == aName ||
624 nsHtml5AttributeName::ATTR_SRC == aName ||
625 nsHtml5AttributeName::ATTR_ACTION == aName ||
626 nsHtml5AttributeName::ATTR_CITE == aName ||
627 nsHtml5AttributeName::ATTR_BACKGROUND == aName ||
628 nsHtml5AttributeName::ATTR_LONGDESC == aName ||
629 nsHtml5AttributeName::ATTR_XLINK_HREF == aName ||
630 nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) {
631 return;
633 AddViewSourceHref(*aValue);
636 void
637 nsHtml5Highlighter::CompletedNamedCharacterReference()
639 AddClass(sEntity);
642 nsIContent**
643 nsHtml5Highlighter::AllocateContentHandle()
645 if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) {
646 mOldHandles.AppendElement(mHandles.forget());
647 mHandles = new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH];
648 mHandlesUsed = 0;
650 #ifdef DEBUG
651 mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD;
652 #endif
653 return &mHandles[mHandlesUsed++];
656 nsIContent**
657 nsHtml5Highlighter::CreateElement(nsIAtom* aName,
658 nsHtml5HtmlAttributes* aAttributes,
659 nsIContent** aIntendedParent)
661 NS_PRECONDITION(aName, "Got null name.");
662 nsIContent** content = AllocateContentHandle();
663 mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML,
664 aName,
665 aAttributes,
666 content,
667 aIntendedParent,
668 true);
669 return content;
672 nsIContent**
673 nsHtml5Highlighter::CurrentNode()
675 NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack.");
676 return mStack[mStack.Length() - 1];
679 void
680 nsHtml5Highlighter::Push(nsIAtom* aName,
681 nsHtml5HtmlAttributes* aAttributes)
683 NS_PRECONDITION(mStack.Length() >= 1, "Pushing without root.");
684 nsIContent** elt = CreateElement(aName, aAttributes, CurrentNode()); // Don't inline below!
685 mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode());
686 mStack.AppendElement(elt);
689 void
690 nsHtml5Highlighter::Pop()
692 NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short.");
693 mStack.RemoveElementAt(mStack.Length() - 1);
696 void
697 nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
698 int32_t aStart,
699 int32_t aLength)
701 NS_PRECONDITION(aBuffer, "Null buffer");
703 char16_t* bufferCopy = new char16_t[aLength];
704 memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t));
706 mOpQueue.AppendElement()->Init(eTreeOpAppendText,
707 bufferCopy,
708 aLength,
709 CurrentNode());
713 void
714 nsHtml5Highlighter::AddClass(const char16_t* aClass)
716 if (!mSyntaxHighlight) {
717 return;
719 mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
722 void
723 nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue)
725 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
726 memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(char16_t));
727 bufferCopy[aValue.Length()] = 0;
729 mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref,
730 bufferCopy,
731 aValue.Length(),
732 CurrentNode());
735 void
736 nsHtml5Highlighter::AddBase(const nsString& aValue)
738 if(mSeenBase) {
739 return;
741 mSeenBase = true;
742 char16_t* bufferCopy = new char16_t[aValue.Length() + 1];
743 memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(char16_t));
744 bufferCopy[aValue.Length()] = 0;
746 mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceBase,
747 bufferCopy,
748 aValue.Length());
751 void
752 nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
754 if (!mSyntaxHighlight) {
755 return;
757 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
758 NS_ASSERTION(treeOp, "Tree op allocation failed.");
759 treeOp->Init(CurrentNode(), aMsgId);
762 void
763 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
765 if (!mSyntaxHighlight) {
766 return;
768 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
769 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
770 NS_ASSERTION(treeOp, "Tree op allocation failed.");
771 treeOp->Init(mCurrentRun, aMsgId);
774 void
775 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
776 nsIAtom* aName)
778 if (!mSyntaxHighlight) {
779 return;
781 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
782 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
783 NS_ASSERTION(treeOp, "Tree op allocation failed.");
784 treeOp->Init(mCurrentRun, aMsgId, aName);
787 void
788 nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
789 nsIAtom* aName,
790 nsIAtom* aOther)
792 if (!mSyntaxHighlight) {
793 return;
795 NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
796 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
797 NS_ASSERTION(treeOp, "Tree op allocation failed.");
798 treeOp->Init(mCurrentRun, aMsgId, aName, aOther);
801 void
802 nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
804 if (!mSyntaxHighlight) {
805 return;
807 NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
808 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
809 NS_ASSERTION(treeOp, "Tree op allocation failed.");
810 treeOp->Init(mAmpersand, aMsgId);
813 void
814 nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
816 if (!mSyntaxHighlight) {
817 return;
819 NS_PRECONDITION(mSlash, "Adding error to slash without one!");
820 nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
821 NS_ASSERTION(treeOp, "Tree op allocation failed.");
822 treeOp->Init(mSlash, aMsgId);