|
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 #include "mozilla/Preferences.h" |
|
13 |
|
14 using namespace mozilla; |
|
15 |
|
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 |
|
19 |
|
20 char16_t nsHtml5Highlighter::sComment[] = |
|
21 { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 }; |
|
22 |
|
23 char16_t nsHtml5Highlighter::sCdata[] = |
|
24 { 'c', 'd', 'a', 't', 'a', 0 }; |
|
25 |
|
26 char16_t nsHtml5Highlighter::sEntity[] = |
|
27 { 'e', 'n', 't', 'i', 't', 'y', 0 }; |
|
28 |
|
29 char16_t nsHtml5Highlighter::sEndTag[] = |
|
30 { 'e', 'n', 'd', '-', 't', 'a', 'g', 0 }; |
|
31 |
|
32 char16_t nsHtml5Highlighter::sStartTag[] = |
|
33 { 's', 't', 'a', 'r', 't', '-', 't', 'a', 'g', 0 }; |
|
34 |
|
35 char16_t nsHtml5Highlighter::sAttributeName[] = |
|
36 { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', 'n', 'a', 'm', 'e', 0 }; |
|
37 |
|
38 char16_t nsHtml5Highlighter::sAttributeValue[] = |
|
39 { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', |
|
40 'v', 'a', 'l', 'u', 'e', 0 }; |
|
41 |
|
42 char16_t nsHtml5Highlighter::sDoctype[] = |
|
43 { 'd', 'o', 'c', 't', 'y', 'p', 'e', 0 }; |
|
44 |
|
45 char16_t nsHtml5Highlighter::sPi[] = |
|
46 { 'p', 'i', 0 }; |
|
47 |
|
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 } |
|
67 |
|
68 nsHtml5Highlighter::~nsHtml5Highlighter() |
|
69 { |
|
70 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
71 } |
|
72 |
|
73 void |
|
74 nsHtml5Highlighter::Start(const nsAutoString& aTitle) |
|
75 { |
|
76 // Doctype |
|
77 mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString()); |
|
78 |
|
79 mOpQueue.AppendElement()->Init(STANDARDS_MODE); |
|
80 |
|
81 nsIContent** root = CreateElement(nsHtml5Atoms::html, nullptr); |
|
82 mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root); |
|
83 mStack.AppendElement(root); |
|
84 |
|
85 Push(nsGkAtoms::head, nullptr); |
|
86 |
|
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 |
|
95 |
|
96 Push(nsGkAtoms::link, nsHtml5ViewSourceUtils::NewLinkAttributes()); |
|
97 |
|
98 mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode()); |
|
99 |
|
100 Pop(); // link |
|
101 |
|
102 Pop(); // head |
|
103 |
|
104 Push(nsGkAtoms::body, nsHtml5ViewSourceUtils::NewBodyAttributes()); |
|
105 |
|
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); |
|
110 |
|
111 StartCharacters(); |
|
112 |
|
113 mOpQueue.AppendElement()->Init(eTreeOpStartLayout); |
|
114 } |
|
115 |
|
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 } |
|
428 |
|
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 } |
|
472 |
|
473 void |
|
474 nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer) |
|
475 { |
|
476 NS_PRECONDITION(!mBuffer, "Old buffer still here!"); |
|
477 mBuffer = aBuffer; |
|
478 mCStart = aBuffer->getStart(); |
|
479 } |
|
480 |
|
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 } |
|
489 |
|
490 void |
|
491 nsHtml5Highlighter::StartSpan() |
|
492 { |
|
493 FlushChars(); |
|
494 Push(nsGkAtoms::span, nullptr); |
|
495 ++mInlinesOpen; |
|
496 } |
|
497 |
|
498 void |
|
499 nsHtml5Highlighter::StartSpan(const char16_t* aClass) |
|
500 { |
|
501 StartSpan(); |
|
502 AddClass(aClass); |
|
503 } |
|
504 |
|
505 void |
|
506 nsHtml5Highlighter::EndSpanOrA() |
|
507 { |
|
508 FlushChars(); |
|
509 Pop(); |
|
510 --mInlinesOpen; |
|
511 } |
|
512 |
|
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 } |
|
522 |
|
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 } |
|
534 |
|
535 void |
|
536 nsHtml5Highlighter::StartA() |
|
537 { |
|
538 FlushChars(); |
|
539 Push(nsGkAtoms::a, nullptr); |
|
540 AddClass(sAttributeValue); |
|
541 ++mInlinesOpen; |
|
542 } |
|
543 |
|
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 } |
|
555 |
|
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 } |
|
600 |
|
601 void |
|
602 nsHtml5Highlighter::FlushCurrent() |
|
603 { |
|
604 mPos++; |
|
605 FlushChars(); |
|
606 } |
|
607 |
|
608 bool |
|
609 nsHtml5Highlighter::FlushOps() |
|
610 { |
|
611 bool hasOps = !mOpQueue.IsEmpty(); |
|
612 if (hasOps) { |
|
613 mOpSink->MoveOpsFrom(mOpQueue); |
|
614 } |
|
615 return hasOps; |
|
616 } |
|
617 |
|
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 } |
|
634 |
|
635 void |
|
636 nsHtml5Highlighter::CompletedNamedCharacterReference() |
|
637 { |
|
638 AddClass(sEntity); |
|
639 } |
|
640 |
|
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 } |
|
654 |
|
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 } |
|
668 |
|
669 nsIContent** |
|
670 nsHtml5Highlighter::CurrentNode() |
|
671 { |
|
672 NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack."); |
|
673 return mStack[mStack.Length() - 1]; |
|
674 } |
|
675 |
|
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 } |
|
685 |
|
686 void |
|
687 nsHtml5Highlighter::Pop() |
|
688 { |
|
689 NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short."); |
|
690 mStack.RemoveElementAt(mStack.Length() - 1); |
|
691 } |
|
692 |
|
693 void |
|
694 nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer, |
|
695 int32_t aStart, |
|
696 int32_t aLength) |
|
697 { |
|
698 NS_PRECONDITION(aBuffer, "Null buffer"); |
|
699 |
|
700 char16_t* bufferCopy = new char16_t[aLength]; |
|
701 memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(char16_t)); |
|
702 |
|
703 mOpQueue.AppendElement()->Init(eTreeOpAppendText, |
|
704 bufferCopy, |
|
705 aLength, |
|
706 CurrentNode()); |
|
707 } |
|
708 |
|
709 |
|
710 void |
|
711 nsHtml5Highlighter::AddClass(const char16_t* aClass) |
|
712 { |
|
713 if (!mSyntaxHighlight) { |
|
714 return; |
|
715 } |
|
716 mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass); |
|
717 } |
|
718 |
|
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; |
|
725 |
|
726 mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref, |
|
727 bufferCopy, |
|
728 aValue.Length(), |
|
729 CurrentNode()); |
|
730 } |
|
731 |
|
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 } |
|
742 |
|
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 } |
|
754 |
|
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 } |
|
767 |
|
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 } |
|
781 |
|
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 } |
|
793 |
|
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 } |