parser/html/nsHtml5Parser.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial