PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/parser/html/nsHtml5StreamParser.cpp

http://github.com/zpao/v8monkey
C++ | 1622 lines | 1253 code | 125 blank | 244 comment | 235 complexity | 8a4384fa50368e7683dcce1be9d410ae MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, LGPL-2.1, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD
  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. /* ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org code.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Netscape Communications Corporation.
  20. * Portions created by the Initial Developer are Copyright (C) 1998
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Pierre Phaneuf <pp@ludusdesign.com>
  25. * Henri Sivonen <hsivonen@iki.fi>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "nsHtml5StreamParser.h"
  41. #include "nsICharsetConverterManager.h"
  42. #include "nsICharsetAlias.h"
  43. #include "nsServiceManagerUtils.h"
  44. #include "nsEncoderDecoderUtils.h"
  45. #include "nsContentUtils.h"
  46. #include "nsHtml5Tokenizer.h"
  47. #include "nsIHttpChannel.h"
  48. #include "nsHtml5Parser.h"
  49. #include "nsHtml5TreeBuilder.h"
  50. #include "nsHtml5AtomTable.h"
  51. #include "nsHtml5Module.h"
  52. #include "nsHtml5RefPtr.h"
  53. #include "nsIScriptError.h"
  54. #include "mozilla/Preferences.h"
  55. #include "nsHtml5Highlighter.h"
  56. #include "expat_config.h"
  57. #include "expat.h"
  58. #include "nsINestedURI.h"
  59. using namespace mozilla;
  60. static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
  61. PRInt32 nsHtml5StreamParser::sTimerInitialDelay = 120;
  62. PRInt32 nsHtml5StreamParser::sTimerSubsequentDelay = 120;
  63. // static
  64. void
  65. nsHtml5StreamParser::InitializeStatics()
  66. {
  67. Preferences::AddIntVarCache(&sTimerInitialDelay,
  68. "html5.flushtimer.initialdelay");
  69. Preferences::AddIntVarCache(&sTimerSubsequentDelay,
  70. "html5.flushtimer.subsequentdelay");
  71. }
  72. /*
  73. * Note that nsHtml5StreamParser implements cycle collecting AddRef and
  74. * Release. Therefore, nsHtml5StreamParser must never be refcounted from
  75. * the parser thread!
  76. *
  77. * To work around this limitation, runnables posted by the main thread to the
  78. * parser thread hold their reference to the stream parser in an
  79. * nsHtml5RefPtr. Upon creation, nsHtml5RefPtr addrefs the object it holds
  80. * just like a regular nsRefPtr. This is OK, since the creation of the
  81. * runnable and the nsHtml5RefPtr happens on the main thread.
  82. *
  83. * When the runnable is done on the parser thread, the destructor of
  84. * nsHtml5RefPtr runs there. It doesn't call Release on the held object
  85. * directly. Instead, it posts another runnable back to the main thread where
  86. * that runnable calls Release on the wrapped object.
  87. *
  88. * When posting runnables in the other direction, the runnables have to be
  89. * created on the main thread when nsHtml5StreamParser is instantiated and
  90. * held for the lifetime of the nsHtml5StreamParser. This works, because the
  91. * same runnabled can be dispatched multiple times and currently runnables
  92. * posted from the parser thread to main thread don't need to wrap any
  93. * runnable-specific data. (In the other direction, the runnables most notably
  94. * wrap the byte data of the stream.)
  95. */
  96. NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5StreamParser)
  97. NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5StreamParser)
  98. NS_INTERFACE_TABLE_HEAD(nsHtml5StreamParser)
  99. NS_INTERFACE_TABLE2(nsHtml5StreamParser,
  100. nsIStreamListener,
  101. nsICharsetDetectionObserver)
  102. NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5StreamParser)
  103. NS_INTERFACE_MAP_END
  104. NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5StreamParser)
  105. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
  106. tmp->DropTimer();
  107. NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mObserver)
  108. NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
  109. tmp->mOwner = nsnull;
  110. tmp->mExecutorFlusher = nsnull;
  111. tmp->mLoadFlusher = nsnull;
  112. tmp->mExecutor = nsnull;
  113. NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChardet)
  114. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  115. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
  116. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObserver)
  117. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRequest)
  118. if (tmp->mOwner) {
  119. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOwner");
  120. cb.NoteXPCOMChild(static_cast<nsIParser*> (tmp->mOwner));
  121. }
  122. // hack: count the strongly owned edge wrapped in the runnable
  123. if (tmp->mExecutorFlusher) {
  124. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExecutorFlusher->mExecutor");
  125. cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
  126. }
  127. // hack: count the strongly owned edge wrapped in the runnable
  128. if (tmp->mLoadFlusher) {
  129. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadFlusher->mExecutor");
  130. cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
  131. }
  132. // hack: count self if held by mChardet
  133. if (tmp->mChardet) {
  134. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
  135. "mChardet->mObserver");
  136. cb.NoteXPCOMChild(static_cast<nsIStreamListener*>(tmp));
  137. }
  138. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  139. class nsHtml5ExecutorFlusher : public nsRunnable
  140. {
  141. private:
  142. nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
  143. public:
  144. nsHtml5ExecutorFlusher(nsHtml5TreeOpExecutor* aExecutor)
  145. : mExecutor(aExecutor)
  146. {}
  147. NS_IMETHODIMP Run()
  148. {
  149. mExecutor->RunFlushLoop();
  150. return NS_OK;
  151. }
  152. };
  153. class nsHtml5LoadFlusher : public nsRunnable
  154. {
  155. private:
  156. nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
  157. public:
  158. nsHtml5LoadFlusher(nsHtml5TreeOpExecutor* aExecutor)
  159. : mExecutor(aExecutor)
  160. {}
  161. NS_IMETHODIMP Run()
  162. {
  163. mExecutor->FlushSpeculativeLoads();
  164. return NS_OK;
  165. }
  166. };
  167. nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
  168. nsHtml5Parser* aOwner,
  169. eParserMode aMode)
  170. : mFirstBuffer(nsnull) // Will be filled when starting
  171. , mLastBuffer(nsnull) // Will be filled when starting
  172. , mExecutor(aExecutor)
  173. , mTreeBuilder(new nsHtml5TreeBuilder((aMode == VIEW_SOURCE_HTML ||
  174. aMode == VIEW_SOURCE_XML) ?
  175. nsnull : mExecutor->GetStage(),
  176. aMode == NORMAL ?
  177. mExecutor->GetStage() : nsnull))
  178. , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, aMode == VIEW_SOURCE_XML))
  179. , mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
  180. , mOwner(aOwner)
  181. , mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
  182. , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
  183. , mThread(nsHtml5Module::GetStreamParserThread())
  184. , mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
  185. , mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
  186. , mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
  187. , mMode(aMode)
  188. {
  189. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  190. mFlushTimer->SetTarget(mThread);
  191. mAtomTable.Init(); // we aren't checking for OOM anyway...
  192. #ifdef DEBUG
  193. mAtomTable.SetPermittedLookupThread(mThread);
  194. #endif
  195. mTokenizer->setInterner(&mAtomTable);
  196. mTokenizer->setEncodingDeclarationHandler(this);
  197. if (aMode == VIEW_SOURCE_HTML || aMode == VIEW_SOURCE_XML) {
  198. nsHtml5Highlighter* highlighter =
  199. new nsHtml5Highlighter(mExecutor->GetStage());
  200. mTokenizer->EnableViewSource(highlighter); // takes ownership
  201. mTreeBuilder->EnableViewSource(highlighter); // doesn't own
  202. }
  203. // Chardet instantiation adapted from nsDOMFile.
  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. Preferences::GetLocalizedCString("intl.charset.detector");
  209. if (!detectorName.IsEmpty()) {
  210. nsCAutoString detectorContractID;
  211. detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
  212. detectorContractID += detectorName;
  213. if ((mChardet = do_CreateInstance(detectorContractID.get()))) {
  214. (void) mChardet->Init(this);
  215. mFeedChardet = true;
  216. }
  217. }
  218. // There's a zeroing operator new for everything else
  219. }
  220. nsHtml5StreamParser::~nsHtml5StreamParser()
  221. {
  222. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  223. mTokenizer->end();
  224. NS_ASSERTION(!mFlushTimer, "Flush timer was not dropped before dtor!");
  225. #ifdef DEBUG
  226. mRequest = nsnull;
  227. mObserver = nsnull;
  228. mUnicodeDecoder = nsnull;
  229. mSniffingBuffer = nsnull;
  230. mMetaScanner = nsnull;
  231. mFirstBuffer = nsnull;
  232. mExecutor = nsnull;
  233. mTreeBuilder = nsnull;
  234. mTokenizer = nsnull;
  235. mOwner = nsnull;
  236. #endif
  237. }
  238. nsresult
  239. nsHtml5StreamParser::GetChannel(nsIChannel** aChannel)
  240. {
  241. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  242. return mRequest ? CallQueryInterface(mRequest, aChannel) :
  243. NS_ERROR_NOT_AVAILABLE;
  244. }
  245. NS_IMETHODIMP
  246. nsHtml5StreamParser::Notify(const char* aCharset, nsDetectionConfident aConf)
  247. {
  248. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  249. if (aConf == eBestAnswer || aConf == eSureAnswer) {
  250. mFeedChardet = false; // just in case
  251. if (HasDecoder()) {
  252. if (mCharset.Equals(aCharset)) {
  253. NS_ASSERTION(mCharsetSource < kCharsetFromAutoDetection,
  254. "Why are we running chardet at all?");
  255. mCharsetSource = kCharsetFromAutoDetection;
  256. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  257. } else {
  258. // We've already committed to a decoder. Request a reload from the
  259. // docshell.
  260. nsCAutoString charset(aCharset);
  261. mTreeBuilder->NeedsCharsetSwitchTo(charset, kCharsetFromAutoDetection);
  262. FlushTreeOpsAndDisarmTimer();
  263. Interrupt();
  264. }
  265. } else {
  266. // Got a confident answer from the sniffing buffer. That code will
  267. // take care of setting up the decoder.
  268. mCharset.Assign(aCharset);
  269. mCharsetSource = kCharsetFromAutoDetection;
  270. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  271. }
  272. }
  273. return NS_OK;
  274. }
  275. void
  276. nsHtml5StreamParser::SetViewSourceTitle(nsIURI* aURL)
  277. {
  278. if (aURL) {
  279. nsCOMPtr<nsIURI> temp;
  280. bool isViewSource;
  281. aURL->SchemeIs("view-source", &isViewSource);
  282. if (isViewSource) {
  283. nsCOMPtr<nsINestedURI> nested = do_QueryInterface(aURL);
  284. nested->GetInnerURI(getter_AddRefs(temp));
  285. } else {
  286. temp = aURL;
  287. }
  288. bool isData;
  289. temp->SchemeIs("data", &isData);
  290. if (isData) {
  291. // Avoid showing potentially huge data: URLs. The three last bytes are
  292. // UTF-8 for an ellipsis.
  293. mViewSourceTitle.AssignLiteral("data:\xE2\x80\xA6");
  294. } else {
  295. temp->GetSpec(mViewSourceTitle);
  296. }
  297. }
  298. }
  299. nsresult
  300. nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(const PRUint8* aFromSegment, // can be null
  301. PRUint32 aCount,
  302. PRUint32* aWriteCount)
  303. {
  304. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  305. nsresult rv = NS_OK;
  306. nsCOMPtr<nsICharsetConverterManager> convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
  307. NS_ENSURE_SUCCESS(rv, rv);
  308. rv = convManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
  309. if (rv == NS_ERROR_UCONV_NOCONV) {
  310. mCharset.AssignLiteral("windows-1252"); // lower case is the raw form
  311. mCharsetSource = kCharsetFromWeakDocTypeDefault;
  312. rv = convManager->GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
  313. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  314. }
  315. NS_ENSURE_SUCCESS(rv, rv);
  316. mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
  317. return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
  318. }
  319. nsresult
  320. nsHtml5StreamParser::WriteSniffingBufferAndCurrentSegment(const PRUint8* aFromSegment, // can be null
  321. PRUint32 aCount,
  322. PRUint32* aWriteCount)
  323. {
  324. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  325. nsresult rv = NS_OK;
  326. if (mSniffingBuffer) {
  327. PRUint32 writeCount;
  328. rv = WriteStreamBytes(mSniffingBuffer, mSniffingLength, &writeCount);
  329. NS_ENSURE_SUCCESS(rv, rv);
  330. mSniffingBuffer = nsnull;
  331. }
  332. mMetaScanner = nsnull;
  333. if (aFromSegment) {
  334. rv = WriteStreamBytes(aFromSegment, aCount, aWriteCount);
  335. }
  336. return rv;
  337. }
  338. nsresult
  339. nsHtml5StreamParser::SetupDecodingFromBom(const char* aCharsetName, const char* aDecoderCharsetName)
  340. {
  341. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  342. nsresult rv = NS_OK;
  343. nsCOMPtr<nsICharsetConverterManager> convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
  344. NS_ENSURE_SUCCESS(rv, rv);
  345. rv = convManager->GetUnicodeDecoderRaw(aDecoderCharsetName, getter_AddRefs(mUnicodeDecoder));
  346. NS_ENSURE_SUCCESS(rv, rv);
  347. mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
  348. mCharset.Assign(aCharsetName);
  349. mCharsetSource = kCharsetFromByteOrderMark;
  350. mFeedChardet = false;
  351. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  352. mSniffingBuffer = nsnull;
  353. mMetaScanner = nsnull;
  354. mBomState = BOM_SNIFFING_OVER;
  355. return rv;
  356. }
  357. void
  358. nsHtml5StreamParser::SniffBOMlessUTF16BasicLatin(const PRUint8* aFromSegment,
  359. PRUint32 aCountToSniffingLimit)
  360. {
  361. // Avoid underspecified heuristic craziness for XHR
  362. if (mMode == LOAD_AS_DATA) {
  363. return;
  364. }
  365. // Make sure there's enough data. Require room for "<title></title>"
  366. if (mSniffingLength + aCountToSniffingLimit < 30) {
  367. return;
  368. }
  369. // even-numbered bytes tracked at 0, odd-numbered bytes tracked at 1
  370. bool byteZero[2] = { false, false };
  371. bool byteNonZero[2] = { false, false };
  372. PRUint32 i = 0;
  373. if (mSniffingBuffer) {
  374. for (; i < mSniffingLength; ++i) {
  375. if (mSniffingBuffer[i]) {
  376. if (byteNonZero[1 - (i % 2)]) {
  377. return;
  378. }
  379. byteNonZero[i % 2] = true;
  380. } else {
  381. if (byteZero[1 - (i % 2)]) {
  382. return;
  383. }
  384. byteZero[i % 2] = true;
  385. }
  386. }
  387. }
  388. if (aFromSegment) {
  389. for (PRUint32 j = 0; j < aCountToSniffingLimit; ++j) {
  390. if (aFromSegment[j]) {
  391. if (byteNonZero[1 - ((i + j) % 2)]) {
  392. return;
  393. }
  394. byteNonZero[(i + j) % 2] = true;
  395. } else {
  396. if (byteZero[1 - ((i + j) % 2)]) {
  397. return;
  398. }
  399. byteZero[(i + j) % 2] = true;
  400. }
  401. }
  402. }
  403. if (byteNonZero[0]) {
  404. mCharset.Assign("UTF-16LE");
  405. } else {
  406. mCharset.Assign("UTF-16BE");
  407. }
  408. mCharsetSource = kCharsetFromIrreversibleAutoDetection;
  409. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  410. mFeedChardet = false;
  411. }
  412. void
  413. nsHtml5StreamParser::SetEncodingFromExpat(const PRUnichar* aEncoding)
  414. {
  415. if (aEncoding) {
  416. nsDependentString utf16(aEncoding);
  417. nsCAutoString utf8;
  418. CopyUTF16toUTF8(utf16, utf8);
  419. if (PreferredForInternalEncodingDecl(utf8)) {
  420. mCharset.Assign(utf8);
  421. mCharsetSource = kCharsetFromMetaTag; // closest for XML
  422. return;
  423. }
  424. // else the page declared an encoding Gecko doesn't support and we'd
  425. // end up defaulting to UTF-8 anyway. Might as well fall through here
  426. // right away and let the encoding be set to UTF-8 which we'd default to
  427. // anyway.
  428. }
  429. mCharset.AssignLiteral("UTF-8"); // XML defaults to UTF-8 without a BOM
  430. mCharsetSource = kCharsetFromMetaTag; // means confident
  431. }
  432. // A separate user data struct is used instead of passing the
  433. // nsHtml5StreamParser instance as user data in order to avoid including
  434. // expat.h in nsHtml5StreamParser.h. Doing that would cause naming conflicts.
  435. // Using a separate user data struct also avoids bloating nsHtml5StreamParser
  436. // by one pointer.
  437. struct UserData {
  438. XML_Parser mExpat;
  439. nsHtml5StreamParser* mStreamParser;
  440. };
  441. // Using no-namespace handler callbacks to avoid including expat.h in
  442. // nsHtml5StreamParser.h, since doing so would cause naming conclicts.
  443. static void
  444. HandleXMLDeclaration(void* aUserData,
  445. const XML_Char* aVersion,
  446. const XML_Char* aEncoding,
  447. int aStandalone)
  448. {
  449. UserData* ud = static_cast<UserData*>(aUserData);
  450. ud->mStreamParser->SetEncodingFromExpat(
  451. reinterpret_cast<const PRUnichar*>(aEncoding));
  452. XML_StopParser(ud->mExpat, false);
  453. }
  454. static void
  455. HandleStartElement(void* aUserData,
  456. const XML_Char* aName,
  457. const XML_Char **aAtts)
  458. {
  459. UserData* ud = static_cast<UserData*>(aUserData);
  460. XML_StopParser(ud->mExpat, false);
  461. }
  462. static void
  463. HandleEndElement(void* aUserData,
  464. const XML_Char* aName)
  465. {
  466. UserData* ud = static_cast<UserData*>(aUserData);
  467. XML_StopParser(ud->mExpat, false);
  468. }
  469. static void
  470. HandleComment(void* aUserData,
  471. const XML_Char* aName)
  472. {
  473. UserData* ud = static_cast<UserData*>(aUserData);
  474. XML_StopParser(ud->mExpat, false);
  475. }
  476. static void
  477. HandleProcessingInstruction(void* aUserData,
  478. const XML_Char* aTarget,
  479. const XML_Char* aData)
  480. {
  481. UserData* ud = static_cast<UserData*>(aUserData);
  482. XML_StopParser(ud->mExpat, false);
  483. }
  484. nsresult
  485. nsHtml5StreamParser::FinalizeSniffing(const PRUint8* aFromSegment, // can be null
  486. PRUint32 aCount,
  487. PRUint32* aWriteCount,
  488. PRUint32 aCountToSniffingLimit)
  489. {
  490. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  491. NS_ASSERTION(mCharsetSource < kCharsetFromMetaTag,
  492. "Should not finalize sniffing when already confident.");
  493. if (mMode == VIEW_SOURCE_XML) {
  494. static const XML_Memory_Handling_Suite memsuite =
  495. {
  496. (void *(*)(size_t))moz_xmalloc,
  497. (void *(*)(void *, size_t))moz_xrealloc,
  498. moz_free
  499. };
  500. static const PRUnichar kExpatSeparator[] = { 0xFFFF, '\0' };
  501. static const PRUnichar kISO88591[] =
  502. { 'I', 'S', 'O', '-', '8', '8', '5', '9', '-', '1', '\0' };
  503. UserData ud;
  504. ud.mStreamParser = this;
  505. // If we got this far, the stream didn't have a BOM. UTF-16-encoded XML
  506. // documents MUST begin with a BOM. We don't support EBCDIC and such.
  507. // Thus, at this point, what we have is garbage or something encoded using
  508. // a rough ASCII superset. ISO-8859-1 allows us to decode ASCII bytes
  509. // without throwing errors when bytes have the most significant bit set
  510. // and without triggering expat's unknown encoding code paths. This is
  511. // enough to be able to use expat to parse the XML declaration in order
  512. // to extract the encoding name from it.
  513. ud.mExpat = XML_ParserCreate_MM(kISO88591, &memsuite, kExpatSeparator);
  514. XML_SetXmlDeclHandler(ud.mExpat, HandleXMLDeclaration);
  515. XML_SetElementHandler(ud.mExpat, HandleStartElement, HandleEndElement);
  516. XML_SetCommentHandler(ud.mExpat, HandleComment);
  517. XML_SetProcessingInstructionHandler(ud.mExpat, HandleProcessingInstruction);
  518. XML_SetUserData(ud.mExpat, static_cast<void*>(&ud));
  519. XML_Status status = XML_STATUS_OK;
  520. // aFromSegment points to the data obtained from the current network
  521. // event. mSniffingBuffer (if it exists) contains the data obtained before
  522. // the current event. Thus, mSniffingLenth bytes of mSniffingBuffer
  523. // followed by aCountToSniffingLimit bytes from aFromSegment are the
  524. // first 1024 bytes of the file (or the file as a whole if the file is
  525. // 1024 bytes long or shorter). Thus, we parse both buffers, but if the
  526. // first call succeeds already, we skip parsing the second buffer.
  527. if (mSniffingBuffer) {
  528. status = XML_Parse(ud.mExpat,
  529. reinterpret_cast<const char*>(mSniffingBuffer.get()),
  530. mSniffingLength,
  531. false);
  532. }
  533. if (status == XML_STATUS_OK &&
  534. mCharsetSource < kCharsetFromMetaTag &&
  535. aFromSegment) {
  536. status = XML_Parse(ud.mExpat,
  537. reinterpret_cast<const char*>(aFromSegment),
  538. aCountToSniffingLimit,
  539. false);
  540. }
  541. XML_ParserFree(ud.mExpat);
  542. if (mCharsetSource < kCharsetFromMetaTag) {
  543. // Failed to get an encoding from the XML declaration. XML defaults
  544. // confidently to UTF-8 in this case.
  545. // It is also possible that the document has an XML declaration that is
  546. // longer than 1024 bytes, but that case is not worth worrying about.
  547. mCharset.AssignLiteral("UTF-8");
  548. mCharsetSource = kCharsetFromMetaTag; // means confident
  549. }
  550. return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment,
  551. aCount,
  552. aWriteCount);
  553. }
  554. // meta scan failed.
  555. if (mCharsetSource >= kCharsetFromHintPrevDoc) {
  556. mFeedChardet = false;
  557. return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
  558. }
  559. // Check for BOMless UTF-16 with Basic
  560. // Latin content for compat with IE. See bug 631751.
  561. SniffBOMlessUTF16BasicLatin(aFromSegment, aCountToSniffingLimit);
  562. // the charset may have been set now
  563. // maybe try chardet now;
  564. if (mFeedChardet) {
  565. bool dontFeed;
  566. nsresult rv;
  567. if (mSniffingBuffer) {
  568. rv = mChardet->DoIt((const char*)mSniffingBuffer.get(), mSniffingLength, &dontFeed);
  569. mFeedChardet = !dontFeed;
  570. NS_ENSURE_SUCCESS(rv, rv);
  571. }
  572. if (mFeedChardet && aFromSegment) {
  573. rv = mChardet->DoIt((const char*)aFromSegment,
  574. // Avoid buffer boundary-dependent behavior when
  575. // reparsing is forbidden. If reparse is forbidden,
  576. // act as if we only saw the first 1024 bytes.
  577. // When reparsing isn't forbidden, buffer boundaries
  578. // can have an effect on whether the page is loaded
  579. // once or twice. :-(
  580. mReparseForbidden ? aCountToSniffingLimit : aCount,
  581. &dontFeed);
  582. mFeedChardet = !dontFeed;
  583. NS_ENSURE_SUCCESS(rv, rv);
  584. }
  585. if (mFeedChardet && (!aFromSegment || mReparseForbidden)) {
  586. // mReparseForbidden is checked so that we get to use the sniffing
  587. // buffer with the best guess so far if we aren't allowed to guess
  588. // better later.
  589. mFeedChardet = false;
  590. rv = mChardet->Done();
  591. NS_ENSURE_SUCCESS(rv, rv);
  592. }
  593. // fall thru; callback may have changed charset
  594. }
  595. if (mCharsetSource == kCharsetUninitialized) {
  596. // Hopefully this case is never needed, but dealing with it anyway
  597. mCharset.AssignLiteral("windows-1252");
  598. mCharsetSource = kCharsetFromWeakDocTypeDefault;
  599. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  600. } else if (mMode == LOAD_AS_DATA &&
  601. mCharsetSource == kCharsetFromWeakDocTypeDefault) {
  602. NS_ASSERTION(mReparseForbidden, "Reparse should be forbidden for XHR");
  603. NS_ASSERTION(!mFeedChardet, "Should not feed chardet for XHR");
  604. NS_ASSERTION(mCharset.EqualsLiteral("UTF-8"),
  605. "XHR should default to UTF-8");
  606. // Now mark charset source as non-weak to signal that we have a decision
  607. mCharsetSource = kCharsetFromDocTypeDefault;
  608. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  609. }
  610. return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
  611. }
  612. nsresult
  613. nsHtml5StreamParser::SniffStreamBytes(const PRUint8* aFromSegment,
  614. PRUint32 aCount,
  615. PRUint32* aWriteCount)
  616. {
  617. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  618. nsresult rv = NS_OK;
  619. PRUint32 writeCount;
  620. for (PRUint32 i = 0; i < aCount && mBomState != BOM_SNIFFING_OVER; i++) {
  621. switch (mBomState) {
  622. case BOM_SNIFFING_NOT_STARTED:
  623. NS_ASSERTION(i == 0, "Bad BOM sniffing state.");
  624. switch (*aFromSegment) {
  625. case 0xEF:
  626. mBomState = SEEN_UTF_8_FIRST_BYTE;
  627. break;
  628. case 0xFF:
  629. mBomState = SEEN_UTF_16_LE_FIRST_BYTE;
  630. break;
  631. case 0xFE:
  632. mBomState = SEEN_UTF_16_BE_FIRST_BYTE;
  633. break;
  634. default:
  635. mBomState = BOM_SNIFFING_OVER;
  636. break;
  637. }
  638. break;
  639. case SEEN_UTF_16_LE_FIRST_BYTE:
  640. if (aFromSegment[i] == 0xFE) {
  641. rv = SetupDecodingFromBom("UTF-16", "UTF-16LE"); // upper case is the raw form
  642. NS_ENSURE_SUCCESS(rv, rv);
  643. PRUint32 count = aCount - (i + 1);
  644. rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
  645. NS_ENSURE_SUCCESS(rv, rv);
  646. *aWriteCount = writeCount + (i + 1);
  647. return rv;
  648. }
  649. mBomState = BOM_SNIFFING_OVER;
  650. break;
  651. case SEEN_UTF_16_BE_FIRST_BYTE:
  652. if (aFromSegment[i] == 0xFF) {
  653. rv = SetupDecodingFromBom("UTF-16", "UTF-16BE"); // upper case is the raw form
  654. NS_ENSURE_SUCCESS(rv, rv);
  655. PRUint32 count = aCount - (i + 1);
  656. rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
  657. NS_ENSURE_SUCCESS(rv, rv);
  658. *aWriteCount = writeCount + (i + 1);
  659. return rv;
  660. }
  661. mBomState = BOM_SNIFFING_OVER;
  662. break;
  663. case SEEN_UTF_8_FIRST_BYTE:
  664. if (aFromSegment[i] == 0xBB) {
  665. mBomState = SEEN_UTF_8_SECOND_BYTE;
  666. } else {
  667. mBomState = BOM_SNIFFING_OVER;
  668. }
  669. break;
  670. case SEEN_UTF_8_SECOND_BYTE:
  671. if (aFromSegment[i] == 0xBF) {
  672. rv = SetupDecodingFromBom("UTF-8", "UTF-8"); // upper case is the raw form
  673. NS_ENSURE_SUCCESS(rv, rv);
  674. PRUint32 count = aCount - (i + 1);
  675. rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
  676. NS_ENSURE_SUCCESS(rv, rv);
  677. *aWriteCount = writeCount + (i + 1);
  678. return rv;
  679. }
  680. mBomState = BOM_SNIFFING_OVER;
  681. break;
  682. default:
  683. mBomState = BOM_SNIFFING_OVER;
  684. break;
  685. }
  686. }
  687. // if we get here, there either was no BOM or the BOM sniffing isn't complete yet
  688. if (!mMetaScanner && (mMode == NORMAL ||
  689. mMode == VIEW_SOURCE_HTML ||
  690. mMode == LOAD_AS_DATA)) {
  691. mMetaScanner = new nsHtml5MetaScanner();
  692. }
  693. if (mSniffingLength + aCount >= NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE) {
  694. // this is the last buffer
  695. PRUint32 countToSniffingLimit =
  696. NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE - mSniffingLength;
  697. if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
  698. nsHtml5ByteReadable readable(aFromSegment, aFromSegment +
  699. countToSniffingLimit);
  700. mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
  701. if (mUnicodeDecoder) {
  702. mUnicodeDecoder->SetInputErrorBehavior(
  703. nsIUnicodeDecoder::kOnError_Recover);
  704. // meta scan successful
  705. mCharsetSource = kCharsetFromMetaPrescan;
  706. mFeedChardet = false;
  707. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  708. mMetaScanner = nsnull;
  709. return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount,
  710. aWriteCount);
  711. }
  712. }
  713. return FinalizeSniffing(aFromSegment, aCount, aWriteCount,
  714. countToSniffingLimit);
  715. }
  716. // not the last buffer
  717. if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
  718. nsHtml5ByteReadable readable(aFromSegment, aFromSegment + aCount);
  719. mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
  720. if (mUnicodeDecoder) {
  721. // meta scan successful
  722. mUnicodeDecoder->SetInputErrorBehavior(
  723. nsIUnicodeDecoder::kOnError_Recover);
  724. mCharsetSource = kCharsetFromMetaPrescan;
  725. mFeedChardet = false;
  726. mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
  727. mMetaScanner = nsnull;
  728. return WriteSniffingBufferAndCurrentSegment(aFromSegment,
  729. aCount,
  730. aWriteCount);
  731. }
  732. }
  733. if (!mSniffingBuffer) {
  734. const mozilla::fallible_t fallible = mozilla::fallible_t();
  735. mSniffingBuffer = new (fallible)
  736. PRUint8[NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE];
  737. if (!mSniffingBuffer) {
  738. return NS_ERROR_OUT_OF_MEMORY;
  739. }
  740. }
  741. memcpy(mSniffingBuffer + mSniffingLength, aFromSegment, aCount);
  742. mSniffingLength += aCount;
  743. *aWriteCount = aCount;
  744. return NS_OK;
  745. }
  746. nsresult
  747. nsHtml5StreamParser::WriteStreamBytes(const PRUint8* aFromSegment,
  748. PRUint32 aCount,
  749. PRUint32* aWriteCount)
  750. {
  751. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  752. // mLastBuffer always points to a buffer of the size
  753. // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
  754. if (mLastBuffer->getEnd() == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
  755. nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
  756. nsHtml5OwningUTF16Buffer::FalliblyCreate(
  757. NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
  758. if (!newBuf) {
  759. return NS_ERROR_OUT_OF_MEMORY;
  760. }
  761. mLastBuffer = (mLastBuffer->next = newBuf.forget());
  762. }
  763. PRInt32 totalByteCount = 0;
  764. for (;;) {
  765. PRInt32 end = mLastBuffer->getEnd();
  766. PRInt32 byteCount = aCount - totalByteCount;
  767. PRInt32 utf16Count = NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE - end;
  768. NS_ASSERTION(utf16Count, "Trying to convert into a buffer with no free space!");
  769. // byteCount may be zero to force the decoder to output a pending surrogate
  770. // pair.
  771. nsresult convResult = mUnicodeDecoder->Convert((const char*)aFromSegment, &byteCount, mLastBuffer->getBuffer() + end, &utf16Count);
  772. end += utf16Count;
  773. mLastBuffer->setEnd(end);
  774. totalByteCount += byteCount;
  775. aFromSegment += byteCount;
  776. NS_ASSERTION(end <= NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE,
  777. "The Unicode decoder wrote too much data.");
  778. NS_ASSERTION(byteCount >= -1, "The decoder consumed fewer than -1 bytes.");
  779. if (NS_FAILED(convResult)) {
  780. // Using the more generic NS_FAILED test above in case there are still
  781. // decoders around that don't use NS_ERROR_ILLEGAL_INPUT properly.
  782. NS_ASSERTION(convResult == NS_ERROR_ILLEGAL_INPUT,
  783. "The decoder signaled an error other than NS_ERROR_ILLEGAL_INPUT.");
  784. // There's an illegal byte in the input. It's now the responsibility
  785. // of this calling code to output a U+FFFD REPLACEMENT CHARACTER and
  786. // reset the decoder.
  787. if (totalByteCount < (PRInt32)aCount) {
  788. // advance over the bad byte
  789. ++totalByteCount;
  790. ++aFromSegment;
  791. } else {
  792. NS_NOTREACHED("The decoder signaled an error but consumed all input.");
  793. // Recovering from this situation in case there are still broken
  794. // decoders, since nsScanner had recovery code, too.
  795. totalByteCount = (PRInt32)aCount;
  796. }
  797. // Emit the REPLACEMENT CHARACTER
  798. mLastBuffer->getBuffer()[end] = 0xFFFD;
  799. ++end;
  800. mLastBuffer->setEnd(end);
  801. if (end == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
  802. nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
  803. nsHtml5OwningUTF16Buffer::FalliblyCreate(
  804. NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
  805. if (!newBuf) {
  806. return NS_ERROR_OUT_OF_MEMORY;
  807. }
  808. mLastBuffer = (mLastBuffer->next = newBuf.forget());
  809. }
  810. mUnicodeDecoder->Reset();
  811. if (totalByteCount == (PRInt32)aCount) {
  812. *aWriteCount = (PRUint32)totalByteCount;
  813. return NS_OK;
  814. }
  815. } else if (convResult == NS_PARTIAL_MORE_OUTPUT) {
  816. nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
  817. nsHtml5OwningUTF16Buffer::FalliblyCreate(
  818. NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
  819. if (!newBuf) {
  820. return NS_ERROR_OUT_OF_MEMORY;
  821. }
  822. mLastBuffer = (mLastBuffer->next = newBuf.forget());
  823. // All input may have been consumed if there is a pending surrogate pair
  824. // that doesn't fit in the output buffer. Loop back to push a zero-length
  825. // input to the decoder in that case.
  826. } else {
  827. NS_ASSERTION(totalByteCount == (PRInt32)aCount,
  828. "The Unicode decoder consumed the wrong number of bytes.");
  829. *aWriteCount = (PRUint32)totalByteCount;
  830. return NS_OK;
  831. }
  832. }
  833. }
  834. // nsIRequestObserver methods:
  835. nsresult
  836. nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
  837. {
  838. NS_PRECONDITION(STREAM_NOT_STARTED == mStreamState,
  839. "Got OnStartRequest when the stream had already started.");
  840. NS_PRECONDITION(!mExecutor->HasStarted(),
  841. "Got OnStartRequest at the wrong stage in the executor life cycle.");
  842. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  843. if (mObserver) {
  844. mObserver->OnStartRequest(aRequest, aContext);
  845. }
  846. mRequest = aRequest;
  847. mStreamState = STREAM_BEING_READ;
  848. if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
  849. mTokenizer->StartViewSource(NS_ConvertUTF8toUTF16(mViewSourceTitle));
  850. }
  851. // For View Source, the parser should run with scripts "enabled" if a normal
  852. // load would have scripts enabled.
  853. bool scriptingEnabled = mMode == LOAD_AS_DATA ?
  854. false : mExecutor->IsScriptEnabled();
  855. mOwner->StartTokenizer(scriptingEnabled);
  856. mTreeBuilder->setScriptingEnabled(scriptingEnabled);
  857. mTokenizer->start();
  858. mExecutor->Start();
  859. mExecutor->StartReadingFromStage();
  860. if (mMode == PLAIN_TEXT) {
  861. mTreeBuilder->StartPlainText();
  862. mTokenizer->StartPlainText();
  863. } else if (mMode == VIEW_SOURCE_PLAIN) {
  864. mTreeBuilder->StartPlainTextViewSource(NS_ConvertUTF8toUTF16(mViewSourceTitle));
  865. mTokenizer->StartPlainText();
  866. }
  867. /*
  868. * If you move the following line, be very careful not to cause
  869. * WillBuildModel to be called before the document has had its
  870. * script global object set.
  871. */
  872. mExecutor->WillBuildModel(eDTDMode_unknown);
  873. nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
  874. nsHtml5OwningUTF16Buffer::FalliblyCreate(
  875. NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
  876. if (!newBuf) {
  877. mExecutor->MarkAsBroken(); // marks this stream parser as terminated,
  878. // which prevents entry to code paths that
  879. // would use mFirstBuffer or mLastBuffer.
  880. return NS_ERROR_OUT_OF_MEMORY;
  881. }
  882. NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?");
  883. NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?");
  884. mFirstBuffer = mLastBuffer = newBuf;
  885. nsresult rv = NS_OK;
  886. // The line below means that the encoding can end up being wrong if
  887. // a view-source URL is loaded without having the encoding hint from a
  888. // previous normal load in the history.
  889. mReparseForbidden = !(mMode == NORMAL || mMode == PLAIN_TEXT);
  890. nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mRequest, &rv));
  891. if (NS_SUCCEEDED(rv)) {
  892. nsCAutoString method;
  893. httpChannel->GetRequestMethod(method);
  894. // XXX does Necko have a way to renavigate POST, etc. without hitting
  895. // the network?
  896. if (!method.EqualsLiteral("GET")) {
  897. // This is the old Gecko behavior but the HTML5 spec disagrees.
  898. // Don't reparse on POST.
  899. mReparseForbidden = true;
  900. mFeedChardet = false; // can't restart anyway
  901. }
  902. }
  903. if (mCharsetSource >= kCharsetFromAutoDetection) {
  904. mFeedChardet = false;
  905. }
  906. if (mCharsetSource <= kCharsetFromMetaPrescan) {
  907. // we aren't ready to commit to an encoding yet
  908. // leave converter uninstantiated for now
  909. return NS_OK;
  910. }
  911. nsCOMPtr<nsICharsetConverterManager> convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
  912. NS_ENSURE_SUCCESS(rv, rv);
  913. rv = convManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
  914. // if we failed to get a decoder, there will be fallback, so don't propagate
  915. // the error.
  916. if (NS_SUCCEEDED(rv)) {
  917. mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
  918. } else {
  919. mCharsetSource = kCharsetFromWeakDocTypeDefault;
  920. }
  921. return NS_OK;
  922. }
  923. void
  924. nsHtml5StreamParser::DoStopRequest()
  925. {
  926. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  927. NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
  928. "Stream ended without being open.");
  929. mTokenizerMutex.AssertCurrentThreadOwns();
  930. if (IsTerminated()) {
  931. return;
  932. }
  933. mStreamState = STREAM_ENDED;
  934. if (!mUnicodeDecoder) {
  935. PRUint32 writeCount;
  936. if (NS_FAILED(FinalizeSniffing(nsnull, 0, &writeCount, 0))) {
  937. MarkAsBroken();
  938. return;
  939. }
  940. } else if (mFeedChardet) {
  941. mChardet->Done();
  942. }
  943. if (IsTerminatedOrInterrupted()) {
  944. return;
  945. }
  946. ParseAvailableData();
  947. }
  948. class nsHtml5RequestStopper : public nsRunnable
  949. {
  950. private:
  951. nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
  952. public:
  953. nsHtml5RequestStopper(nsHtml5StreamParser* aStreamParser)
  954. : mStreamParser(aStreamParser)
  955. {}
  956. NS_IMETHODIMP Run()
  957. {
  958. mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
  959. mStreamParser->DoStopRequest();
  960. return NS_OK;
  961. }
  962. };
  963. nsresult
  964. nsHtml5StreamParser::OnStopRequest(nsIRequest* aRequest,
  965. nsISupports* aContext,
  966. nsresult status)
  967. {
  968. NS_ASSERTION(mRequest == aRequest, "Got Stop on wrong stream.");
  969. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  970. if (mObserver) {
  971. mObserver->OnStopRequest(aRequest, aContext, status);
  972. }
  973. nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
  974. if (NS_FAILED(mThread->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
  975. NS_WARNING("Dispatching StopRequest event failed.");
  976. }
  977. return NS_OK;
  978. }
  979. void
  980. nsHtml5StreamParser::DoDataAvailable(PRUint8* aBuffer, PRUint32 aLength)
  981. {
  982. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  983. NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
  984. "DoDataAvailable called when stream not open.");
  985. mTokenizerMutex.AssertCurrentThreadOwns();
  986. if (IsTerminated()) {
  987. return;
  988. }
  989. PRUint32 writeCount;
  990. nsresult rv;
  991. if (HasDecoder()) {
  992. if (mFeedChardet) {
  993. bool dontFeed;
  994. mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
  995. mFeedChardet = !dontFeed;
  996. }
  997. rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
  998. } else {
  999. rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
  1000. }
  1001. if (NS_FAILED(rv)) {
  1002. MarkAsBroken();
  1003. return;
  1004. }
  1005. NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
  1006. if (IsTerminatedOrInterrupted()) {
  1007. return;
  1008. }
  1009. ParseAvailableData();
  1010. if (mFlushTimerArmed || mSpeculating) {
  1011. return;
  1012. }
  1013. mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
  1014. static_cast<void*> (this),
  1015. mFlushTimerEverFired ?
  1016. sTimerInitialDelay :
  1017. sTimerSubsequentDelay,
  1018. nsITimer::TYPE_ONE_SHOT);
  1019. mFlushTimerArmed = true;
  1020. }
  1021. class nsHtml5DataAvailable : public nsRunnable
  1022. {
  1023. private:
  1024. nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
  1025. nsAutoArrayPtr<PRUint8> mData;
  1026. PRUint32 mLength;
  1027. public:
  1028. nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser,
  1029. PRUint8* aData,
  1030. PRUint32 aLength)
  1031. : mStreamParser(aStreamParser)
  1032. , mData(aData)
  1033. , mLength(aLength)
  1034. {}
  1035. NS_IMETHODIMP Run()
  1036. {
  1037. mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
  1038. mStreamParser->DoDataAvailable(mData, mLength);
  1039. return NS_OK;
  1040. }
  1041. };
  1042. // nsIStreamListener method:
  1043. nsresult
  1044. nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
  1045. nsISupports* aContext,
  1046. nsIInputStream* aInStream,
  1047. PRUint32 aSourceOffset,
  1048. PRUint32 aLength)
  1049. {
  1050. if (mExecutor->IsBroken()) {
  1051. return NS_ERROR_OUT_OF_MEMORY;
  1052. }
  1053. NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
  1054. PRUint32 totalRead;
  1055. const mozilla::fallible_t fallible = mozilla::fallible_t();
  1056. nsAutoArrayPtr<PRUint8> data(new (fallible) PRUint8[aLength]);
  1057. if (!data) {
  1058. mExecutor->MarkAsBroken();
  1059. return NS_ERROR_OUT_OF_MEMORY;
  1060. }
  1061. nsresult rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
  1062. aLength, &totalRead);
  1063. NS_ENSURE_SUCCESS(rv, rv);
  1064. NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
  1065. nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
  1066. data.forget(),
  1067. totalRead);
  1068. if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
  1069. NS_WARNING("Dispatching DataAvailable event failed.");
  1070. }
  1071. return rv;
  1072. }
  1073. bool
  1074. nsHtml5StreamParser::PreferredForInternalEncodingDecl(nsACString& aEncoding)
  1075. {
  1076. nsCAutoString newEncoding(aEncoding);
  1077. newEncoding.Trim(" \t\r\n\f");
  1078. if (newEncoding.LowerCaseEqualsLiteral("utf-16") ||
  1079. newEncoding.LowerCaseEqualsLiteral("utf-16be") ||
  1080. newEncoding.LowerCaseEqualsLiteral("utf-16le")) {
  1081. newEncoding.Assign("UTF-8");
  1082. }
  1083. nsresult rv = NS_OK;
  1084. nsCOMPtr<nsICharsetAlias> calias(do_GetService(kCharsetAliasCID, &rv));
  1085. if (NS_FAILED(rv)) {
  1086. NS_NOTREACHED("Charset alias service not available.");
  1087. return false;
  1088. }
  1089. bool eq;
  1090. rv = calias->Equals(newEncoding, mCharset, &eq);
  1091. if (NS_FAILED(rv)) {
  1092. NS_NOTREACHED("Charset name equality check failed.");
  1093. return false;
  1094. }
  1095. if (eq) {
  1096. mCharsetSource = kCharsetFromMetaTag; // become confident
  1097. mFeedChardet = false; // don't feed chardet when confident
  1098. return false;
  1099. }
  1100. // XXX check HTML5 non-IANA aliases here
  1101. nsCAutoString preferred;
  1102. rv = calias->GetPreferred(newEncoding, preferred);
  1103. if (NS_FAILED(rv)) {
  1104. // the encoding name is bogus
  1105. return false;
  1106. }
  1107. if (preferred.LowerCaseEqualsLiteral("utf-16") ||
  1108. preferred.LowerCaseEqualsLiteral("utf-16be") ||
  1109. preferred.LowerCaseEqualsLiteral("utf-16le") ||
  1110. preferred.LowerCaseEqualsLiteral("utf-7") ||
  1111. preferred.LowerCaseEqualsLiteral("jis_x0212-1990") ||
  1112. preferred.LowerCaseEqualsLiteral("x-jis0208") ||
  1113. preferred.LowerCaseEqualsLiteral("x-imap4-modified-utf7") ||
  1114. preferred.LowerCaseEqualsLiteral("x-user-defined")) {
  1115. // Not a rough ASCII superset
  1116. return false;
  1117. }
  1118. aEncoding.Assign(preferred);
  1119. return true;
  1120. }
  1121. bool
  1122. nsHtml5StreamParser::internalEncodingDeclaration(nsString* aEncoding)
  1123. {
  1124. // This code needs to stay in sync with
  1125. // nsHtml5MetaScanner::tryCharset. Unfortunately, the
  1126. // trickery with member fields there leads to some copy-paste reuse. :-(
  1127. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  1128. if (mCharsetSource >= kCharsetFromMetaTag) { // this threshold corresponds to "confident" in the HTML5 spec
  1129. return false;
  1130. }
  1131. if (mReparseForbidden) {
  1132. return false; // not reparsing even if we wanted to
  1133. }
  1134. nsCAutoString newEncoding;
  1135. CopyUTF16toUTF8(*aEncoding, newEncoding);
  1136. if (!PreferredForInternalEncodingDecl(newEncoding)) {
  1137. return false;
  1138. }
  1139. // Avoid having the chardet ask for another restart after this restart
  1140. // request.
  1141. mFeedChardet = false;
  1142. mTreeBuilder->NeedsCharsetSwitchTo(newEncoding, kCharsetFromMetaTag);
  1143. FlushTreeOpsAndDisarmTimer();
  1144. Interrupt();
  1145. // the tree op executor will cause the stream parser to terminate
  1146. // if the charset switch request is accepted or it'll uninterrupt
  1147. // if the request failed. Note that if the restart request fails,
  1148. // we don't bother trying to make chardet resume. Might as well
  1149. // assume that chardet-requested restarts would fail, too.
  1150. return true;
  1151. }
  1152. void
  1153. nsHtml5StreamParser::FlushTreeOpsAndDisarmTimer()
  1154. {
  1155. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  1156. if (mFlushTimerArmed) {
  1157. // avoid calling Cancel if the flush timer isn't armed to avoid acquiring
  1158. // a mutex
  1159. mFlushTimer->Cancel();
  1160. mFlushTimerArmed = false;
  1161. }
  1162. if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
  1163. mTokenizer->FlushViewSource();
  1164. }
  1165. mTreeBuilder->Flush();
  1166. if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
  1167. NS_WARNING("failed to dispatch executor flush event");
  1168. }
  1169. }
  1170. void
  1171. nsHtml5StreamParser::ParseAvailableData()
  1172. {
  1173. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  1174. mTokenizerMutex.AssertCurrentThreadOwns();
  1175. if (IsTerminatedOrInterrupted()) {
  1176. return;
  1177. }
  1178. for (;;) {
  1179. if (!mFirstBuffer->hasMore()) {
  1180. if (mFirstBuffer == mLastBuffer) {
  1181. switch (mStreamState) {
  1182. case STREAM_BEING_READ:
  1183. // never release the last buffer.
  1184. if (!mSpeculating) {
  1185. // reuse buffer space if not speculating
  1186. mFirstBuffer->setStart(0);
  1187. mFirstBuffer->setEnd(0);
  1188. }
  1189. mTreeBuilder->FlushLoads();
  1190. // Dispatch this runnable unconditionally, because the loads
  1191. // that need flushing may have been flushed earlier even if the
  1192. // flush right above here did nothing.
  1193. if (NS_FAILED(NS_DispatchToMainThread(mLoadFlusher))) {
  1194. NS_WARNING("failed to dispatch load flush event");
  1195. }
  1196. return; // no more data for now but expecting more
  1197. case STREAM_ENDED:
  1198. if (mAtEOF) {
  1199. return;
  1200. }
  1201. mAtEOF = true;
  1202. mTokenizer->eof();
  1203. mTreeBuilder->StreamEnded();
  1204. if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
  1205. mTokenizer->EndViewSource();
  1206. }
  1207. FlushTreeOpsAndDisarmTimer();
  1208. return; // no more data and not expecting more
  1209. default:
  1210. NS_NOTREACHED("It should be impossible to reach this.");
  1211. return;
  1212. }
  1213. }
  1214. mFirstBuffer = mFirstBuffer->next;
  1215. continue;
  1216. }
  1217. // now we have a non-empty buffer
  1218. mFirstBuffer->adjust(mLastWasCR);
  1219. mLastWasCR = false;
  1220. if (mFirstBuffer->hasMore()) {
  1221. mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
  1222. // At this point, internalEncodingDeclaration() may have called
  1223. // Terminate, but that never happens together with script.
  1224. // Can't assert that here, though, because it's possible that the main
  1225. // thread has called Terminate() while this thread was parsing.
  1226. if (mMode == NORMAL && mTreeBuilder->HasScript()) {
  1227. mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
  1228. nsHtml5Speculation* speculation =
  1229. new nsHtml5Speculation(mFirstBuffer,
  1230. mFirstBuffer->getStart(),
  1231. mTokenizer->getLineNumber(),
  1232. mTreeBuilder->newSnapshot());
  1233. mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot(),
  1234. speculation->GetStartLineNumber());
  1235. FlushTreeOpsAndDisarmTimer();
  1236. mTreeBuilder->SetOpSink(speculation);
  1237. mSpeculations.AppendElement(speculation); // adopts the pointer
  1238. mSpeculating = true;
  1239. }
  1240. if (IsTerminatedOrInterrupted()) {
  1241. return;
  1242. }
  1243. }
  1244. continue;
  1245. }
  1246. }
  1247. class nsHtml5StreamParserContinuation : public nsRunnable
  1248. {
  1249. private:
  1250. nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
  1251. public:
  1252. nsHtml5StreamParserContinuation(nsHtml5StreamParser* aStreamParser)
  1253. : mStreamParser(aStreamParser)
  1254. {}
  1255. NS_IMETHODIMP Run()
  1256. {
  1257. mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
  1258. mStreamParser->Uninterrupt();
  1259. mStreamParser->ParseAvailableData();
  1260. return NS_OK;
  1261. }
  1262. };
  1263. void
  1264. nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
  1265. nsHtml5TreeBuilder* aTreeBuilder,
  1266. bool aLastWasCR)
  1267. {
  1268. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1269. NS_ASSERTION(!(mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML),
  1270. "ContinueAfterScripts called in view source mode!");
  1271. if (mExecutor->IsBroken()) {
  1272. return;
  1273. }
  1274. #ifdef DEBUG
  1275. mExecutor->AssertStageEmpty();
  1276. #endif
  1277. bool speculationFailed = false;
  1278. {
  1279. mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
  1280. if (mSpeculations.IsEmpty()) {
  1281. NS_NOTREACHED("ContinueAfterScripts called without speculations.");
  1282. return;
  1283. }
  1284. nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
  1285. if (aLastWasCR ||
  1286. !aTokenizer->isInDataState() ||
  1287. !aTreeBuilder->snapshotMatches(speculation->GetSnapshot())) {
  1288. speculationFailed = true;
  1289. // We've got a failed speculation :-(
  1290. Interrupt(); // Make the parser thread release the tokenizer mutex sooner
  1291. // now fall out of the speculationAutoLock into the tokenizerAutoLock block
  1292. } else {
  1293. // We've got a successful speculation!
  1294. if (mSpeculations.Length() > 1) {
  1295. // the first speculation isn't the current speculation, so there's
  1296. // no need to bother the parser thread.
  1297. speculation->FlushToSink(mExecutor);
  1298. NS_ASSERTION(!mExecutor->IsScriptExecuting(),
  1299. "ParseUntilBlocked() was supposed to ensure we don't come "
  1300. "here when scripts are executing.");
  1301. NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
  1302. "RunFlushLoop() didn't call ParseUntilBlocked() which is the "
  1303. "only caller of this method?");
  1304. mSpeculations.RemoveElementAt(0);
  1305. return;
  1306. }
  1307. // else
  1308. Interrupt(); // Make the parser thread release the tokenizer mutex sooner
  1309. // now fall through
  1310. // the first speculation is the current speculation. Need to
  1311. // release the the speculation mutex and acquire the tokenizer
  1312. // mutex. (Just acquiring the other mutex here would deadlock)
  1313. }
  1314. }
  1315. {
  1316. mozilla::MutexAutoLock tokenizerAutoLock(mTokenizerMutex);
  1317. #ifdef DEBUG
  1318. {
  1319. nsCOMPtr<nsIThread> mainThread;
  1320. NS_GetMainThread(getter_AddRefs(mainThread));
  1321. mAtomTable.SetPermittedLookupThread(mainThread);
  1322. }
  1323. #endif
  1324. // In principle, the speculation mutex should be acquired here,
  1325. // but there's no point, because the parser thread only acquires it
  1326. // when it has also acquired the tokenizer mutex and we are already
  1327. // holding the tokenizer mutex.
  1328. if (speculationFailed) {
  1329. // Rewind the stream
  1330. mAtEOF = false;
  1331. nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
  1332. mFirstBuffer = speculation->GetBuffer();
  1333. mFirstBuffer->setStart(speculation->GetStart());
  1334. mTokenizer->setLineNumber(speculation->GetStartLineNumber());
  1335. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1336. "DOM Events",
  1337. mExecutor->GetDocument(),
  1338. nsContentUtils::eDOM_PROPERTIES,
  1339. "SpeculationFailed",
  1340. nsnull, 0,
  1341. nsnull,
  1342. EmptyString(),
  1343. speculation->GetStartLineNumber());
  1344. nsHtml5OwningUTF16Buffer* buffer = mFirstBuffer->next;
  1345. while (buffer) {
  1346. buffer->setStart(0);
  1347. buffer = buffer->next;
  1348. }
  1349. mSpeculations.Clear(); // potentially a huge number of destructors
  1350. // run here synchronously on the main thread...
  1351. mTreeBuilder->flushCharacters(); // empty the pending buffer
  1352. mTreeBuilder->ClearOps(); // now get rid of the failed ops
  1353. mTreeBuilder->SetOpSink(mExecutor->GetStage());
  1354. mExecutor->StartReadingFromStage();
  1355. mSpeculating = false;
  1356. // Copy state over
  1357. mLastWasCR = aLastWasCR;
  1358. mTokenizer->loadState(aTokenizer);
  1359. mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
  1360. } else {
  1361. // We've got a successful speculation and at least a moment ago it was
  1362. // the current speculation
  1363. mSpeculations.ElementAt(0)->FlushToSink(mExecutor);
  1364. NS_ASSERTION(!mExecutor->IsScriptExecuting(),
  1365. "ParseUntilBlocked() was supposed to ensure we don't come "
  1366. "here when scripts are executing.");
  1367. NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
  1368. "RunFlushLoop() didn't call ParseUntilBlocked() which is the "
  1369. "only caller of this method?");
  1370. mSpeculations.RemoveElementAt(0);
  1371. if (mSpeculations.IsEmpty()) {
  1372. // yes, it was still the only speculation. Now stop speculating
  1373. // However, before telling the executor to read from stage, flush
  1374. // any pending ops straight to the executor, because otherwise
  1375. // they remain unflushed until we get more data from the network.
  1376. mTreeBuilder->SetOpSink(mExecutor);
  1377. mTreeBuilder->Flush(true);
  1378. mTreeBuilder->SetOpSink(mExecutor->GetStage());
  1379. mExecutor->StartReadingFromStage();
  1380. mSpeculating = false;
  1381. }
  1382. }
  1383. nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
  1384. if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
  1385. NS_WARNING("Failed to dispatch nsHtml5StreamParserContinuation");
  1386. }
  1387. // A stream event might run before this event runs, but that's harmless.
  1388. #ifdef DEBUG
  1389. mAtomTable.SetPermittedLookupThread(mThread);
  1390. #endif
  1391. }
  1392. }
  1393. void
  1394. nsHtml5StreamParser::ContinueAfterFailedCharsetSwitch()
  1395. {
  1396. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1397. nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
  1398. if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
  1399. NS_WARNING("Failed to dispatch nsHtml5StreamParserContinuation");
  1400. }
  1401. }
  1402. class nsHtml5TimerKungFu : public nsRunnable
  1403. {
  1404. private:
  1405. nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
  1406. public:
  1407. nsHtml5TimerKungFu(nsHtml5StreamParser* aStreamParser)
  1408. : mStreamParser(aStreamParser)
  1409. {}
  1410. NS_IMETHODIMP Run()
  1411. {
  1412. if (mStreamParser->mFlushTimer) {
  1413. mStreamParser->mFlushTimer->Cancel();
  1414. mStreamParser->mFlushTimer = nsnull;
  1415. }
  1416. return NS_OK;
  1417. }
  1418. };
  1419. void
  1420. nsHtml5StreamParser::DropTimer()
  1421. {
  1422. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1423. /*
  1424. * Simply nulling out the timer wouldn't work, because if the timer is
  1425. * armed, it needs to be canceled first. Simply canceling it first wouldn't
  1426. * work, because nsTimerImpl::Cancel is not safe for calling from outside
  1427. * the thread where nsTimerImpl::Fire would run. It's not safe to
  1428. * dispatch a runnable to cancel the timer from the destructor of this
  1429. * class, because the timer has a weak (void*) pointer back to this instance
  1430. * of the stream parser and having the timer fire before the runnable
  1431. * cancels it would make the timer access a deleted object.
  1432. *
  1433. * This DropTimer method addresses these issues. This method must be called
  1434. * on the main thread before the destructor of this class is reached.
  1435. * The nsHtml5TimerKungFu object has an nsHtml5RefPtr that addrefs this
  1436. * stream parser object to keep it alive until the runnable is done.
  1437. * The runnable cancels the timer on the parser thread, drops the timer
  1438. * and lets nsHtml5RefPtr send a runnable back to the main thread to
  1439. * release the stream parser.
  1440. */
  1441. if (mFlushTimer) {
  1442. nsCOMPtr<nsIRunnable> event = new nsHtml5TimerKungFu(this);
  1443. if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
  1444. NS_WARNING("Failed to dispatch TimerKungFu event");
  1445. }
  1446. }
  1447. }
  1448. // Using a static, because the method name Notify is taken by the chardet
  1449. // callback.
  1450. void
  1451. nsHtml5StreamParser::TimerCallback(nsITimer* aTimer, void* aClosure)
  1452. {
  1453. (static_cast<nsHtml5StreamParser*> (aClosure))->TimerFlush();
  1454. }
  1455. void
  1456. nsHtml5StreamParser::TimerFlush()
  1457. {
  1458. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  1459. mozilla::MutexAutoLock autoLock(mTokenizerMutex);
  1460. NS_ASSERTION(!mSpeculating, "Flush timer fired while speculating.");
  1461. // The timer fired if we got here. No need to cancel it. Mark it as
  1462. // not armed, though.
  1463. mFlushTimerArmed = false;
  1464. mFlushTimerEverFired = true;
  1465. if (IsTerminatedOrInterrupted()) {
  1466. return;
  1467. }
  1468. if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
  1469. mTreeBuilder->Flush(); // delete useless ops
  1470. if (mTokenizer->FlushViewSource()) {
  1471. if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
  1472. NS_WARNING("failed to dispatch executor flush event");
  1473. }
  1474. }
  1475. } else {
  1476. // we aren't speculating and we don't know when new data is
  1477. // going to arrive. Send data to the main thread.
  1478. if (mTreeBuilder->Flush(true)) {
  1479. if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
  1480. NS_WARNING("failed to dispatch executor flush event");
  1481. }
  1482. }
  1483. }
  1484. }
  1485. void
  1486. nsHtml5StreamParser::MarkAsBroken()
  1487. {
  1488. NS_ASSERTION(IsParserThread(), "Wrong thread!");
  1489. mTokenizerMutex.AssertCurrentThreadOwns();
  1490. Terminate();
  1491. mTreeBuilder->MarkAsBroken();
  1492. mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
  1493. NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
  1494. if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
  1495. NS_WARNING("failed to dispatch executor flush event");
  1496. }
  1497. }