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