LCOV - code coverage report
Current view: top level - parser/html - nsHtml5StreamParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 434 886 49.0 %
Date: 2017-07-14 16:53:18 Functions: 49 67 73.1 %
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 "nsHtml5StreamParser.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/Encoding.h"
      11             : #include "nsContentUtils.h"
      12             : #include "nsHtml5Tokenizer.h"
      13             : #include "nsIHttpChannel.h"
      14             : #include "nsHtml5Parser.h"
      15             : #include "nsHtml5TreeBuilder.h"
      16             : #include "nsHtml5AtomTable.h"
      17             : #include "nsHtml5Module.h"
      18             : #include "nsHtml5StreamParserPtr.h"
      19             : #include "nsIScriptError.h"
      20             : #include "mozilla/Preferences.h"
      21             : #include "mozilla/UniquePtrExtensions.h"
      22             : #include "nsHtml5Highlighter.h"
      23             : #include "expat_config.h"
      24             : #include "expat.h"
      25             : #include "nsINestedURI.h"
      26             : #include "nsCharsetSource.h"
      27             : #include "nsIWyciwygChannel.h"
      28             : #include "nsIThreadRetargetableRequest.h"
      29             : #include "nsPrintfCString.h"
      30             : #include "nsNetUtil.h"
      31             : #include "nsXULAppAPI.h"
      32             : #include "mozilla/SchedulerGroup.h"
      33             : 
      34             : using namespace mozilla;
      35             : 
      36             : int32_t nsHtml5StreamParser::sTimerInitialDelay = 120;
      37             : int32_t nsHtml5StreamParser::sTimerSubsequentDelay = 120;
      38             : 
      39             : // static
      40             : void
      41           3 : nsHtml5StreamParser::InitializeStatics()
      42             : {
      43             :   Preferences::AddIntVarCache(&sTimerInitialDelay,
      44           3 :                               "html5.flushtimer.initialdelay");
      45             :   Preferences::AddIntVarCache(&sTimerSubsequentDelay,
      46           3 :                               "html5.flushtimer.subsequentdelay");
      47           3 : }
      48             : 
      49             : /*
      50             :  * Note that nsHtml5StreamParser implements cycle collecting AddRef and
      51             :  * Release. Therefore, nsHtml5StreamParser must never be refcounted from
      52             :  * the parser thread!
      53             :  *
      54             :  * To work around this limitation, runnables posted by the main thread to the
      55             :  * parser thread hold their reference to the stream parser in an
      56             :  * nsHtml5StreamParserPtr. Upon creation, nsHtml5StreamParserPtr addrefs the object it holds
      57             :  * just like a regular nsRefPtr. This is OK, since the creation of the
      58             :  * runnable and the nsHtml5StreamParserPtr happens on the main thread.
      59             :  *
      60             :  * When the runnable is done on the parser thread, the destructor of
      61             :  * nsHtml5StreamParserPtr runs there. It doesn't call Release on the held object
      62             :  * directly. Instead, it posts another runnable back to the main thread where
      63             :  * that runnable calls Release on the wrapped object.
      64             :  *
      65             :  * When posting runnables in the other direction, the runnables have to be
      66             :  * created on the main thread when nsHtml5StreamParser is instantiated and
      67             :  * held for the lifetime of the nsHtml5StreamParser. This works, because the
      68             :  * same runnabled can be dispatched multiple times and currently runnables
      69             :  * posted from the parser thread to main thread don't need to wrap any
      70             :  * runnable-specific data. (In the other direction, the runnables most notably
      71             :  * wrap the byte data of the stream.)
      72             :  */
      73          12 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5StreamParser)
      74          13 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5StreamParser)
      75             : 
      76           6 : NS_INTERFACE_TABLE_HEAD(nsHtml5StreamParser)
      77           6 :   NS_INTERFACE_TABLE(nsHtml5StreamParser,
      78             :                      nsICharsetDetectionObserver)
      79           6 :   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5StreamParser)
      80           0 : NS_INTERFACE_MAP_END
      81             : 
      82             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5StreamParser)
      83             : 
      84           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
      85           0 :   tmp->DropTimer();
      86           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObserver)
      87           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
      88           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
      89           0 :   tmp->mExecutorFlusher = nullptr;
      90           0 :   tmp->mLoadFlusher = nullptr;
      91           0 :   tmp->mExecutor = nullptr;
      92           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChardet)
      93           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      94             : 
      95           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
      96           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObserver)
      97           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
      98           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
      99             :   // hack: count the strongly owned edge wrapped in the runnable
     100           0 :   if (tmp->mExecutorFlusher) {
     101           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExecutorFlusher->mExecutor");
     102           0 :     cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
     103             :   }
     104             :   // hack: count the strongly owned edge wrapped in the runnable
     105           0 :   if (tmp->mLoadFlusher) {
     106           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadFlusher->mExecutor");
     107           0 :     cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
     108             :   }
     109             :   // hack: count self if held by mChardet
     110           0 :   if (tmp->mChardet) {
     111           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChardet->mObserver");
     112           0 :     cb.NoteXPCOMChild(static_cast<nsICharsetDetectionObserver*>(tmp));
     113             :   }
     114           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     115             : 
     116           3 : class nsHtml5ExecutorFlusher : public Runnable
     117             : {
     118             :   private:
     119             :     RefPtr<nsHtml5TreeOpExecutor> mExecutor;
     120             :   public:
     121           2 :     explicit nsHtml5ExecutorFlusher(nsHtml5TreeOpExecutor* aExecutor)
     122           2 :       : Runnable("nsHtml5ExecutorFlusher")
     123           2 :       , mExecutor(aExecutor)
     124           2 :     {}
     125           8 :     NS_IMETHOD Run() override
     126             :     {
     127           8 :       if (!mExecutor->isInList()) {
     128           8 :         mExecutor->RunFlushLoop();
     129             :       }
     130           8 :       return NS_OK;
     131             :     }
     132             : };
     133             : 
     134           3 : class nsHtml5LoadFlusher : public Runnable
     135             : {
     136             :   private:
     137             :     RefPtr<nsHtml5TreeOpExecutor> mExecutor;
     138             :   public:
     139           2 :     explicit nsHtml5LoadFlusher(nsHtml5TreeOpExecutor* aExecutor)
     140           2 :       : Runnable("nsHtml5LoadFlusher")
     141           2 :       , mExecutor(aExecutor)
     142           2 :     {}
     143           2 :     NS_IMETHOD Run() override
     144             :     {
     145           2 :       mExecutor->FlushSpeculativeLoads();
     146           2 :       return NS_OK;
     147             :     }
     148             : };
     149             : 
     150           2 : nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
     151             :                                          nsHtml5Parser* aOwner,
     152           2 :                                          eParserMode aMode)
     153             :   : mSniffingLength(0)
     154             :   , mBomState(eBomState::BOM_SNIFFING_NOT_STARTED)
     155             :   , mCharsetSource(kCharsetUninitialized)
     156             :   , mEncoding(WINDOWS_1252_ENCODING)
     157             :   , mReparseForbidden(false)
     158             :   , mLastBuffer(nullptr) // Will be filled when starting
     159             :   , mExecutor(aExecutor)
     160           2 :   , mTreeBuilder(new nsHtml5TreeBuilder((aMode == VIEW_SOURCE_HTML ||
     161           4 :                                          aMode == VIEW_SOURCE_XML) ?
     162           2 :                                              nullptr : mExecutor->GetStage(),
     163           4 :                                          aMode == NORMAL ?
     164           4 :                                              mExecutor->GetStage() : nullptr))
     165           4 :   , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, aMode == VIEW_SOURCE_XML))
     166             :   , mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
     167             :   , mOwner(aOwner)
     168             :   , mLastWasCR(false)
     169             :   , mStreamState(eHtml5StreamState::STREAM_NOT_STARTED)
     170             :   , mSpeculating(false)
     171             :   , mAtEOF(false)
     172             :   , mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
     173             :   , mSpeculationFailureCount(0)
     174             :   , mTerminated(false)
     175             :   , mInterrupted(false)
     176             :   , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
     177           2 :   , mEventTarget(nsHtml5Module::GetStreamParserThread()->SerialEventTarget())
     178           2 :   , mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
     179           2 :   , mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
     180             :   , mFeedChardet(false)
     181             :   , mInitialEncodingWasFromParentFrame(false)
     182           4 :   , mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
     183             :   , mFlushTimerMutex("nsHtml5StreamParser mFlushTimerMutex")
     184             :   , mFlushTimerArmed(false)
     185             :   , mFlushTimerEverFired(false)
     186          16 :   , mMode(aMode)
     187             : {
     188           2 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     189           2 :   mFlushTimer->SetTarget(mEventTarget);
     190             : #ifdef DEBUG
     191           2 :   mAtomTable.SetPermittedLookupEventTarget(mEventTarget);
     192             : #endif
     193           2 :   mTokenizer->setInterner(&mAtomTable);
     194           2 :   mTokenizer->setEncodingDeclarationHandler(this);
     195             : 
     196           2 :   if (aMode == VIEW_SOURCE_HTML || aMode == VIEW_SOURCE_XML) {
     197             :     nsHtml5Highlighter* highlighter =
     198           0 :       new nsHtml5Highlighter(mExecutor->GetStage());
     199           0 :     mTokenizer->EnableViewSource(highlighter); // takes ownership
     200           0 :     mTreeBuilder->EnableViewSource(highlighter); // doesn't own
     201             :   }
     202             : 
     203             :   // Chardet instantiation adapted from File.
     204             :   // Chardet is initialized here even if it turns out to be useless
     205             :   // to make the chardet refcount its observer (nsHtml5StreamParser)
     206             :   // on the main thread.
     207             :   const nsAdoptingCString& detectorName =
     208           4 :     Preferences::GetLocalizedCString("intl.charset.detector");
     209           2 :   if (!detectorName.IsEmpty()) {
     210           0 :     nsAutoCString detectorContractID;
     211           0 :     detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
     212           0 :     detectorContractID += detectorName;
     213           0 :     if ((mChardet = do_CreateInstance(detectorContractID.get()))) {
     214           0 :       (void) mChardet->Init(this);
     215           0 :       mFeedChardet = true;
     216             :     }
     217             :   }
     218             : 
     219             :   // There's a zeroing operator new for everything else
     220           2 : }
     221             : 
     222           3 : nsHtml5StreamParser::~nsHtml5StreamParser()
     223             : {
     224           1 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     225           1 :   mTokenizer->end();
     226             : #ifdef DEBUG
     227             :   {
     228           2 :     mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
     229           1 :     MOZ_ASSERT(!mFlushTimer, "Flush timer was not dropped before dtor!");
     230             :   }
     231           1 :   mRequest = nullptr;
     232           1 :   mObserver = nullptr;
     233           1 :   mUnicodeDecoder = nullptr;
     234           1 :   mSniffingBuffer = nullptr;
     235           1 :   mMetaScanner = nullptr;
     236           1 :   mFirstBuffer = nullptr;
     237           1 :   mExecutor = nullptr;
     238           1 :   mTreeBuilder = nullptr;
     239           1 :   mTokenizer = nullptr;
     240           1 :   mOwner = nullptr;
     241             : #endif
     242           3 : }
     243             : 
     244             : nsresult
     245           4 : nsHtml5StreamParser::GetChannel(nsIChannel** aChannel)
     246             : {
     247           4 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     248           4 :   return mRequest ? CallQueryInterface(mRequest, aChannel) :
     249           8 :                     NS_ERROR_NOT_AVAILABLE;
     250             : }
     251             : 
     252             : NS_IMETHODIMP
     253           0 : nsHtml5StreamParser::Notify(const char* aCharset, nsDetectionConfident aConf)
     254             : {
     255           0 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     256           0 :   if (aConf == eBestAnswer || aConf == eSureAnswer) {
     257           0 :     mFeedChardet = false; // just in case
     258           0 :     auto encoding = Encoding::ForLabelNoReplacement(
     259           0 :         nsDependentCString(aCharset));
     260           0 :     if (!encoding) {
     261           0 :       return NS_OK;
     262             :     }
     263           0 :     if (HasDecoder()) {
     264           0 :       if (mEncoding == encoding) {
     265           0 :         NS_ASSERTION(mCharsetSource < kCharsetFromAutoDetection,
     266             :             "Why are we running chardet at all?");
     267           0 :         mCharsetSource = kCharsetFromAutoDetection;
     268           0 :         mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     269             :       } else {
     270             :         // We've already committed to a decoder. Request a reload from the
     271             :         // docshell.
     272           0 :         mTreeBuilder->NeedsCharsetSwitchTo(WrapNotNull(encoding),
     273             :                                            kCharsetFromAutoDetection,
     274           0 :                                            0);
     275           0 :         FlushTreeOpsAndDisarmTimer();
     276           0 :         Interrupt();
     277             :       }
     278             :     } else {
     279             :       // Got a confident answer from the sniffing buffer. That code will
     280             :       // take care of setting up the decoder.
     281           0 :       mEncoding = WrapNotNull(encoding);
     282           0 :       mCharsetSource = kCharsetFromAutoDetection;
     283           0 :       mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     284             :     }
     285             :   }
     286           0 :   return NS_OK;
     287             : }
     288             : 
     289             : void
     290           2 : nsHtml5StreamParser::SetViewSourceTitle(nsIURI* aURL)
     291             : {
     292           2 :   if (aURL) {
     293           4 :     nsCOMPtr<nsIURI> temp;
     294             :     bool isViewSource;
     295           2 :     aURL->SchemeIs("view-source", &isViewSource);
     296           2 :     if (isViewSource) {
     297           0 :       nsCOMPtr<nsINestedURI> nested = do_QueryInterface(aURL);
     298           0 :       nested->GetInnerURI(getter_AddRefs(temp));
     299             :     } else {
     300           2 :       temp = aURL;
     301             :     }
     302             :     bool isData;
     303           2 :     temp->SchemeIs("data", &isData);
     304           2 :     if (isData) {
     305             :       // Avoid showing potentially huge data: URLs. The three last bytes are
     306             :       // UTF-8 for an ellipsis.
     307           0 :       mViewSourceTitle.AssignLiteral("data:\xE2\x80\xA6");
     308             :     } else {
     309           2 :       nsresult rv = temp->GetSpec(mViewSourceTitle);
     310           2 :       if (NS_FAILED(rv)) {
     311           0 :         mViewSourceTitle.AssignLiteral("\xE2\x80\xA6");
     312             :       }
     313             :     }
     314             :   }
     315           2 : }
     316             : 
     317             : nsresult
     318           2 : nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(const uint8_t* aFromSegment, // can be null
     319             :                                                                           uint32_t aCount,
     320             :                                                                           uint32_t* aWriteCount)
     321             : {
     322           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     323           2 :   nsresult rv = NS_OK;
     324           2 :   mUnicodeDecoder = mEncoding->NewDecoderWithBOMRemoval();
     325           2 :   if (mSniffingBuffer) {
     326             :     uint32_t writeCount;
     327           0 :     rv = WriteStreamBytes(mSniffingBuffer.get(), mSniffingLength, &writeCount);
     328           0 :     NS_ENSURE_SUCCESS(rv, rv);
     329           0 :     mSniffingBuffer = nullptr;
     330             :   }
     331           2 :   mMetaScanner = nullptr;
     332           2 :   if (aFromSegment) {
     333           2 :     rv = WriteStreamBytes(aFromSegment, aCount, aWriteCount);
     334             :   }
     335           2 :   return rv;
     336             : }
     337             : 
     338             : nsresult
     339           0 : nsHtml5StreamParser::SetupDecodingFromBom(NotNull<const Encoding*> aEncoding)
     340             : {
     341           0 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     342           0 :   mEncoding = aEncoding;
     343           0 :   mUnicodeDecoder = mEncoding->NewDecoderWithBOMRemoval();
     344           0 :   mCharsetSource = kCharsetFromByteOrderMark;
     345           0 :   mFeedChardet = false;
     346           0 :   mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     347           0 :   mSniffingBuffer = nullptr;
     348           0 :   mMetaScanner = nullptr;
     349           0 :   mBomState = BOM_SNIFFING_OVER;
     350           0 :   return NS_OK;
     351             : }
     352             : 
     353             : void
     354           0 : nsHtml5StreamParser::SniffBOMlessUTF16BasicLatin(const uint8_t* aFromSegment,
     355             :                                                  uint32_t aCountToSniffingLimit)
     356             : {
     357             :   // Avoid underspecified heuristic craziness for XHR
     358           0 :   if (mMode == LOAD_AS_DATA) {
     359           0 :     return;
     360             :   }
     361             :   // Make sure there's enough data. Require room for "<title></title>"
     362           0 :   if (mSniffingLength + aCountToSniffingLimit < 30) {
     363           0 :     return;
     364             :   }
     365             :   // even-numbered bytes tracked at 0, odd-numbered bytes tracked at 1
     366           0 :   bool byteZero[2] = { false, false };
     367           0 :   bool byteNonZero[2] = { false, false };
     368           0 :   uint32_t i = 0;
     369           0 :   if (mSniffingBuffer) {
     370           0 :     for (; i < mSniffingLength; ++i) {
     371           0 :       if (mSniffingBuffer[i]) {
     372           0 :         if (byteNonZero[1 - (i % 2)]) {
     373           0 :           return;
     374             :         }
     375           0 :         byteNonZero[i % 2] = true;
     376             :       } else {
     377           0 :         if (byteZero[1 - (i % 2)]) {
     378           0 :           return;
     379             :         }
     380           0 :         byteZero[i % 2] = true;
     381             :       }
     382             :     }
     383             :   }
     384           0 :   if (aFromSegment) {
     385           0 :     for (uint32_t j = 0; j < aCountToSniffingLimit; ++j) {
     386           0 :       if (aFromSegment[j]) {
     387           0 :         if (byteNonZero[1 - ((i + j) % 2)]) {
     388           0 :           return;
     389             :         }
     390           0 :         byteNonZero[(i + j) % 2] = true;
     391             :       } else {
     392           0 :         if (byteZero[1 - ((i + j) % 2)]) {
     393           0 :           return;
     394             :         }
     395           0 :         byteZero[(i + j) % 2] = true;
     396             :       }
     397             :     }
     398             :   }
     399             : 
     400           0 :   if (byteNonZero[0]) {
     401           0 :     mEncoding = UTF_16LE_ENCODING;
     402             :   } else {
     403           0 :     mEncoding = UTF_16BE_ENCODING;
     404             :   }
     405           0 :   mCharsetSource = kCharsetFromIrreversibleAutoDetection;
     406           0 :   mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     407           0 :   mFeedChardet = false;
     408           0 :   mTreeBuilder->MaybeComplainAboutCharset("EncBomlessUtf16",
     409             :                                           true,
     410           0 :                                           0);
     411             : 
     412             : }
     413             : 
     414             : void
     415           0 : nsHtml5StreamParser::SetEncodingFromExpat(const char16_t* aEncoding)
     416             : {
     417           0 :   if (aEncoding) {
     418           0 :     nsDependentString utf16(aEncoding);
     419           0 :     nsAutoCString utf8;
     420           0 :     CopyUTF16toUTF8(utf16, utf8);
     421           0 :     auto encoding = PreferredForInternalEncodingDecl(utf8);
     422           0 :     if (encoding) {
     423           0 :       mEncoding = WrapNotNull(encoding);
     424           0 :       mCharsetSource = kCharsetFromMetaTag; // closest for XML
     425           0 :       return;
     426             :     }
     427             :     // else the page declared an encoding Gecko doesn't support and we'd
     428             :     // end up defaulting to UTF-8 anyway. Might as well fall through here
     429             :     // right away and let the encoding be set to UTF-8 which we'd default to
     430             :     // anyway.
     431             :   }
     432           0 :   mEncoding = UTF_8_ENCODING; // XML defaults to UTF-8 without a BOM
     433           0 :   mCharsetSource = kCharsetFromMetaTag; // means confident
     434             : }
     435             : 
     436             : // A separate user data struct is used instead of passing the
     437             : // nsHtml5StreamParser instance as user data in order to avoid including
     438             : // expat.h in nsHtml5StreamParser.h. Doing that would cause naming conflicts.
     439             : // Using a separate user data struct also avoids bloating nsHtml5StreamParser
     440             : // by one pointer.
     441             : struct UserData {
     442             :   XML_Parser mExpat;
     443             :   nsHtml5StreamParser* mStreamParser;
     444             : };
     445             : 
     446             : // Using no-namespace handler callbacks to avoid including expat.h in
     447             : // nsHtml5StreamParser.h, since doing so would cause naming conclicts.
     448             : static void
     449           0 : HandleXMLDeclaration(void* aUserData,
     450             :                      const XML_Char* aVersion,
     451             :                      const XML_Char* aEncoding,
     452             :                      int aStandalone)
     453             : {
     454           0 :   UserData* ud = static_cast<UserData*>(aUserData);
     455           0 :   ud->mStreamParser->SetEncodingFromExpat(
     456           0 :       reinterpret_cast<const char16_t*>(aEncoding));
     457           0 :   XML_StopParser(ud->mExpat, false);
     458           0 : }
     459             : 
     460             : static void
     461           0 : HandleStartElement(void* aUserData,
     462             :                    const XML_Char* aName,
     463             :                    const XML_Char **aAtts)
     464             : {
     465           0 :   UserData* ud = static_cast<UserData*>(aUserData);
     466           0 :   XML_StopParser(ud->mExpat, false);
     467           0 : }
     468             : 
     469             : static void
     470           0 : HandleEndElement(void* aUserData,
     471             :                  const XML_Char* aName)
     472             : {
     473           0 :   UserData* ud = static_cast<UserData*>(aUserData);
     474           0 :   XML_StopParser(ud->mExpat, false);
     475           0 : }
     476             : 
     477             : static void
     478           0 : HandleComment(void* aUserData,
     479             :               const XML_Char* aName)
     480             : {
     481           0 :   UserData* ud = static_cast<UserData*>(aUserData);
     482           0 :   XML_StopParser(ud->mExpat, false);
     483           0 : }
     484             : 
     485             : static void
     486           0 : HandleProcessingInstruction(void* aUserData,
     487             :                             const XML_Char* aTarget,
     488             :                             const XML_Char* aData)
     489             : {
     490           0 :   UserData* ud = static_cast<UserData*>(aUserData);
     491           0 :   XML_StopParser(ud->mExpat, false);
     492           0 : }
     493             : 
     494             : nsresult
     495           0 : nsHtml5StreamParser::FinalizeSniffing(const uint8_t* aFromSegment, // can be null
     496             :                                       uint32_t aCount,
     497             :                                       uint32_t* aWriteCount,
     498             :                                       uint32_t aCountToSniffingLimit)
     499             : {
     500           0 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     501           0 :   NS_ASSERTION(mCharsetSource < kCharsetFromParentForced,
     502             :       "Should not finalize sniffing when using forced charset.");
     503           0 :   if (mMode == VIEW_SOURCE_XML) {
     504             :     static const XML_Memory_Handling_Suite memsuite =
     505             :       {
     506             :         (void *(*)(size_t))moz_xmalloc,
     507             :         (void *(*)(void *, size_t))moz_xrealloc,
     508             :         free
     509             :       };
     510             : 
     511             :     static const char16_t kExpatSeparator[] = { 0xFFFF, '\0' };
     512             : 
     513             :     static const char16_t kISO88591[] =
     514             :         { 'I', 'S', 'O', '-', '8', '8', '5', '9', '-', '1', '\0' };
     515             : 
     516             :     UserData ud;
     517           0 :     ud.mStreamParser = this;
     518             : 
     519             :     // If we got this far, the stream didn't have a BOM. UTF-16-encoded XML
     520             :     // documents MUST begin with a BOM. We don't support EBCDIC and such.
     521             :     // Thus, at this point, what we have is garbage or something encoded using
     522             :     // a rough ASCII superset. ISO-8859-1 allows us to decode ASCII bytes
     523             :     // without throwing errors when bytes have the most significant bit set
     524             :     // and without triggering expat's unknown encoding code paths. This is
     525             :     // enough to be able to use expat to parse the XML declaration in order
     526             :     // to extract the encoding name from it.
     527           0 :     ud.mExpat = XML_ParserCreate_MM(kISO88591, &memsuite, kExpatSeparator);
     528           0 :     XML_SetXmlDeclHandler(ud.mExpat, HandleXMLDeclaration);
     529           0 :     XML_SetElementHandler(ud.mExpat, HandleStartElement, HandleEndElement);
     530           0 :     XML_SetCommentHandler(ud.mExpat, HandleComment);
     531           0 :     XML_SetProcessingInstructionHandler(ud.mExpat, HandleProcessingInstruction);
     532           0 :     XML_SetUserData(ud.mExpat, static_cast<void*>(&ud));
     533             : 
     534           0 :     XML_Status status = XML_STATUS_OK;
     535             : 
     536             :     // aFromSegment points to the data obtained from the current network
     537             :     // event. mSniffingBuffer (if it exists) contains the data obtained before
     538             :     // the current event. Thus, mSniffingLenth bytes of mSniffingBuffer
     539             :     // followed by aCountToSniffingLimit bytes from aFromSegment are the
     540             :     // first 1024 bytes of the file (or the file as a whole if the file is
     541             :     // 1024 bytes long or shorter). Thus, we parse both buffers, but if the
     542             :     // first call succeeds already, we skip parsing the second buffer.
     543           0 :     if (mSniffingBuffer) {
     544           0 :       status = XML_Parse(ud.mExpat,
     545           0 :                          reinterpret_cast<const char*>(mSniffingBuffer.get()),
     546           0 :                          mSniffingLength,
     547           0 :                          false);
     548             :     }
     549           0 :     if (status == XML_STATUS_OK &&
     550           0 :         mCharsetSource < kCharsetFromMetaTag &&
     551             :         aFromSegment) {
     552           0 :       status = XML_Parse(ud.mExpat,
     553             :                          reinterpret_cast<const char*>(aFromSegment),
     554             :                          aCountToSniffingLimit,
     555           0 :                          false);
     556             :     }
     557           0 :     XML_ParserFree(ud.mExpat);
     558             : 
     559           0 :     if (mCharsetSource < kCharsetFromMetaTag) {
     560             :       // Failed to get an encoding from the XML declaration. XML defaults
     561             :       // confidently to UTF-8 in this case.
     562             :       // It is also possible that the document has an XML declaration that is
     563             :       // longer than 1024 bytes, but that case is not worth worrying about.
     564           0 :       mEncoding = UTF_8_ENCODING;
     565           0 :       mCharsetSource = kCharsetFromMetaTag; // means confident
     566             :     }
     567             : 
     568             :     return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment,
     569             :                                                                 aCount,
     570           0 :                                                                 aWriteCount);
     571             :   }
     572             : 
     573             :   // meta scan failed.
     574           0 :   if (mCharsetSource >= kCharsetFromHintPrevDoc) {
     575           0 :     mFeedChardet = false;
     576           0 :     return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
     577             :   }
     578             :   // Check for BOMless UTF-16 with Basic
     579             :   // Latin content for compat with IE. See bug 631751.
     580           0 :   SniffBOMlessUTF16BasicLatin(aFromSegment, aCountToSniffingLimit);
     581             :   // the charset may have been set now
     582             :   // maybe try chardet now;
     583           0 :   if (mFeedChardet) {
     584             :     bool dontFeed;
     585             :     nsresult rv;
     586           0 :     if (mSniffingBuffer) {
     587           0 :       rv = mChardet->DoIt((const char*)mSniffingBuffer.get(), mSniffingLength, &dontFeed);
     588           0 :       mFeedChardet = !dontFeed;
     589           0 :       NS_ENSURE_SUCCESS(rv, rv);
     590             :     }
     591           0 :     if (mFeedChardet && aFromSegment) {
     592           0 :       rv = mChardet->DoIt((const char*)aFromSegment,
     593             :                           // Avoid buffer boundary-dependent behavior when
     594             :                           // reparsing is forbidden. If reparse is forbidden,
     595             :                           // act as if we only saw the first 1024 bytes.
     596             :                           // When reparsing isn't forbidden, buffer boundaries
     597             :                           // can have an effect on whether the page is loaded
     598             :                           // once or twice. :-(
     599           0 :                           mReparseForbidden ? aCountToSniffingLimit : aCount,
     600           0 :                           &dontFeed);
     601           0 :       mFeedChardet = !dontFeed;
     602           0 :       NS_ENSURE_SUCCESS(rv, rv);
     603             :     }
     604           0 :     if (mFeedChardet && (!aFromSegment || mReparseForbidden)) {
     605             :       // mReparseForbidden is checked so that we get to use the sniffing
     606             :       // buffer with the best guess so far if we aren't allowed to guess
     607             :       // better later.
     608           0 :       mFeedChardet = false;
     609           0 :       rv = mChardet->Done();
     610           0 :       NS_ENSURE_SUCCESS(rv, rv);
     611             :     }
     612             :     // fall thru; callback may have changed charset
     613             :   }
     614           0 :   if (mCharsetSource == kCharsetUninitialized) {
     615             :     // Hopefully this case is never needed, but dealing with it anyway
     616           0 :     mEncoding = WINDOWS_1252_ENCODING;
     617           0 :     mCharsetSource = kCharsetFromFallback;
     618           0 :     mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     619           0 :   } else if (mMode == LOAD_AS_DATA &&
     620           0 :              mCharsetSource == kCharsetFromFallback) {
     621           0 :     NS_ASSERTION(mReparseForbidden, "Reparse should be forbidden for XHR");
     622           0 :     NS_ASSERTION(!mFeedChardet, "Should not feed chardet for XHR");
     623           0 :     NS_ASSERTION(mEncoding == UTF_8_ENCODING,
     624             :                  "XHR should default to UTF-8");
     625             :     // Now mark charset source as non-weak to signal that we have a decision
     626           0 :     mCharsetSource = kCharsetFromDocTypeDefault;
     627           0 :     mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     628             :   }
     629           0 :   return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
     630             : }
     631             : 
     632             : nsresult
     633           2 : nsHtml5StreamParser::SniffStreamBytes(const uint8_t* aFromSegment,
     634             :                                       uint32_t aCount,
     635             :                                       uint32_t* aWriteCount)
     636             : {
     637           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     638           2 :   nsresult rv = NS_OK;
     639             :   uint32_t writeCount;
     640             : 
     641             :   // mEncoding and mCharsetSource potentially have come from channel or higher
     642             :   // by now. If we find a BOM, SetupDecodingFromBom() will overwrite them.
     643             :   // If we don't find a BOM, the previously set values of mEncoding and
     644             :   // mCharsetSource are not modified by the BOM sniffing here.
     645           4 :   for (uint32_t i = 0; i < aCount && mBomState != BOM_SNIFFING_OVER; i++) {
     646           2 :     switch (mBomState) {
     647             :       case BOM_SNIFFING_NOT_STARTED:
     648           2 :         NS_ASSERTION(i == 0, "Bad BOM sniffing state.");
     649           2 :         switch (*aFromSegment) {
     650             :           case 0xEF:
     651           0 :             mBomState = SEEN_UTF_8_FIRST_BYTE;
     652           0 :             break;
     653             :           case 0xFF:
     654           0 :             mBomState = SEEN_UTF_16_LE_FIRST_BYTE;
     655           0 :             break;
     656             :           case 0xFE:
     657           0 :             mBomState = SEEN_UTF_16_BE_FIRST_BYTE;
     658           0 :             break;
     659             :           default:
     660           2 :             mBomState = BOM_SNIFFING_OVER;
     661           2 :             break;
     662             :         }
     663           2 :         break;
     664             :       case SEEN_UTF_16_LE_FIRST_BYTE:
     665           0 :         if (aFromSegment[i] == 0xFE) {
     666           0 :           rv = SetupDecodingFromBom(UTF_16LE_ENCODING); // upper case is the raw form
     667           0 :           NS_ENSURE_SUCCESS(rv, rv);
     668           0 :           uint32_t count = aCount - (i + 1);
     669           0 :           rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
     670           0 :           NS_ENSURE_SUCCESS(rv, rv);
     671           0 :           *aWriteCount = writeCount + (i + 1);
     672           0 :           return rv;
     673             :         }
     674           0 :         mBomState = BOM_SNIFFING_OVER;
     675           0 :         break;
     676             :       case SEEN_UTF_16_BE_FIRST_BYTE:
     677           0 :         if (aFromSegment[i] == 0xFF) {
     678           0 :           rv = SetupDecodingFromBom(UTF_16BE_ENCODING); // upper case is the raw form
     679           0 :           NS_ENSURE_SUCCESS(rv, rv);
     680           0 :           uint32_t count = aCount - (i + 1);
     681           0 :           rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
     682           0 :           NS_ENSURE_SUCCESS(rv, rv);
     683           0 :           *aWriteCount = writeCount + (i + 1);
     684           0 :           return rv;
     685             :         }
     686           0 :         mBomState = BOM_SNIFFING_OVER;
     687           0 :         break;
     688             :       case SEEN_UTF_8_FIRST_BYTE:
     689           0 :         if (aFromSegment[i] == 0xBB) {
     690           0 :           mBomState = SEEN_UTF_8_SECOND_BYTE;
     691             :         } else {
     692           0 :           mBomState = BOM_SNIFFING_OVER;
     693             :         }
     694           0 :         break;
     695             :       case SEEN_UTF_8_SECOND_BYTE:
     696           0 :         if (aFromSegment[i] == 0xBF) {
     697           0 :           rv = SetupDecodingFromBom(UTF_8_ENCODING); // upper case is the raw form
     698           0 :           NS_ENSURE_SUCCESS(rv, rv);
     699           0 :           uint32_t count = aCount - (i + 1);
     700           0 :           rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
     701           0 :           NS_ENSURE_SUCCESS(rv, rv);
     702           0 :           *aWriteCount = writeCount + (i + 1);
     703           0 :           return rv;
     704             :         }
     705           0 :         mBomState = BOM_SNIFFING_OVER;
     706           0 :         break;
     707             :       default:
     708           0 :         mBomState = BOM_SNIFFING_OVER;
     709           0 :         break;
     710             :     }
     711             :   }
     712             :   // if we get here, there either was no BOM or the BOM sniffing isn't complete
     713             :   // yet
     714             : 
     715           2 :   MOZ_ASSERT(mCharsetSource != kCharsetFromByteOrderMark,
     716             :              "Should not come here if BOM was found.");
     717           2 :   MOZ_ASSERT(mCharsetSource != kCharsetFromOtherComponent,
     718             :              "kCharsetFromOtherComponent is for XSLT.");
     719             : 
     720           4 :   if (mBomState == BOM_SNIFFING_OVER &&
     721           2 :     mCharsetSource == kCharsetFromChannel) {
     722             :     // There was no BOM and the charset came from channel. mEncoding
     723             :     // still contains the charset from the channel as set by an
     724             :     // earlier call to SetDocumentCharset(), since we didn't find a BOM and
     725             :     // overwrite mEncoding. (Note that if the user has overridden the charset,
     726             :     // we don't come here but check <meta> for XSS-dangerous charsets first.)
     727           0 :     mFeedChardet = false;
     728           0 :     mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     729             :     return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment,
     730           0 :       aCount, aWriteCount);
     731             :   }
     732             : 
     733           4 :   if (!mMetaScanner && (mMode == NORMAL ||
     734           0 :                         mMode == VIEW_SOURCE_HTML ||
     735           0 :                         mMode == LOAD_AS_DATA)) {
     736           4 :     mMetaScanner = new nsHtml5MetaScanner(mTreeBuilder);
     737             :   }
     738             : 
     739           2 :   if (mSniffingLength + aCount >= NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE) {
     740             :     // this is the last buffer
     741             :     uint32_t countToSniffingLimit =
     742           1 :         NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE - mSniffingLength;
     743           1 :     if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
     744             :       nsHtml5ByteReadable readable(aFromSegment, aFromSegment +
     745           1 :           countToSniffingLimit);
     746           1 :       nsAutoCString charset;
     747           1 :       auto encoding = mMetaScanner->sniff(&readable);
     748             :       // Due to the way nsHtml5Portability reports OOM, ask the tree buider
     749             :       nsresult rv;
     750           1 :       if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
     751           0 :         MarkAsBroken(rv);
     752           0 :         return rv;
     753             :       }
     754           1 :       if (encoding) {
     755             :         // meta scan successful; honor overrides unless meta is XSS-dangerous
     756           3 :         if ((mCharsetSource == kCharsetFromParentForced ||
     757           1 :              mCharsetSource == kCharsetFromUserForced) &&
     758           0 :             (encoding->IsAsciiCompatible() ||
     759           0 :              encoding == ISO_2022_JP_ENCODING)) {
     760             :           // Honor override
     761             :           return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
     762           0 :             aFromSegment, aCount, aWriteCount);
     763             :         }
     764           1 :         mEncoding = WrapNotNull(encoding);
     765           1 :         mCharsetSource = kCharsetFromMetaPrescan;
     766           1 :         mFeedChardet = false;
     767           1 :         mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     768             :         return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
     769           1 :           aFromSegment, aCount, aWriteCount);
     770             :       }
     771             :     }
     772           0 :     if (mCharsetSource == kCharsetFromParentForced ||
     773           0 :         mCharsetSource == kCharsetFromUserForced) {
     774             :       // meta not found, honor override
     775             :       return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
     776           0 :         aFromSegment, aCount, aWriteCount);
     777             :     }
     778             :     return FinalizeSniffing(aFromSegment, aCount, aWriteCount,
     779           0 :         countToSniffingLimit);
     780             :   }
     781             : 
     782             :   // not the last buffer
     783           1 :   if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
     784           1 :     nsHtml5ByteReadable readable(aFromSegment, aFromSegment + aCount);
     785           1 :     auto encoding = mMetaScanner->sniff(&readable);
     786             :     // Due to the way nsHtml5Portability reports OOM, ask the tree buider
     787             :     nsresult rv;
     788           1 :     if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
     789           0 :       MarkAsBroken(rv);
     790           1 :       return rv;
     791             :     }
     792           1 :     if (encoding) {
     793             :       // meta scan successful; honor overrides unless meta is XSS-dangerous
     794           3 :       if ((mCharsetSource == kCharsetFromParentForced ||
     795           1 :            mCharsetSource == kCharsetFromUserForced) &&
     796           0 :           (encoding->IsAsciiCompatible() ||
     797           0 :            encoding == ISO_2022_JP_ENCODING)) {
     798             :         // Honor override
     799             :         return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment,
     800           0 :             aCount, aWriteCount);
     801             :       }
     802           1 :       mEncoding = WrapNotNull(encoding);
     803           1 :       mCharsetSource = kCharsetFromMetaPrescan;
     804           1 :       mFeedChardet = false;
     805           1 :       mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
     806             :       return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment,
     807           1 :         aCount, aWriteCount);
     808             :     }
     809             :   }
     810             : 
     811           0 :   if (!mSniffingBuffer) {
     812             :     mSniffingBuffer =
     813           0 :       MakeUniqueFallible<uint8_t[]>(NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE);
     814           0 :     if (!mSniffingBuffer) {
     815           0 :       return NS_ERROR_OUT_OF_MEMORY;
     816             :     }
     817             :   }
     818           0 :   memcpy(&mSniffingBuffer[mSniffingLength], aFromSegment, aCount);
     819           0 :   mSniffingLength += aCount;
     820           0 :   *aWriteCount = aCount;
     821           0 :   return NS_OK;
     822             : }
     823             : 
     824             : nsresult
     825           2 : nsHtml5StreamParser::WriteStreamBytes(const uint8_t* aFromSegment,
     826             :                                       uint32_t aCount,
     827             :                                       uint32_t* aWriteCount)
     828             : {
     829           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
     830             :   // mLastBuffer should always point to a buffer of the size
     831             :   // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
     832           2 :   if (!mLastBuffer) {
     833           0 :     NS_WARNING("mLastBuffer should not be null!");
     834           0 :     MarkAsBroken(NS_ERROR_NULL_POINTER);
     835           0 :     return NS_ERROR_NULL_POINTER;
     836             :   }
     837           2 :   size_t totalRead = 0;
     838           2 :   auto src = MakeSpan(aFromSegment, aCount);
     839             :   for (;;) {
     840           4 :     auto dst = mLastBuffer->TailAsSpan(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
     841             :     uint32_t result;
     842             :     size_t read;
     843             :     size_t written;
     844             :     bool hadErrors;
     845           8 :     Tie(result, read, written, hadErrors) =
     846          12 :       mUnicodeDecoder->DecodeToUTF16(src, dst, false);
     847             :     Unused << hadErrors;
     848           4 :     src = src.From(read);
     849           4 :     totalRead += read;
     850           4 :     mLastBuffer->AdvanceEnd(written);
     851           4 :     if (result == kOutputFull) {
     852             :       RefPtr<nsHtml5OwningUTF16Buffer> newBuf =
     853           4 :         nsHtml5OwningUTF16Buffer::FalliblyCreate(
     854           4 :           NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
     855           2 :       if (!newBuf) {
     856           0 :         MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
     857           0 :         return NS_ERROR_OUT_OF_MEMORY;
     858             :       }
     859           2 :       mLastBuffer = (mLastBuffer->next = newBuf.forget());
     860             :     } else {
     861           2 :       MOZ_ASSERT(totalRead == aCount,
     862             :                  "The Unicode decoder consumed the wrong number of bytes.");
     863           2 :       *aWriteCount = totalRead;
     864           2 :       return NS_OK;
     865             :     }
     866           2 :   }
     867             : }
     868             : 
     869             : nsresult
     870           2 : nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
     871             : {
     872           2 :   NS_PRECONDITION(STREAM_NOT_STARTED == mStreamState,
     873             :                   "Got OnStartRequest when the stream had already started.");
     874           2 :   NS_PRECONDITION(
     875             :     !mExecutor->HasStarted(),
     876             :     "Got OnStartRequest at the wrong stage in the executor life cycle.");
     877           2 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     878           2 :   if (mObserver) {
     879           0 :     mObserver->OnStartRequest(aRequest, aContext);
     880             :   }
     881           2 :   mRequest = aRequest;
     882             : 
     883           2 :   mStreamState = STREAM_BEING_READ;
     884             : 
     885           2 :   if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
     886           0 :     mTokenizer->StartViewSource(NS_ConvertUTF8toUTF16(mViewSourceTitle));
     887             :   }
     888             : 
     889             :   // For View Source, the parser should run with scripts "enabled" if a normal
     890             :   // load would have scripts enabled.
     891           4 :   bool scriptingEnabled = mMode == LOAD_AS_DATA ?
     892           4 :                                    false : mExecutor->IsScriptEnabled();
     893           2 :   mOwner->StartTokenizer(scriptingEnabled);
     894             : 
     895           2 :   bool isSrcdoc = false;
     896           4 :   nsCOMPtr<nsIChannel> channel;
     897           2 :   nsresult rv = GetChannel(getter_AddRefs(channel));
     898           2 :   if (NS_SUCCEEDED(rv)) {
     899           2 :     isSrcdoc = NS_IsSrcdocChannel(channel);
     900             :   }
     901           2 :   mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
     902           2 :   mTreeBuilder->setScriptingEnabled(scriptingEnabled);
     903           4 :   mTreeBuilder->SetPreventScriptExecution(!((mMode == NORMAL) &&
     904           4 :                                             scriptingEnabled));
     905           2 :   mTokenizer->start();
     906           2 :   mExecutor->Start();
     907           2 :   mExecutor->StartReadingFromStage();
     908             : 
     909           2 :   if (mMode == PLAIN_TEXT) {
     910           0 :     mTreeBuilder->StartPlainText();
     911           0 :     mTokenizer->StartPlainText();
     912           2 :   } else if (mMode == VIEW_SOURCE_PLAIN) {
     913           0 :     nsAutoString viewSourceTitle;
     914           0 :     CopyUTF8toUTF16(mViewSourceTitle, viewSourceTitle);
     915           0 :     mTreeBuilder->EnsureBufferSpace(viewSourceTitle.Length());
     916           0 :     mTreeBuilder->StartPlainTextViewSource(viewSourceTitle);
     917           0 :     mTokenizer->StartPlainText();
     918             :   }
     919             : 
     920             :   /*
     921             :    * If you move the following line, be very careful not to cause
     922             :    * WillBuildModel to be called before the document has had its
     923             :    * script global object set.
     924             :    */
     925           2 :   rv = mExecutor->WillBuildModel(eDTDMode_unknown);
     926           2 :   NS_ENSURE_SUCCESS(rv, rv);
     927             : 
     928             :   RefPtr<nsHtml5OwningUTF16Buffer> newBuf =
     929           4 :     nsHtml5OwningUTF16Buffer::FalliblyCreate(
     930           4 :       NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
     931           2 :   if (!newBuf) {
     932             :     // marks this stream parser as terminated,
     933             :     // which prevents entry to code paths that
     934             :     // would use mFirstBuffer or mLastBuffer.
     935           0 :     return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
     936             :   }
     937           2 :   NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?");
     938           2 :   NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?");
     939           2 :   mFirstBuffer = mLastBuffer = newBuf;
     940             : 
     941           2 :   rv = NS_OK;
     942             : 
     943             :   // The line below means that the encoding can end up being wrong if
     944             :   // a view-source URL is loaded without having the encoding hint from a
     945             :   // previous normal load in the history.
     946           2 :   mReparseForbidden = !(mMode == NORMAL || mMode == PLAIN_TEXT);
     947             : 
     948           2 :   mDocGroup = mExecutor->GetDocument()->GetDocGroup();
     949             : 
     950           4 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mRequest, &rv));
     951           2 :   if (NS_SUCCEEDED(rv)) {
     952             :     // Non-HTTP channels are bogus enough that we let them work with unlabeled
     953             :     // runnables for now. Asserting for HTTP channels only.
     954           1 :     MOZ_ASSERT(mDocGroup || mMode == LOAD_AS_DATA, "How come the doc group is still null?");
     955             : 
     956           2 :     nsAutoCString method;
     957           1 :     Unused << httpChannel->GetRequestMethod(method);
     958             :     // XXX does Necko have a way to renavigate POST, etc. without hitting
     959             :     // the network?
     960           1 :     if (!method.EqualsLiteral("GET")) {
     961             :       // This is the old Gecko behavior but the HTML5 spec disagrees.
     962             :       // Don't reparse on POST.
     963           0 :       mReparseForbidden = true;
     964           0 :       mFeedChardet = false; // can't restart anyway
     965             :     }
     966             :   }
     967             : 
     968             :   // Attempt to retarget delivery of data (via OnDataAvailable) to the parser
     969             :   // thread, rather than through the main thread.
     970             :   nsCOMPtr<nsIThreadRetargetableRequest> threadRetargetableRequest =
     971           4 :     do_QueryInterface(mRequest, &rv);
     972           2 :   if (threadRetargetableRequest) {
     973           2 :     rv = threadRetargetableRequest->RetargetDeliveryTo(mEventTarget);
     974             :   }
     975             : 
     976           2 :   if (NS_FAILED(rv)) {
     977           0 :     NS_WARNING("Failed to retarget HTML data delivery to the parser thread.");
     978             :   }
     979             : 
     980           2 :   if (mCharsetSource == kCharsetFromParentFrame) {
     981             :     // Remember this in case chardet overwrites mCharsetSource
     982           0 :     mInitialEncodingWasFromParentFrame = true;
     983             :   }
     984             : 
     985           2 :   if (mCharsetSource >= kCharsetFromAutoDetection) {
     986           0 :     mFeedChardet = false;
     987             :   }
     988             : 
     989           4 :   nsCOMPtr<nsIWyciwygChannel> wyciwygChannel(do_QueryInterface(mRequest));
     990           2 :   if (mCharsetSource < kCharsetFromUtf8OnlyMime && !wyciwygChannel) {
     991             :     // we aren't ready to commit to an encoding yet
     992             :     // leave converter uninstantiated for now
     993           2 :     return NS_OK;
     994             :   }
     995             : 
     996             :   // We are reloading a document.open()ed doc or loading JSON/WebVTT/etc. into
     997             :   // a browsing context. In the latter case, there's no need to remove the
     998             :   // BOM manually here, because the UTF-8 decoder removes it.
     999           0 :   mReparseForbidden = true;
    1000           0 :   mFeedChardet = false;
    1001             : 
    1002             :   // Instantiate the converter here to avoid BOM sniffing.
    1003           0 :   mUnicodeDecoder = mEncoding->NewDecoderWithBOMRemoval();
    1004           0 :   return NS_OK;
    1005             : }
    1006             : 
    1007             : nsresult
    1008           2 : nsHtml5StreamParser::CheckListenerChain()
    1009             : {
    1010           2 :   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
    1011           2 :   if (!mObserver) {
    1012           2 :     return NS_OK;
    1013             :   }
    1014             :   nsresult rv;
    1015             :   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
    1016           0 :     do_QueryInterface(mObserver, &rv);
    1017           0 :   if (NS_SUCCEEDED(rv) && retargetable) {
    1018           0 :     rv = retargetable->CheckListenerChain();
    1019             :   }
    1020           0 :   return rv;
    1021             : }
    1022             : 
    1023             : void
    1024           2 : nsHtml5StreamParser::DoStopRequest()
    1025             : {
    1026           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1027           2 :   NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
    1028             :                   "Stream ended without being open.");
    1029           2 :   mTokenizerMutex.AssertCurrentThreadOwns();
    1030             : 
    1031           2 :   if (IsTerminated()) {
    1032           0 :     return;
    1033             :   }
    1034             : 
    1035           2 :   mStreamState = STREAM_ENDED;
    1036             : 
    1037           2 :   if (!mUnicodeDecoder) {
    1038             :     uint32_t writeCount;
    1039             :     nsresult rv;
    1040           0 :     if (NS_FAILED(rv = FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
    1041           0 :       MarkAsBroken(rv);
    1042           0 :       return;
    1043             :     }
    1044           2 :   } else if (mFeedChardet) {
    1045           0 :     mChardet->Done();
    1046             :   }
    1047             : 
    1048           2 :   MOZ_ASSERT(mUnicodeDecoder, "Should have a decoder after finalizing sniffing.");
    1049             : 
    1050             :   // mLastBuffer should always point to a buffer of the size
    1051             :   // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
    1052           2 :   if (!mLastBuffer) {
    1053           0 :     NS_WARNING("mLastBuffer should not be null!");
    1054           0 :     MarkAsBroken(NS_ERROR_NULL_POINTER);
    1055           0 :     return;
    1056             :   }
    1057             : 
    1058           2 :   Span<uint8_t> src; // empty span
    1059             :   for (;;) {
    1060           2 :     auto dst = mLastBuffer->TailAsSpan(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
    1061             :     uint32_t result;
    1062             :     size_t read;
    1063             :     size_t written;
    1064             :     bool hadErrors;
    1065           4 :     Tie(result, read, written, hadErrors) =
    1066           6 :       mUnicodeDecoder->DecodeToUTF16(src, dst, true);
    1067             :     Unused << hadErrors;
    1068           2 :     MOZ_ASSERT(read == 0, "How come an empty span was read form?");
    1069           2 :     mLastBuffer->AdvanceEnd(written);
    1070           2 :     if (result == kOutputFull) {
    1071             :       RefPtr<nsHtml5OwningUTF16Buffer> newBuf =
    1072           0 :         nsHtml5OwningUTF16Buffer::FalliblyCreate(
    1073           0 :           NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
    1074           0 :       if (!newBuf) {
    1075           0 :         MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
    1076           0 :         return;
    1077             :       }
    1078           0 :       mLastBuffer = (mLastBuffer->next = newBuf.forget());
    1079             :     } else {
    1080           4 :       break;
    1081             :     }
    1082           0 :   }
    1083             : 
    1084             : 
    1085           2 :   if (IsTerminatedOrInterrupted()) {
    1086           0 :     return;
    1087             :   }
    1088             : 
    1089           2 :   ParseAvailableData();
    1090             : }
    1091             : 
    1092           6 : class nsHtml5RequestStopper : public Runnable
    1093             : {
    1094             :   private:
    1095             :     nsHtml5StreamParserPtr mStreamParser;
    1096             : 
    1097             :   public:
    1098           2 :     explicit nsHtml5RequestStopper(nsHtml5StreamParser* aStreamParser)
    1099           2 :       : Runnable("nsHtml5RequestStopper")
    1100           2 :       , mStreamParser(aStreamParser)
    1101           2 :     {}
    1102           2 :     NS_IMETHOD Run() override
    1103             :     {
    1104           4 :       mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
    1105           2 :       mStreamParser->DoStopRequest();
    1106           4 :       return NS_OK;
    1107             :     }
    1108             : };
    1109             : 
    1110             : nsresult
    1111           2 : nsHtml5StreamParser::OnStopRequest(nsIRequest* aRequest,
    1112             :                              nsISupports* aContext,
    1113             :                              nsresult status)
    1114             : {
    1115           2 :   NS_ASSERTION(mRequest == aRequest, "Got Stop on wrong stream.");
    1116           2 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1117           2 :   if (mObserver) {
    1118           0 :     mObserver->OnStopRequest(aRequest, aContext, status);
    1119             :   }
    1120           4 :   nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
    1121           2 :   if (NS_FAILED(mEventTarget->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
    1122           0 :     NS_WARNING("Dispatching StopRequest event failed.");
    1123             :   }
    1124           4 :   return NS_OK;
    1125             : }
    1126             : 
    1127             : void
    1128           2 : nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength)
    1129             : {
    1130           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1131           2 :   NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
    1132             :                   "DoDataAvailable called when stream not open.");
    1133           2 :   mTokenizerMutex.AssertCurrentThreadOwns();
    1134             : 
    1135           2 :   if (IsTerminated()) {
    1136           1 :     return;
    1137             :   }
    1138             : 
    1139             :   uint32_t writeCount;
    1140             :   nsresult rv;
    1141           2 :   if (HasDecoder()) {
    1142           0 :     if (mFeedChardet) {
    1143             :       bool dontFeed;
    1144           0 :       mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
    1145           0 :       mFeedChardet = !dontFeed;
    1146             :     }
    1147           0 :     rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
    1148             :   } else {
    1149           2 :     rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
    1150             :   }
    1151           2 :   if (NS_FAILED(rv)) {
    1152           0 :     MarkAsBroken(rv);
    1153           0 :     return;
    1154             :   }
    1155           2 :   NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
    1156             : 
    1157           2 :   if (IsTerminatedOrInterrupted()) {
    1158           0 :     return;
    1159             :   }
    1160             : 
    1161           2 :   ParseAvailableData();
    1162             : 
    1163           2 :   if (mFlushTimerArmed || mSpeculating) {
    1164           1 :     return;
    1165             :   }
    1166             : 
    1167             :   {
    1168           2 :     mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
    1169           3 :     mFlushTimer->InitWithNamedFuncCallback(
    1170             :       nsHtml5StreamParser::TimerCallback,
    1171             :       static_cast<void*>(this),
    1172           2 :       mFlushTimerEverFired ? sTimerInitialDelay : sTimerSubsequentDelay,
    1173             :       nsITimer::TYPE_ONE_SHOT,
    1174           2 :       "nsHtml5StreamParser::DoDataAvailable");
    1175             :   }
    1176           1 :   mFlushTimerArmed = true;
    1177             : }
    1178             : 
    1179           0 : class nsHtml5DataAvailable : public Runnable
    1180             : {
    1181             :   private:
    1182             :     nsHtml5StreamParserPtr mStreamParser;
    1183             :     UniquePtr<uint8_t[]> mData;
    1184             :     uint32_t mLength;
    1185             : 
    1186             :   public:
    1187           0 :     nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser,
    1188             :                          UniquePtr<uint8_t[]> aData,
    1189             :                          uint32_t             aLength)
    1190           0 :       : Runnable("nsHtml5DataAvailable")
    1191             :       , mStreamParser(aStreamParser)
    1192           0 :       , mData(Move(aData))
    1193           0 :       , mLength(aLength)
    1194           0 :     {}
    1195           0 :     NS_IMETHOD Run() override
    1196             :     {
    1197           0 :       mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
    1198           0 :       mStreamParser->DoDataAvailable(mData.get(), mLength);
    1199           0 :       return NS_OK;
    1200             :     }
    1201             : };
    1202             : 
    1203             : nsresult
    1204           2 : nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
    1205             :                                      nsISupports* aContext,
    1206             :                                      nsIInputStream* aInStream,
    1207             :                                      uint64_t aSourceOffset,
    1208             :                                      uint32_t aLength)
    1209             : {
    1210             :   nsresult rv;
    1211           2 :   if (NS_FAILED(rv = mExecutor->IsBroken())) {
    1212           0 :     return rv;
    1213             :   }
    1214             : 
    1215           2 :   NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
    1216             :   uint32_t totalRead;
    1217             :   // Main thread to parser thread dispatch requires copying to buffer first.
    1218           2 :   if (NS_IsMainThread()) {
    1219           0 :     auto data = MakeUniqueFallible<uint8_t[]>(aLength);
    1220           0 :     if (!data) {
    1221           0 :       return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
    1222             :     }
    1223           0 :     rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
    1224           0 :                          aLength, &totalRead);
    1225           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1226           0 :     NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
    1227             : 
    1228             :     nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
    1229           0 :                                                                    Move(data),
    1230           0 :                                                                    totalRead);
    1231           0 :     if (NS_FAILED(mEventTarget->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
    1232           0 :       NS_WARNING("Dispatching DataAvailable event failed.");
    1233             :     }
    1234           0 :     return rv;
    1235             :   } else {
    1236           2 :     NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1237           4 :     mozilla::MutexAutoLock autoLock(mTokenizerMutex);
    1238             : 
    1239             :     // Read directly from response buffer.
    1240             :     rv = aInStream->ReadSegments(CopySegmentsToParser, this, aLength,
    1241           2 :                                  &totalRead);
    1242           2 :     if (NS_FAILED(rv)) {
    1243           0 :       NS_WARNING("Failed reading response data to parser");
    1244           0 :       return rv;
    1245             :     }
    1246           2 :     return NS_OK;
    1247             :   }
    1248             : }
    1249             : 
    1250             : /* static */ nsresult
    1251           2 : nsHtml5StreamParser::CopySegmentsToParser(nsIInputStream *aInStream,
    1252             :                                           void *aClosure,
    1253             :                                           const char *aFromSegment,
    1254             :                                           uint32_t aToOffset,
    1255             :                                           uint32_t aCount,
    1256             :                                           uint32_t *aWriteCount)
    1257             : {
    1258           2 :   nsHtml5StreamParser* parser = static_cast<nsHtml5StreamParser*>(aClosure);
    1259             : 
    1260           2 :   parser->DoDataAvailable((const uint8_t*)aFromSegment, aCount);
    1261             :   // Assume DoDataAvailable consumed all available bytes.
    1262           2 :   *aWriteCount = aCount;
    1263           2 :   return NS_OK;
    1264             : }
    1265             : 
    1266             : const Encoding*
    1267           2 : nsHtml5StreamParser::PreferredForInternalEncodingDecl(const nsACString& aEncoding)
    1268             : {
    1269           2 :   const Encoding* newEncoding = Encoding::ForLabel(aEncoding);
    1270           2 :   if (!newEncoding) {
    1271             :     // the encoding name is bogus
    1272           0 :     mTreeBuilder->MaybeComplainAboutCharset("EncMetaUnsupported",
    1273             :                                             true,
    1274           0 :                                             mTokenizer->getLineNumber());
    1275           0 :     return nullptr;
    1276             :   }
    1277             : 
    1278           4 :   if (newEncoding == UTF_16BE_ENCODING ||
    1279           2 :       newEncoding == UTF_16LE_ENCODING) {
    1280           0 :     mTreeBuilder->MaybeComplainAboutCharset("EncMetaUtf16",
    1281             :                                             true,
    1282           0 :                                             mTokenizer->getLineNumber());
    1283           0 :     newEncoding = UTF_8_ENCODING;
    1284             :   }
    1285             : 
    1286           2 :   if (newEncoding == X_USER_DEFINED_ENCODING) {
    1287             :     // WebKit/Blink hack for Indian and Armenian legacy sites
    1288           0 :     mTreeBuilder->MaybeComplainAboutCharset("EncMetaUserDefined",
    1289             :                                             true,
    1290           0 :                                             mTokenizer->getLineNumber());
    1291           0 :     newEncoding = WINDOWS_1252_ENCODING;
    1292             :   }
    1293             : 
    1294           2 :   if (newEncoding == mEncoding) {
    1295           2 :     if (mCharsetSource < kCharsetFromMetaPrescan) {
    1296           0 :       if (mInitialEncodingWasFromParentFrame) {
    1297           0 :         mTreeBuilder->MaybeComplainAboutCharset("EncLateMetaFrame",
    1298             :                                                 false,
    1299           0 :                                                 mTokenizer->getLineNumber());
    1300             :       } else {
    1301           0 :         mTreeBuilder->MaybeComplainAboutCharset("EncLateMeta",
    1302             :                                                 false,
    1303           0 :                                                 mTokenizer->getLineNumber());
    1304             :       }
    1305             :     }
    1306           2 :     mCharsetSource = kCharsetFromMetaTag; // become confident
    1307           2 :     mFeedChardet = false; // don't feed chardet when confident
    1308           2 :     return nullptr;
    1309             :   }
    1310             : 
    1311           0 :   return newEncoding;
    1312             : }
    1313             : 
    1314             : bool
    1315           2 : nsHtml5StreamParser::internalEncodingDeclaration(nsHtml5String aEncoding)
    1316             : {
    1317             :   // This code needs to stay in sync with
    1318             :   // nsHtml5MetaScanner::tryCharset. Unfortunately, the
    1319             :   // trickery with member fields there leads to some copy-paste reuse. :-(
    1320           2 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1321           2 :   if (mCharsetSource >= kCharsetFromMetaTag) { // this threshold corresponds to "confident" in the HTML5 spec
    1322           0 :     return false;
    1323             :   }
    1324             : 
    1325           4 :   nsString newEncoding16; // Not Auto, because using it to hold nsStringBuffer*
    1326           2 :   aEncoding.ToString(newEncoding16);
    1327           4 :   nsAutoCString newEncoding;
    1328           2 :   CopyUTF16toUTF8(newEncoding16, newEncoding);
    1329             : 
    1330           2 :   auto encoding = PreferredForInternalEncodingDecl(newEncoding);
    1331           2 :   if (!encoding) {
    1332           2 :     return false;
    1333             :   }
    1334             : 
    1335           0 :   if (mReparseForbidden) {
    1336             :     // This mReparseForbidden check happens after the call to
    1337             :     // PreferredForInternalEncodingDecl so that if that method calls
    1338             :     // MaybeComplainAboutCharset, its charset complaint wins over the one
    1339             :     // below.
    1340           0 :     mTreeBuilder->MaybeComplainAboutCharset("EncLateMetaTooLate",
    1341             :                                             true,
    1342           0 :                                             mTokenizer->getLineNumber());
    1343           0 :     return false; // not reparsing even if we wanted to
    1344             :   }
    1345             : 
    1346             :   // Avoid having the chardet ask for another restart after this restart
    1347             :   // request.
    1348           0 :   mFeedChardet = false;
    1349           0 :   mTreeBuilder->NeedsCharsetSwitchTo(WrapNotNull(encoding),
    1350             :                                      kCharsetFromMetaTag,
    1351           0 :                                      mTokenizer->getLineNumber());
    1352           0 :   FlushTreeOpsAndDisarmTimer();
    1353           0 :   Interrupt();
    1354             :   // the tree op executor will cause the stream parser to terminate
    1355             :   // if the charset switch request is accepted or it'll uninterrupt
    1356             :   // if the request failed. Note that if the restart request fails,
    1357             :   // we don't bother trying to make chardet resume. Might as well
    1358             :   // assume that chardet-requested restarts would fail, too.
    1359           0 :   return true;
    1360             : }
    1361             : 
    1362             : void
    1363           7 : nsHtml5StreamParser::FlushTreeOpsAndDisarmTimer()
    1364             : {
    1365           7 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1366           7 :   if (mFlushTimerArmed) {
    1367             :     // avoid calling Cancel if the flush timer isn't armed to avoid acquiring
    1368             :     // a mutex
    1369             :     {
    1370           0 :       mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
    1371           0 :       mFlushTimer->Cancel();
    1372             :     }
    1373           0 :     mFlushTimerArmed = false;
    1374             :   }
    1375           7 :   if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
    1376           0 :     mTokenizer->FlushViewSource();
    1377             :   }
    1378           7 :   mTreeBuilder->Flush();
    1379          14 :   nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
    1380           7 :   if (NS_FAILED(DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
    1381           0 :     NS_WARNING("failed to dispatch executor flush event");
    1382             :   }
    1383           7 : }
    1384             : 
    1385             : void
    1386           5 : nsHtml5StreamParser::ParseAvailableData()
    1387             : {
    1388           5 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1389           5 :   mTokenizerMutex.AssertCurrentThreadOwns();
    1390             : 
    1391           5 :   if (IsTerminatedOrInterrupted()) {
    1392           0 :     return;
    1393             :   }
    1394             : 
    1395           5 :   if (mSpeculating && !IsSpeculationEnabled()) {
    1396           0 :     return;
    1397             :   }
    1398             : 
    1399             :   for (;;) {
    1400          16 :     if (!mFirstBuffer->hasMore()) {
    1401           7 :       if (mFirstBuffer == mLastBuffer) {
    1402           5 :         switch (mStreamState) {
    1403             :           case STREAM_BEING_READ:
    1404             :             // never release the last buffer.
    1405           2 :             if (!mSpeculating) {
    1406             :               // reuse buffer space if not speculating
    1407           1 :               mFirstBuffer->setStart(0);
    1408           1 :               mFirstBuffer->setEnd(0);
    1409             :             }
    1410           2 :             mTreeBuilder->FlushLoads();
    1411             :             {
    1412             :               // Dispatch this runnable unconditionally, because the loads
    1413             :               // that need flushing may have been flushed earlier even if the
    1414             :               // flush right above here did nothing.
    1415           4 :               nsCOMPtr<nsIRunnable> runnable(mLoadFlusher);
    1416           2 :               if (NS_FAILED(
    1417             :                     DispatchToMain("nsHtml5LoadFlusher", runnable.forget()))) {
    1418           0 :                 NS_WARNING("failed to dispatch load flush event");
    1419             :               }
    1420             :             }
    1421           2 :             return; // no more data for now but expecting more
    1422             :           case STREAM_ENDED:
    1423           3 :             if (mAtEOF) {
    1424           1 :               return;
    1425             :             }
    1426           2 :             mAtEOF = true;
    1427           2 :             if (mCharsetSource < kCharsetFromMetaTag) {
    1428           0 :               if (mInitialEncodingWasFromParentFrame) {
    1429             :                 // Unfortunately, this check doesn't take effect for
    1430             :                 // cross-origin frames, so cross-origin ad frames that have
    1431             :                 // no text and only an image or a Flash embed get the more
    1432             :                 // severe message from the next if block. The message is
    1433             :                 // technically accurate, though.
    1434           0 :                 mTreeBuilder->MaybeComplainAboutCharset("EncNoDeclarationFrame",
    1435             :                                                         false,
    1436           0 :                                                         0);
    1437           0 :               } else if (mMode == NORMAL) {
    1438           0 :                 mTreeBuilder->MaybeComplainAboutCharset("EncNoDeclaration",
    1439             :                                                         true,
    1440           0 :                                                         0);
    1441           0 :               } else if (mMode == PLAIN_TEXT) {
    1442           0 :                 mTreeBuilder->MaybeComplainAboutCharset("EncNoDeclarationPlain",
    1443             :                                                         true,
    1444           0 :                                                         0);
    1445             :               }
    1446             :             }
    1447           2 :             if (NS_SUCCEEDED(mTreeBuilder->IsBroken())) {
    1448           2 :               mTokenizer->eof();
    1449             :               nsresult rv;
    1450           2 :               if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
    1451           0 :                 MarkAsBroken(rv);
    1452             :               } else {
    1453           2 :                 mTreeBuilder->StreamEnded();
    1454           2 :                 if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
    1455           0 :                   mTokenizer->EndViewSource();
    1456             :                 }
    1457             :               }
    1458             :             }
    1459           2 :             FlushTreeOpsAndDisarmTimer();
    1460           2 :             return; // no more data and not expecting more
    1461             :           default:
    1462           0 :             NS_NOTREACHED("It should be impossible to reach this.");
    1463           0 :             return;
    1464             :         }
    1465             :       }
    1466           2 :       mFirstBuffer = mFirstBuffer->next;
    1467           2 :       continue;
    1468             :     }
    1469             : 
    1470             :     // now we have a non-empty buffer
    1471           9 :     mFirstBuffer->adjust(mLastWasCR);
    1472           9 :     mLastWasCR = false;
    1473           9 :     if (mFirstBuffer->hasMore()) {
    1474           9 :       if (!mTokenizer->EnsureBufferSpace(mFirstBuffer->getLength())) {
    1475           0 :         MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
    1476           0 :         return;
    1477             :       }
    1478           9 :       mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
    1479             :       nsresult rv;
    1480           9 :       if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
    1481           0 :         MarkAsBroken(rv);
    1482           0 :         return;
    1483             :       }
    1484             :       // At this point, internalEncodingDeclaration() may have called
    1485             :       // Terminate, but that never happens together with script.
    1486             :       // Can't assert that here, though, because it's possible that the main
    1487             :       // thread has called Terminate() while this thread was parsing.
    1488           9 :       if (mTreeBuilder->HasScript()) {
    1489             :         // HasScript() cannot return true if the tree builder is preventing
    1490             :         // script execution.
    1491           5 :         MOZ_ASSERT(mMode == NORMAL);
    1492          10 :         mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
    1493             :         nsHtml5Speculation* speculation =
    1494             :           new nsHtml5Speculation(mFirstBuffer,
    1495           5 :                                  mFirstBuffer->getStart(),
    1496           5 :                                  mTokenizer->getLineNumber(),
    1497          15 :                                  mTreeBuilder->newSnapshot());
    1498           5 :         mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot(),
    1499           5 :                                           speculation->GetStartLineNumber());
    1500           5 :         FlushTreeOpsAndDisarmTimer();
    1501           5 :         mTreeBuilder->SetOpSink(speculation);
    1502           5 :         mSpeculations.AppendElement(speculation); // adopts the pointer
    1503           5 :         mSpeculating = true;
    1504             :       }
    1505           9 :       if (IsTerminatedOrInterrupted()) {
    1506           0 :         return;
    1507             :       }
    1508             :     }
    1509           9 :     continue;
    1510          11 :   }
    1511             : }
    1512             : 
    1513           3 : class nsHtml5StreamParserContinuation : public Runnable
    1514             : {
    1515             : private:
    1516             :   nsHtml5StreamParserPtr mStreamParser;
    1517             : 
    1518             : public:
    1519           1 :   explicit nsHtml5StreamParserContinuation(nsHtml5StreamParser* aStreamParser)
    1520           1 :     : Runnable("nsHtml5StreamParserContinuation")
    1521           1 :     , mStreamParser(aStreamParser)
    1522           1 :   {}
    1523           1 :   NS_IMETHOD Run() override
    1524             :   {
    1525           2 :     mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
    1526           1 :     mStreamParser->Uninterrupt();
    1527           1 :     mStreamParser->ParseAvailableData();
    1528           2 :     return NS_OK;
    1529             :   }
    1530             : };
    1531             : 
    1532             : void
    1533           5 : nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
    1534             :                                           nsHtml5TreeBuilder* aTreeBuilder,
    1535             :                                           bool aLastWasCR)
    1536             : {
    1537           5 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1538           5 :   NS_ASSERTION(!(mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML),
    1539             :       "ContinueAfterScripts called in view source mode!");
    1540           5 :   if (NS_FAILED(mExecutor->IsBroken())) {
    1541           0 :     return;
    1542             :   }
    1543             :   #ifdef DEBUG
    1544           5 :     mExecutor->AssertStageEmpty();
    1545             :   #endif
    1546           5 :   bool speculationFailed = false;
    1547             :   {
    1548           6 :     mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
    1549           5 :     if (mSpeculations.IsEmpty()) {
    1550           0 :       NS_NOTREACHED("ContinueAfterScripts called without speculations.");
    1551           0 :       return;
    1552             :     }
    1553           5 :     nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
    1554          10 :     if (aLastWasCR || !aTokenizer->isInDataState() ||
    1555           5 :         !aTreeBuilder->snapshotMatches(speculation->GetSnapshot())) {
    1556           0 :       speculationFailed = true;
    1557             :       // We've got a failed speculation :-(
    1558           0 :       MaybeDisableFutureSpeculation();
    1559           0 :       Interrupt(); // Make the parser thread release the tokenizer mutex sooner
    1560             :       // now fall out of the speculationAutoLock into the tokenizerAutoLock block
    1561             :     } else {
    1562             :       // We've got a successful speculation!
    1563           5 :       if (mSpeculations.Length() > 1) {
    1564             :         // the first speculation isn't the current speculation, so there's
    1565             :         // no need to bother the parser thread.
    1566           4 :         speculation->FlushToSink(mExecutor);
    1567           4 :         NS_ASSERTION(!mExecutor->IsScriptExecuting(),
    1568             :           "ParseUntilBlocked() was supposed to ensure we don't come "
    1569             :           "here when scripts are executing.");
    1570           4 :         NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
    1571             :           "RunFlushLoop() didn't call ParseUntilBlocked() which is the "
    1572             :           "only caller of this method?");
    1573           4 :         mSpeculations.RemoveElementAt(0);
    1574           4 :         return;
    1575             :       }
    1576             :       // else
    1577           1 :       Interrupt(); // Make the parser thread release the tokenizer mutex sooner
    1578             : 
    1579             :       // now fall through
    1580             :       // the first speculation is the current speculation. Need to
    1581             :       // release the the speculation mutex and acquire the tokenizer
    1582             :       // mutex. (Just acquiring the other mutex here would deadlock)
    1583             :     }
    1584             :   }
    1585             :   {
    1586           2 :     mozilla::MutexAutoLock tokenizerAutoLock(mTokenizerMutex);
    1587             :     #ifdef DEBUG
    1588             :     {
    1589           1 :       mAtomTable.SetPermittedLookupEventTarget(GetMainThreadSerialEventTarget());
    1590             :     }
    1591             :     #endif
    1592             :     // In principle, the speculation mutex should be acquired here,
    1593             :     // but there's no point, because the parser thread only acquires it
    1594             :     // when it has also acquired the tokenizer mutex and we are already
    1595             :     // holding the tokenizer mutex.
    1596           1 :     if (speculationFailed) {
    1597             :       // Rewind the stream
    1598           0 :       mAtEOF = false;
    1599           0 :       nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
    1600           0 :       mFirstBuffer = speculation->GetBuffer();
    1601           0 :       mFirstBuffer->setStart(speculation->GetStart());
    1602           0 :       mTokenizer->setLineNumber(speculation->GetStartLineNumber());
    1603             : 
    1604           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    1605           0 :                                       NS_LITERAL_CSTRING("DOM Events"),
    1606           0 :                                       mExecutor->GetDocument(),
    1607             :                                       nsContentUtils::eDOM_PROPERTIES,
    1608             :                                       "SpeculationFailed",
    1609             :                                       nullptr, 0,
    1610             :                                       nullptr,
    1611             :                                       EmptyString(),
    1612           0 :                                       speculation->GetStartLineNumber());
    1613             : 
    1614           0 :       nsHtml5OwningUTF16Buffer* buffer = mFirstBuffer->next;
    1615           0 :       while (buffer) {
    1616           0 :         buffer->setStart(0);
    1617           0 :         buffer = buffer->next;
    1618             :       }
    1619             : 
    1620           0 :       mSpeculations.Clear(); // potentially a huge number of destructors
    1621             :                              // run here synchronously on the main thread...
    1622             : 
    1623           0 :       mTreeBuilder->flushCharacters(); // empty the pending buffer
    1624           0 :       mTreeBuilder->ClearOps(); // now get rid of the failed ops
    1625             : 
    1626           0 :       mTreeBuilder->SetOpSink(mExecutor->GetStage());
    1627           0 :       mExecutor->StartReadingFromStage();
    1628           0 :       mSpeculating = false;
    1629             : 
    1630             :       // Copy state over
    1631           0 :       mLastWasCR = aLastWasCR;
    1632           0 :       mTokenizer->loadState(aTokenizer);
    1633           0 :       mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
    1634             :     } else {
    1635             :       // We've got a successful speculation and at least a moment ago it was
    1636             :       // the current speculation
    1637           1 :       mSpeculations.ElementAt(0)->FlushToSink(mExecutor);
    1638           1 :       NS_ASSERTION(!mExecutor->IsScriptExecuting(),
    1639             :         "ParseUntilBlocked() was supposed to ensure we don't come "
    1640             :         "here when scripts are executing.");
    1641           1 :       NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
    1642             :         "RunFlushLoop() didn't call ParseUntilBlocked() which is the "
    1643             :         "only caller of this method?");
    1644           1 :       mSpeculations.RemoveElementAt(0);
    1645           1 :       if (mSpeculations.IsEmpty()) {
    1646             :         // yes, it was still the only speculation. Now stop speculating
    1647             :         // However, before telling the executor to read from stage, flush
    1648             :         // any pending ops straight to the executor, because otherwise
    1649             :         // they remain unflushed until we get more data from the network.
    1650           1 :         mTreeBuilder->SetOpSink(mExecutor);
    1651           1 :         mTreeBuilder->Flush(true);
    1652           1 :         mTreeBuilder->SetOpSink(mExecutor->GetStage());
    1653           1 :         mExecutor->StartReadingFromStage();
    1654           1 :         mSpeculating = false;
    1655             :       }
    1656             :     }
    1657           2 :     nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
    1658           1 :     if (NS_FAILED(mEventTarget->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
    1659           0 :       NS_WARNING("Failed to dispatch nsHtml5StreamParserContinuation");
    1660             :     }
    1661             :     // A stream event might run before this event runs, but that's harmless.
    1662             :     #ifdef DEBUG
    1663           1 :       mAtomTable.SetPermittedLookupEventTarget(mEventTarget);
    1664             :     #endif
    1665             :   }
    1666             : }
    1667             : 
    1668             : void
    1669           0 : nsHtml5StreamParser::ContinueAfterFailedCharsetSwitch()
    1670             : {
    1671           0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1672           0 :   nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
    1673           0 :   if (NS_FAILED(mEventTarget->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
    1674           0 :     NS_WARNING("Failed to dispatch nsHtml5StreamParserContinuation");
    1675             :   }
    1676           0 : }
    1677             : 
    1678           6 : class nsHtml5TimerKungFu : public Runnable
    1679             : {
    1680             : private:
    1681             :   nsHtml5StreamParserPtr mStreamParser;
    1682             : 
    1683             : public:
    1684           2 :   explicit nsHtml5TimerKungFu(nsHtml5StreamParser* aStreamParser)
    1685           2 :     : Runnable("nsHtml5TimerKungFu")
    1686           2 :     , mStreamParser(aStreamParser)
    1687           2 :   {}
    1688           2 :   NS_IMETHOD Run() override
    1689             :   {
    1690           4 :     mozilla::MutexAutoLock flushTimerLock(mStreamParser->mFlushTimerMutex);
    1691           2 :     if (mStreamParser->mFlushTimer) {
    1692           2 :       mStreamParser->mFlushTimer->Cancel();
    1693           2 :       mStreamParser->mFlushTimer = nullptr;
    1694             :     }
    1695           4 :     return NS_OK;
    1696             :   }
    1697             : };
    1698             : 
    1699             : void
    1700           2 : nsHtml5StreamParser::DropTimer()
    1701             : {
    1702           2 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1703             :   /*
    1704             :    * Simply nulling out the timer wouldn't work, because if the timer is
    1705             :    * armed, it needs to be canceled first. Simply canceling it first wouldn't
    1706             :    * work, because nsTimerImpl::Cancel is not safe for calling from outside
    1707             :    * the thread where nsTimerImpl::Fire would run. It's not safe to
    1708             :    * dispatch a runnable to cancel the timer from the destructor of this
    1709             :    * class, because the timer has a weak (void*) pointer back to this instance
    1710             :    * of the stream parser and having the timer fire before the runnable
    1711             :    * cancels it would make the timer access a deleted object.
    1712             :    *
    1713             :    * This DropTimer method addresses these issues. This method must be called
    1714             :    * on the main thread before the destructor of this class is reached.
    1715             :    * The nsHtml5TimerKungFu object has an nsHtml5StreamParserPtr that addrefs this
    1716             :    * stream parser object to keep it alive until the runnable is done.
    1717             :    * The runnable cancels the timer on the parser thread, drops the timer
    1718             :    * and lets nsHtml5StreamParserPtr send a runnable back to the main thread to
    1719             :    * release the stream parser.
    1720             :    */
    1721           4 :   mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
    1722           2 :   if (mFlushTimer) {
    1723           4 :     nsCOMPtr<nsIRunnable> event = new nsHtml5TimerKungFu(this);
    1724           2 :     if (NS_FAILED(mEventTarget->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
    1725           0 :       NS_WARNING("Failed to dispatch TimerKungFu event");
    1726             :     }
    1727             :   }
    1728           2 : }
    1729             : 
    1730             : // Using a static, because the method name Notify is taken by the chardet
    1731             : // callback.
    1732             : void
    1733           1 : nsHtml5StreamParser::TimerCallback(nsITimer* aTimer, void* aClosure)
    1734             : {
    1735           1 :   (static_cast<nsHtml5StreamParser*> (aClosure))->TimerFlush();
    1736           1 : }
    1737             : 
    1738             : void
    1739           1 : nsHtml5StreamParser::TimerFlush()
    1740             : {
    1741           1 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1742           2 :   mozilla::MutexAutoLock autoLock(mTokenizerMutex);
    1743             : 
    1744           1 :   NS_ASSERTION(!mSpeculating, "Flush timer fired while speculating.");
    1745             : 
    1746             :   // The timer fired if we got here. No need to cancel it. Mark it as
    1747             :   // not armed, though.
    1748           1 :   mFlushTimerArmed = false;
    1749             : 
    1750           1 :   mFlushTimerEverFired = true;
    1751             : 
    1752           1 :   if (IsTerminatedOrInterrupted()) {
    1753           0 :     return;
    1754             :   }
    1755             : 
    1756           1 :   if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
    1757           0 :     mTreeBuilder->Flush(); // delete useless ops
    1758           0 :     if (mTokenizer->FlushViewSource()) {
    1759           0 :       nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
    1760           0 :       if (NS_FAILED(
    1761             :             DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
    1762           0 :         NS_WARNING("failed to dispatch executor flush event");
    1763             :       }
    1764           0 :     }
    1765             :   } else {
    1766             :     // we aren't speculating and we don't know when new data is
    1767             :     // going to arrive. Send data to the main thread.
    1768           1 :     if (mTreeBuilder->Flush(true)) {
    1769           2 :       nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
    1770           1 :       if (NS_FAILED(
    1771             :             DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
    1772           0 :         NS_WARNING("failed to dispatch executor flush event");
    1773             :       }
    1774             :     }
    1775             :   }
    1776             : }
    1777             : 
    1778             : void
    1779           0 : nsHtml5StreamParser::MarkAsBroken(nsresult aRv)
    1780             : {
    1781           0 :   NS_ASSERTION(IsParserThread(), "Wrong thread!");
    1782           0 :   mTokenizerMutex.AssertCurrentThreadOwns();
    1783             : 
    1784           0 :   Terminate();
    1785           0 :   mTreeBuilder->MarkAsBroken(aRv);
    1786           0 :   mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
    1787           0 :   NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
    1788           0 :   nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
    1789           0 :   if (NS_FAILED(DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
    1790           0 :     NS_WARNING("failed to dispatch executor flush event");
    1791             :   }
    1792           0 : }
    1793             : 
    1794             : nsresult
    1795          17 : nsHtml5StreamParser::DispatchToMain(const char* aName,
    1796             :                                     already_AddRefed<nsIRunnable>&& aRunnable)
    1797             : {
    1798          34 :   nsCOMPtr<nsIRunnable> runnable(aRunnable);
    1799          17 :   if (mDocGroup) {
    1800          17 :     return mDocGroup->Dispatch(aName, TaskCategory::Network, runnable.forget());
    1801             :   }
    1802             :   return SchedulerGroup::UnlabeledDispatch(
    1803           0 :     aName, TaskCategory::Network, runnable.forget());
    1804             : }

Generated by: LCOV version 1.13