/parser/html/nsHtml5Parser.cpp

http://github.com/zpao/v8monkey · C++ · 766 lines · 551 code · 72 blank · 143 comment · 99 complexity · 7fe0dee377650495b0c7a4c3efbbce34 MD5 · raw file

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