Coverage Report

Created: 2018-09-25 14:53

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