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