Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/parser/html/nsHtml5Parser.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=2 et tw=79: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsHtml5Parser.h"
8
9
#include "mozilla/AutoRestore.h"
10
#include "nsCRT.h"
11
#include "nsContentUtils.h" // for kLoadAsData
12
#include "nsHtml5AtomTable.h"
13
#include "nsHtml5DependentUTF16Buffer.h"
14
#include "nsHtml5Tokenizer.h"
15
#include "nsHtml5TreeBuilder.h"
16
#include "nsNetUtil.h"
17
18
0
NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
19
0
  NS_INTERFACE_TABLE(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
20
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
21
0
NS_INTERFACE_MAP_END
22
23
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
24
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
25
26
NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
27
28
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
29
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExecutor)
30
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetStreamParser())
31
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
32
33
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
34
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
35
0
  tmp->DropStreamParser();
36
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37
38
nsHtml5Parser::nsHtml5Parser()
39
  : mLastWasCR(false)
40
  , mDocWriteSpeculativeLastWasCR(false)
41
  , mBlocked(0)
42
  , mDocWriteSpeculatorActive(false)
43
  , mInsertionPointPushLevel(0)
44
  , mDocumentClosed(false)
45
  , mInDocumentWrite(false)
46
  , mInsertionPointPermanentlyUndefined(false)
47
  , mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr))
48
  , mLastBuffer(mFirstBuffer)
49
  , mExecutor(new nsHtml5TreeOpExecutor())
50
  , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr))
51
  , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false))
52
  , mRootContextLineNumber(1)
53
  , mReturnToStreamParserPermitted(false)
54
0
{
55
0
  mTokenizer->setInterner(&mAtomTable);
56
0
}
57
58
nsHtml5Parser::~nsHtml5Parser()
59
0
{
60
0
  mTokenizer->end();
61
0
  if (mDocWriteSpeculativeTokenizer) {
62
0
    mDocWriteSpeculativeTokenizer->end();
63
0
  }
64
0
}
65
66
NS_IMETHODIMP_(void)
67
nsHtml5Parser::SetContentSink(nsIContentSink* aSink)
68
0
{
69
0
  NS_ASSERTION(aSink == static_cast<nsIContentSink*>(mExecutor),
70
0
               "Attempt to set a foreign sink.");
71
0
}
72
73
NS_IMETHODIMP_(nsIContentSink*)
74
nsHtml5Parser::GetContentSink()
75
0
{
76
0
  return static_cast<nsIContentSink*>(mExecutor);
77
0
}
78
79
NS_IMETHODIMP_(void)
80
nsHtml5Parser::GetCommand(nsCString& aCommand)
81
0
{
82
0
  aCommand.AssignLiteral("view");
83
0
}
84
85
NS_IMETHODIMP_(void)
86
nsHtml5Parser::SetCommand(const char* aCommand)
87
0
{
88
0
  NS_ASSERTION(!strcmp(aCommand, "view") || !strcmp(aCommand, "view-source") ||
89
0
                 !strcmp(aCommand, "external-resource") ||
90
0
                 !strcmp(aCommand, "import") || !strcmp(aCommand, kLoadAsData),
91
0
               "Unsupported parser command");
92
0
}
93
94
NS_IMETHODIMP_(void)
95
nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
96
0
{
97
0
  NS_ASSERTION(aParserCommand == eViewNormal,
98
0
               "Parser command was not eViewNormal.");
99
0
}
100
101
void
102
nsHtml5Parser::SetDocumentCharset(NotNull<const Encoding*> aEncoding,
103
                                  int32_t aCharsetSource)
104
0
{
105
0
  MOZ_ASSERT(!mExecutor->HasStarted(), "Document charset set too late.");
106
0
  MOZ_ASSERT(GetStreamParser(), "Setting charset on a script-only parser.");
107
0
  GetStreamParser()->SetDocumentCharset(aEncoding, aCharsetSource);
108
0
  mExecutor->SetDocumentCharsetAndSource(aEncoding, aCharsetSource);
109
0
}
110
111
NS_IMETHODIMP
112
nsHtml5Parser::GetChannel(nsIChannel** aChannel)
113
0
{
114
0
  if (GetStreamParser()) {
115
0
    return GetStreamParser()->GetChannel(aChannel);
116
0
  } else {
117
0
    return NS_ERROR_NOT_AVAILABLE;
118
0
  }
119
0
}
120
121
NS_IMETHODIMP
122
nsHtml5Parser::GetDTD(nsIDTD** aDTD)
123
0
{
124
0
  *aDTD = nullptr;
125
0
  return NS_OK;
126
0
}
127
128
nsIStreamListener*
129
nsHtml5Parser::GetStreamListener()
130
0
{
131
0
  return mStreamListener;
132
0
}
133
134
NS_IMETHODIMP
135
nsHtml5Parser::ContinueInterruptedParsing()
136
0
{
137
0
  MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
138
0
  return NS_ERROR_NOT_IMPLEMENTED;
139
0
}
140
141
NS_IMETHODIMP_(void)
142
nsHtml5Parser::BlockParser()
143
0
{
144
0
  mBlocked++;
145
0
}
146
147
NS_IMETHODIMP_(void)
148
nsHtml5Parser::UnblockParser()
149
0
{
150
0
  MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
151
0
  if (MOZ_LIKELY(mBlocked > 0)) {
152
0
    mBlocked--;
153
0
  }
154
0
  if (MOZ_LIKELY(mBlocked == 0) && mExecutor) {
155
0
    mExecutor->ContinueInterruptedParsingAsync();
156
0
  }
157
0
}
158
159
NS_IMETHODIMP_(void)
160
nsHtml5Parser::ContinueInterruptedParsingAsync()
161
0
{
162
0
  if (mExecutor) {
163
0
    mExecutor->ContinueInterruptedParsingAsync();
164
0
  }
165
0
}
166
167
NS_IMETHODIMP_(bool)
168
nsHtml5Parser::IsParserEnabled()
169
0
{
170
0
  return !mBlocked;
171
0
}
172
173
NS_IMETHODIMP_(bool)
174
nsHtml5Parser::IsComplete()
175
0
{
176
0
  return mExecutor->IsComplete();
177
0
}
178
179
NS_IMETHODIMP
180
nsHtml5Parser::Parse(nsIURI* aURL,
181
                     nsIRequestObserver* aObserver,
182
                     void* aKey,      // legacy; ignored
183
                     nsDTDMode aMode) // legacy; ignored
184
0
{
185
0
  /*
186
0
   * Do NOT cause WillBuildModel to be called synchronously from here!
187
0
   * The document won't be ready for it until OnStartRequest!
188
0
   */
189
0
  MOZ_ASSERT(!mExecutor->HasStarted(),
190
0
             "Tried to start parse without initializing the parser.");
191
0
  MOZ_ASSERT(GetStreamParser(),
192
0
             "Can't call this Parse() variant on script-created parser");
193
0
194
0
  GetStreamParser()->SetObserver(aObserver);
195
0
  GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
196
0
  mExecutor->SetStreamParser(GetStreamParser());
197
0
  mExecutor->SetParser(this);
198
0
  return NS_OK;
199
0
}
200
201
nsresult
202
nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
203
                     void* aKey,
204
                     const nsACString& aContentType,
205
                     bool aLastCall,
206
                     nsDTDMode aMode) // ignored
207
0
{
208
0
  nsresult rv;
209
0
  if (NS_FAILED(rv = mExecutor->IsBroken())) {
210
0
    return rv;
211
0
  }
212
0
  if (aSourceBuffer.Length() > INT32_MAX) {
213
0
    return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
214
0
  }
215
0
216
0
  // Maintain a reference to ourselves so we don't go away
217
0
  // till we're completely done. The old parser grips itself in this method.
218
0
  nsCOMPtr<nsIParser> kungFuDeathGrip(this);
219
0
220
0
  // Gripping the other objects just in case, since the other old grip
221
0
  // required grips to these, too.
222
0
  RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
223
0
  mozilla::Unused << streamKungFuDeathGrip; // Not used within function
224
0
  RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
225
0
226
0
  if (!executor->HasStarted()) {
227
0
    NS_ASSERTION(!GetStreamParser(),
228
0
                 "Had stream parser but document.write started life cycle.");
229
0
    // This is the first document.write() on a document.open()ed document
230
0
    executor->SetParser(this);
231
0
    mTreeBuilder->setScriptingEnabled(executor->IsScriptEnabled());
232
0
233
0
    bool isSrcdoc = false;
234
0
    nsCOMPtr<nsIChannel> channel;
235
0
    rv = GetChannel(getter_AddRefs(channel));
236
0
    if (NS_SUCCEEDED(rv)) {
237
0
      isSrcdoc = NS_IsSrcdocChannel(channel);
238
0
    }
239
0
    mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
240
0
241
0
    mTokenizer->start();
242
0
    executor->Start();
243
0
    if (!aContentType.EqualsLiteral("text/html")) {
244
0
      mTreeBuilder->StartPlainText();
245
0
      mTokenizer->StartPlainText();
246
0
    }
247
0
    /*
248
0
     * If you move the following line, be very careful not to cause
249
0
     * WillBuildModel to be called before the document has had its
250
0
     * script global object set.
251
0
     */
252
0
    rv = executor->WillBuildModel(eDTDMode_unknown);
253
0
    NS_ENSURE_SUCCESS(rv, rv);
254
0
  }
255
0
256
0
  // Return early if the parser has processed EOF
257
0
  if (executor->IsComplete()) {
258
0
    return NS_OK;
259
0
  }
260
0
261
0
  if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
262
0
    // document.close()
263
0
    NS_ASSERTION(!GetStreamParser(),
264
0
                 "Had stream parser but got document.close().");
265
0
    if (mDocumentClosed) {
266
0
      // already closed
267
0
      return NS_OK;
268
0
    }
269
0
    mDocumentClosed = true;
270
0
    if (!mBlocked && !mInDocumentWrite) {
271
0
      return ParseUntilBlocked();
272
0
    }
273
0
    return NS_OK;
274
0
  }
275
0
276
0
  // If we got this far, we are dealing with a document.write or
277
0
  // document.writeln call--not document.close().
278
0
279
0
  MOZ_RELEASE_ASSERT(
280
0
    IsInsertionPointDefined(),
281
0
    "Doc.write reached parser with undefined insertion point.");
282
0
283
0
  MOZ_RELEASE_ASSERT(!(GetStreamParser() && !aKey),
284
0
                     "Got a null key in a non-script-created parser");
285
0
286
0
  // XXX is this optimization bogus?
287
0
  if (aSourceBuffer.IsEmpty()) {
288
0
    return NS_OK;
289
0
  }
290
0
291
0
  // This guard is here to prevent document.close from tokenizing synchronously
292
0
  // while a document.write (that wrote the script that called document.close!)
293
0
  // is still on the call stack.
294
0
  mozilla::AutoRestore<bool> guard(mInDocumentWrite);
295
0
  mInDocumentWrite = true;
296
0
297
0
  // The script is identified by aKey. If there's nothing in the buffer
298
0
  // chain for that key, we'll insert at the head of the queue.
299
0
  // When the script leaves something in the queue, a zero-length
300
0
  // key-holder "buffer" is inserted in the queue. If the same script
301
0
  // leaves something in the chain again, it will be inserted immediately
302
0
  // before the old key holder belonging to the same script.
303
0
  //
304
0
  // We don't do the actual data insertion yet in the hope that the data gets
305
0
  // tokenized and there no data or less data to copy to the heap after
306
0
  // tokenization. Also, this way, we avoid inserting one empty data buffer
307
0
  // per document.write, which matters for performance when the parser isn't
308
0
  // blocked and a badly-authored script calls document.write() once per
309
0
  // input character. (As seen in a benchmark!)
310
0
  //
311
0
  // The insertion into the input stream happens conceptually before anything
312
0
  // gets tokenized. To make sure multi-level document.write works right,
313
0
  // it's necessary to establish the location of our parser key up front
314
0
  // in case this is the first write with this key.
315
0
  //
316
0
  // In a document.open() case, the first write level has a null key, so that
317
0
  // case is handled separately, because normal buffers containing data
318
0
  // have null keys.
319
0
320
0
  // These don't need to be owning references, because they always point to
321
0
  // the buffer queue and buffers can't be removed from the buffer queue
322
0
  // before document.write() returns. The buffer queue clean-up happens the
323
0
  // next time ParseUntilBlocked() is called.
324
0
  // However, they are made owning just in case the reasoning above is flawed
325
0
  // and a flaw would lead to worse problems with plain pointers. If this
326
0
  // turns out to be a perf problem, it's worthwhile to consider making
327
0
  // prevSearchbuf a plain pointer again.
328
0
  RefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
329
0
  RefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
330
0
331
0
  if (aKey) {
332
0
    if (mFirstBuffer == mLastBuffer) {
333
0
      nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
334
0
      keyHolder->next = mLastBuffer;
335
0
      mFirstBuffer = keyHolder;
336
0
    } else if (mFirstBuffer->key != aKey) {
337
0
      prevSearchBuf = mFirstBuffer;
338
0
      for (;;) {
339
0
        if (prevSearchBuf->next == mLastBuffer) {
340
0
          // key was not found
341
0
          nsHtml5OwningUTF16Buffer* keyHolder =
342
0
            new nsHtml5OwningUTF16Buffer(aKey);
343
0
          keyHolder->next = mFirstBuffer;
344
0
          mFirstBuffer = keyHolder;
345
0
          prevSearchBuf = nullptr;
346
0
          break;
347
0
        }
348
0
        if (prevSearchBuf->next->key == aKey) {
349
0
          // found a key holder
350
0
          break;
351
0
        }
352
0
        prevSearchBuf = prevSearchBuf->next;
353
0
      }
354
0
    } // else mFirstBuffer is the keyholder
355
0
356
0
    // prevSearchBuf is the previous buffer before the keyholder or null if
357
0
    // there isn't one.
358
0
  } else {
359
0
    // We have a first-level write in the document.open() case. We insert before
360
0
    // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
361
0
    // and redesignating the previous mLastBuffer as our firstLevelMarker.  We
362
0
    // need to put a marker there, because otherwise additional document.writes
363
0
    // from nested event loops would insert in the wrong place. Sigh.
364
0
    mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
365
0
    firstLevelMarker = mLastBuffer;
366
0
    mLastBuffer = mLastBuffer->next;
367
0
  }
368
0
369
0
  nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
370
0
371
0
  while (!mBlocked && stackBuffer.hasMore()) {
372
0
    stackBuffer.adjust(mLastWasCR);
373
0
    mLastWasCR = false;
374
0
    if (stackBuffer.hasMore()) {
375
0
      int32_t lineNumberSave;
376
0
      bool inRootContext = (!GetStreamParser() && !aKey);
377
0
      if (inRootContext) {
378
0
        mTokenizer->setLineNumber(mRootContextLineNumber);
379
0
      } else {
380
0
        // we aren't the root context, so save the line number on the
381
0
        // *stack* so that we can restore it.
382
0
        lineNumberSave = mTokenizer->getLineNumber();
383
0
      }
384
0
385
0
      if (!mTokenizer->EnsureBufferSpace(stackBuffer.getLength())) {
386
0
        return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
387
0
      }
388
0
      mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
389
0
      if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
390
0
        return executor->MarkAsBroken(rv);
391
0
      }
392
0
393
0
      if (inRootContext) {
394
0
        mRootContextLineNumber = mTokenizer->getLineNumber();
395
0
      } else {
396
0
        mTokenizer->setLineNumber(lineNumberSave);
397
0
      }
398
0
399
0
      if (mTreeBuilder->HasScript()) {
400
0
        mTreeBuilder->Flush();               // Move ops to the executor
401
0
        rv = executor->FlushDocumentWrite(); // run the ops
402
0
        NS_ENSURE_SUCCESS(rv, rv);
403
0
        // Flushing tree ops can cause all sorts of things.
404
0
        // Return early if the parser got terminated.
405
0
        if (executor->IsComplete()) {
406
0
          return NS_OK;
407
0
        }
408
0
      }
409
0
      // Ignore suspension requests
410
0
    }
411
0
  }
412
0
413
0
  RefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
414
0
  if (stackBuffer.hasMore()) {
415
0
    // The buffer wasn't tokenized to completion. Create a copy of the tail
416
0
    // on the heap.
417
0
    heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
418
0
    if (!heapBuffer) {
419
0
      // Allocation failed. The parser is now broken.
420
0
      return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
421
0
    }
422
0
  }
423
0
424
0
  if (heapBuffer) {
425
0
    // We have something to insert before the keyholder holding in the non-null
426
0
    // aKey case and we have something to swap into firstLevelMarker in the
427
0
    // null aKey case.
428
0
    if (aKey) {
429
0
      NS_ASSERTION(mFirstBuffer != mLastBuffer, "Where's the keyholder?");
430
0
      // the key holder is still somewhere further down the list from
431
0
      // prevSearchBuf (which may be null)
432
0
      if (mFirstBuffer->key == aKey) {
433
0
        NS_ASSERTION(
434
0
          !prevSearchBuf,
435
0
          "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
436
0
        heapBuffer->next = mFirstBuffer;
437
0
        mFirstBuffer = heapBuffer;
438
0
      } else {
439
0
        if (!prevSearchBuf) {
440
0
          prevSearchBuf = mFirstBuffer;
441
0
        }
442
0
        // We created a key holder earlier, so we will find it without walking
443
0
        // past the end of the list.
444
0
        while (prevSearchBuf->next->key != aKey) {
445
0
          prevSearchBuf = prevSearchBuf->next;
446
0
        }
447
0
        heapBuffer->next = prevSearchBuf->next;
448
0
        prevSearchBuf->next = heapBuffer;
449
0
      }
450
0
    } else {
451
0
      NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
452
0
      firstLevelMarker->Swap(heapBuffer);
453
0
    }
454
0
  }
455
0
456
0
  if (!mBlocked) { // buffer was tokenized to completion
457
0
    NS_ASSERTION(!stackBuffer.hasMore(),
458
0
                 "Buffer wasn't tokenized to completion?");
459
0
    // Scripting semantics require a forced tree builder flush here
460
0
    mTreeBuilder->Flush();               // Move ops to the executor
461
0
    rv = executor->FlushDocumentWrite(); // run the ops
462
0
    NS_ENSURE_SUCCESS(rv, rv);
463
0
  } else if (stackBuffer.hasMore()) {
464
0
    // The buffer wasn't tokenized to completion. Tokenize the untokenized
465
0
    // content in order to preload stuff. This content will be retokenized
466
0
    // later for normal parsing.
467
0
    if (!mDocWriteSpeculatorActive) {
468
0
      mDocWriteSpeculatorActive = true;
469
0
      if (!mDocWriteSpeculativeTreeBuilder) {
470
0
        // Lazily initialize if uninitialized
471
0
        mDocWriteSpeculativeTreeBuilder =
472
0
          new nsHtml5TreeBuilder(nullptr, executor->GetStage());
473
0
        mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
474
0
          mTreeBuilder->isScriptingEnabled());
475
0
        mDocWriteSpeculativeTokenizer =
476
0
          new nsHtml5Tokenizer(mDocWriteSpeculativeTreeBuilder, false);
477
0
        mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
478
0
        mDocWriteSpeculativeTokenizer->start();
479
0
      }
480
0
      mDocWriteSpeculativeTokenizer->resetToDataState();
481
0
      mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
482
0
      mDocWriteSpeculativeLastWasCR = false;
483
0
    }
484
0
485
0
    // Note that with multilevel document.write if we didn't just activate the
486
0
    // speculator, it's possible that the speculator is now in the wrong state.
487
0
    // That's OK for the sake of simplicity. The worst that can happen is
488
0
    // that the speculative loads aren't exactly right. The content will be
489
0
    // reparsed anyway for non-preload purposes.
490
0
491
0
    // The buffer position for subsequent non-speculative parsing now lives
492
0
    // in heapBuffer, so it's ok to let the buffer position of stackBuffer
493
0
    // to be overwritten and not restored below.
494
0
    while (stackBuffer.hasMore()) {
495
0
      stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
496
0
      if (stackBuffer.hasMore()) {
497
0
        if (!mDocWriteSpeculativeTokenizer->EnsureBufferSpace(
498
0
              stackBuffer.getLength())) {
499
0
          return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
500
0
        }
501
0
        mDocWriteSpeculativeLastWasCR =
502
0
          mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
503
0
        nsresult rv;
504
0
        if (NS_FAILED((rv = mDocWriteSpeculativeTreeBuilder->IsBroken()))) {
505
0
          return executor->MarkAsBroken(rv);
506
0
        }
507
0
      }
508
0
    }
509
0
510
0
    mDocWriteSpeculativeTreeBuilder->Flush();
511
0
    mDocWriteSpeculativeTreeBuilder->DropHandles();
512
0
    executor->FlushSpeculativeLoads();
513
0
  }
514
0
515
0
  return NS_OK;
516
0
}
517
518
NS_IMETHODIMP
519
nsHtml5Parser::Terminate()
520
0
{
521
0
  // We should only call DidBuildModel once, so don't do anything if this is
522
0
  // the second time that Terminate has been called.
523
0
  if (mExecutor->IsComplete()) {
524
0
    return NS_OK;
525
0
  }
526
0
  // XXX - [ until we figure out a way to break parser-sink circularity ]
527
0
  // Hack - Hold a reference until we are completely done...
528
0
  nsCOMPtr<nsIParser> kungFuDeathGrip(this);
529
0
  RefPtr<nsHtml5StreamParser> streamParser(GetStreamParser());
530
0
  RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
531
0
  if (streamParser) {
532
0
    streamParser->Terminate();
533
0
  }
534
0
  return executor->DidBuildModel(true);
535
0
}
536
537
NS_IMETHODIMP
538
nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
539
                             nsTArray<nsString>& aTagStack)
540
0
{
541
0
  return NS_ERROR_NOT_IMPLEMENTED;
542
0
}
543
544
NS_IMETHODIMP
545
nsHtml5Parser::BuildModel()
546
0
{
547
0
  MOZ_ASSERT_UNREACHABLE("Don't call this!");
548
0
  return NS_ERROR_NOT_IMPLEMENTED;
549
0
}
550
551
NS_IMETHODIMP
552
nsHtml5Parser::CancelParsingEvents()
553
0
{
554
0
  MOZ_ASSERT_UNREACHABLE("Don't call this!");
555
0
  return NS_ERROR_NOT_IMPLEMENTED;
556
0
}
557
558
void
559
nsHtml5Parser::Reset()
560
0
{
561
0
  MOZ_ASSERT_UNREACHABLE("Don't call this!");
562
0
}
563
564
bool
565
nsHtml5Parser::IsInsertionPointDefined()
566
0
{
567
0
  return !mExecutor->IsFlushing() && !mInsertionPointPermanentlyUndefined &&
568
0
         (!GetStreamParser() || mInsertionPointPushLevel);
569
0
}
570
571
void
572
nsHtml5Parser::PushDefinedInsertionPoint()
573
0
{
574
0
  ++mInsertionPointPushLevel;
575
0
}
576
577
void
578
nsHtml5Parser::PopDefinedInsertionPoint()
579
0
{
580
0
  --mInsertionPointPushLevel;
581
0
}
582
583
void
584
nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
585
0
{
586
0
  MOZ_ASSERT(!mStreamListener, "Must not call this twice.");
587
0
  eParserMode mode = NORMAL;
588
0
  if (!nsCRT::strcmp(aCommand, "view-source")) {
589
0
    mode = VIEW_SOURCE_HTML;
590
0
  } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
591
0
    mode = VIEW_SOURCE_XML;
592
0
  } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
593
0
    mode = VIEW_SOURCE_PLAIN;
594
0
  } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
595
0
    mode = PLAIN_TEXT;
596
0
  } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
597
0
    mode = LOAD_AS_DATA;
598
0
  }
599
#ifdef DEBUG
600
  else {
601
    NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
602
                   !nsCRT::strcmp(aCommand, "external-resource") ||
603
                   !nsCRT::strcmp(aCommand, "import"),
604
                 "Unsupported parser command!");
605
  }
606
#endif
607
  mStreamListener =
608
0
    new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
609
0
}
610
611
bool
612
nsHtml5Parser::IsScriptCreated()
613
0
{
614
0
  return !GetStreamParser();
615
0
}
616
617
/* End nsIParser  */
618
619
// not from interface
620
nsresult
621
nsHtml5Parser::ParseUntilBlocked()
622
0
{
623
0
  nsresult rv = mExecutor->IsBroken();
624
0
  NS_ENSURE_SUCCESS(rv, rv);
625
0
  if (mBlocked || mInsertionPointPermanentlyUndefined ||
626
0
      mExecutor->IsComplete()) {
627
0
    return NS_OK;
628
0
  }
629
0
  NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
630
0
  NS_ASSERTION(!mInDocumentWrite,
631
0
               "ParseUntilBlocked entered while in doc.write!");
632
0
633
0
  mDocWriteSpeculatorActive = false;
634
0
635
0
  for (;;) {
636
0
    if (!mFirstBuffer->hasMore()) {
637
0
      if (mFirstBuffer == mLastBuffer) {
638
0
        if (mExecutor->IsComplete()) {
639
0
          // something like cache manisfests stopped the parse in mid-flight
640
0
          return NS_OK;
641
0
        }
642
0
        if (mDocumentClosed) {
643
0
          PermanentlyUndefineInsertionPoint();
644
0
          nsresult rv;
645
0
          MOZ_RELEASE_ASSERT(
646
0
            !GetStreamParser(),
647
0
            "This should only happen with script-created parser.");
648
0
          if (NS_SUCCEEDED((rv = mExecutor->IsBroken()))) {
649
0
            mTokenizer->eof();
650
0
            if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
651
0
              mExecutor->MarkAsBroken(rv);
652
0
            } else {
653
0
              mTreeBuilder->StreamEnded();
654
0
            }
655
0
          }
656
0
          mTreeBuilder->Flush();
657
0
          mExecutor->FlushDocumentWrite();
658
0
          // The below call does memory cleanup, so call it even if the
659
0
          // parser has been marked as broken.
660
0
          mTokenizer->end();
661
0
          return rv;
662
0
        }
663
0
        // never release the last buffer.
664
0
        NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
665
0
                     "Sentinel buffer had its indeces changed.");
666
0
        if (GetStreamParser()) {
667
0
          if (mReturnToStreamParserPermitted &&
668
0
              !mExecutor->IsScriptExecuting()) {
669
0
            mTreeBuilder->Flush();
670
0
            mReturnToStreamParserPermitted = false;
671
0
            GetStreamParser()->ContinueAfterScripts(
672
0
              mTokenizer, mTreeBuilder, mLastWasCR);
673
0
          }
674
0
        } else {
675
0
          // Script-created parser
676
0
          mTreeBuilder->Flush();
677
0
          // No need to flush the executor, because the executor is already
678
0
          // in a flush
679
0
          NS_ASSERTION(mExecutor->IsInFlushLoop(),
680
0
                       "How did we come here without being in the flush loop?");
681
0
        }
682
0
        return NS_OK; // no more data for now but expecting more
683
0
      }
684
0
      mFirstBuffer = mFirstBuffer->next;
685
0
      continue;
686
0
    }
687
0
688
0
    if (mBlocked || mExecutor->IsComplete()) {
689
0
      return NS_OK;
690
0
    }
691
0
692
0
    // now we have a non-empty buffer
693
0
    mFirstBuffer->adjust(mLastWasCR);
694
0
    mLastWasCR = false;
695
0
    if (mFirstBuffer->hasMore()) {
696
0
      bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
697
0
      if (inRootContext) {
698
0
        mTokenizer->setLineNumber(mRootContextLineNumber);
699
0
      }
700
0
      if (!mTokenizer->EnsureBufferSpace(mFirstBuffer->getLength())) {
701
0
        return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
702
0
      }
703
0
      mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
704
0
      nsresult rv;
705
0
      if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
706
0
        return mExecutor->MarkAsBroken(rv);
707
0
      }
708
0
      if (inRootContext) {
709
0
        mRootContextLineNumber = mTokenizer->getLineNumber();
710
0
      }
711
0
      if (mTreeBuilder->HasScript()) {
712
0
        mTreeBuilder->Flush();
713
0
        rv = mExecutor->FlushDocumentWrite();
714
0
        NS_ENSURE_SUCCESS(rv, rv);
715
0
      }
716
0
      if (mBlocked) {
717
0
        return NS_OK;
718
0
      }
719
0
    }
720
0
  }
721
0
}
722
723
nsresult
724
nsHtml5Parser::Initialize(nsIDocument* aDoc,
725
                          nsIURI* aURI,
726
                          nsISupports* aContainer,
727
                          nsIChannel* aChannel)
728
0
{
729
0
  return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
730
0
}
731
732
void
733
nsHtml5Parser::StartTokenizer(bool aScriptingEnabled)
734
0
{
735
0
736
0
  bool isSrcdoc = false;
737
0
  nsCOMPtr<nsIChannel> channel;
738
0
  nsresult rv = GetChannel(getter_AddRefs(channel));
739
0
  if (NS_SUCCEEDED(rv)) {
740
0
    isSrcdoc = NS_IsSrcdocChannel(channel);
741
0
  }
742
0
  mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
743
0
744
0
  mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
745
0
  mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
746
0
  mTokenizer->start();
747
0
}
748
749
void
750
nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
751
                                             int32_t aLine)
752
0
{
753
0
  mTokenizer->resetToDataState();
754
0
  mTokenizer->setLineNumber(aLine);
755
0
  mTreeBuilder->loadState(aState, &mAtomTable);
756
0
  mLastWasCR = false;
757
0
  mReturnToStreamParserPermitted = true;
758
0
}
759
760
void
761
nsHtml5Parser::ContinueAfterFailedCharsetSwitch()
762
0
{
763
0
  MOZ_ASSERT(
764
0
    GetStreamParser(),
765
0
    "Tried to continue after failed charset switch without a stream parser");
766
0
  GetStreamParser()->ContinueAfterFailedCharsetSwitch();
767
0
}