LCOV - code coverage report
Current view: top level - parser/html - nsHtml5Parser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 109 351 31.1 %
Date: 2017-07-14 16:53:18 Functions: 21 41 51.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13