/parser/html/nsHtml5TreeOpExecutor.h

http://github.com/zpao/v8monkey · C Header · 445 lines · 255 code · 82 blank · 108 comment · 18 complexity · cf18012464c6cd9cbcf177f4358e5db2 MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is HTML Parser Gecko integration code.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * Mozilla Foundation.
  18. * Portions created by the Initial Developer are Copyright (C) 2009
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. * Henri Sivonen <hsivonen@iki.fi>
  23. *
  24. * Alternatively, the contents of this file may be used under the terms of
  25. * either the GNU General Public License Version 2 or later (the "GPL"), or
  26. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. * in which case the provisions of the GPL or the LGPL are applicable instead
  28. * of those above. If you wish to allow use of your version of this file only
  29. * under the terms of either the GPL or the LGPL, and not to allow others to
  30. * use your version of this file under the terms of the MPL, indicate your
  31. * decision by deleting the provisions above and replace them with the notice
  32. * and other provisions required by the GPL or the LGPL. If you do not delete
  33. * the provisions above, a recipient may use your version of this file under
  34. * the terms of any one of the MPL, the GPL or the LGPL.
  35. *
  36. * ***** END LICENSE BLOCK ***** */
  37. #ifndef nsHtml5TreeOpExecutor_h__
  38. #define nsHtml5TreeOpExecutor_h__
  39. #include "prtypes.h"
  40. #include "nsIAtom.h"
  41. #include "nsINameSpaceManager.h"
  42. #include "nsIContent.h"
  43. #include "nsIDocument.h"
  44. #include "nsTraceRefcnt.h"
  45. #include "nsHtml5TreeOperation.h"
  46. #include "nsHtml5SpeculativeLoad.h"
  47. #include "nsHtml5PendingNotification.h"
  48. #include "nsTArray.h"
  49. #include "nsContentSink.h"
  50. #include "nsNodeInfoManager.h"
  51. #include "nsHtml5DocumentMode.h"
  52. #include "nsIScriptElement.h"
  53. #include "nsIParser.h"
  54. #include "nsCOMArray.h"
  55. #include "nsAHtml5TreeOpSink.h"
  56. #include "nsHtml5TreeOpStage.h"
  57. #include "nsHashSets.h"
  58. #include "nsIURI.h"
  59. class nsHtml5Parser;
  60. class nsHtml5TreeBuilder;
  61. class nsHtml5Tokenizer;
  62. class nsHtml5StreamParser;
  63. typedef nsIContent* nsIContentPtr;
  64. enum eHtml5FlushState {
  65. eNotFlushing = 0, // not flushing
  66. eInFlush = 1, // the Flush() method is on the call stack
  67. eInDocUpdate = 2, // inside an update batch on the document
  68. eNotifying = 3 // flushing pending append notifications
  69. };
  70. class nsHtml5TreeOpExecutor : public nsContentSink,
  71. public nsIContentSink,
  72. public nsAHtml5TreeOpSink
  73. {
  74. friend class nsHtml5FlushLoopGuard;
  75. public:
  76. NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
  77. NS_DECL_ISUPPORTS_INHERITED
  78. NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
  79. private:
  80. #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
  81. static PRUint32 sAppendBatchMaxSize;
  82. static PRUint32 sAppendBatchSlotsExamined;
  83. static PRUint32 sAppendBatchExaminations;
  84. static PRUint32 sLongestTimeOffTheEventLoop;
  85. static PRUint32 sTimesFlushLoopInterrupted;
  86. #endif
  87. /**
  88. * Whether EOF needs to be suppressed
  89. */
  90. bool mSuppressEOF;
  91. bool mReadingFromStage;
  92. nsTArray<nsHtml5TreeOperation> mOpQueue;
  93. nsTArray<nsIContentPtr> mElementsSeenInThisAppendBatch;
  94. nsTArray<nsHtml5PendingNotification> mPendingNotifications;
  95. nsHtml5StreamParser* mStreamParser;
  96. nsCOMArray<nsIContent> mOwnedElements;
  97. /**
  98. * URLs already preloaded/preloading.
  99. */
  100. nsCStringHashSet mPreloadedURLs;
  101. nsCOMPtr<nsIURI> mSpeculationBaseURI;
  102. nsCOMPtr<nsIURI> mViewSourceBaseURI;
  103. /**
  104. * Whether the parser has started
  105. */
  106. bool mStarted;
  107. nsHtml5TreeOpStage mStage;
  108. eHtml5FlushState mFlushState;
  109. bool mRunFlushLoopOnStack;
  110. bool mCallContinueInterruptedParsingIfEnabled;
  111. /**
  112. * True if this parser should refuse to process any more input.
  113. * Currently, the only way a parser can break is if it drops some input
  114. * due to a memory allocation failure. In such a case, the whole parser
  115. * needs to be marked as broken, because some input has been lost and
  116. * parsing more input could lead to a DOM where pieces of HTML source
  117. * that weren't supposed to become scripts become scripts.
  118. */
  119. bool mBroken;
  120. public:
  121. nsHtml5TreeOpExecutor(bool aRunsToCompletion = false);
  122. virtual ~nsHtml5TreeOpExecutor();
  123. // nsIContentSink
  124. /**
  125. * Unimplemented. For interface compat only.
  126. */
  127. NS_IMETHOD WillParse();
  128. /**
  129. *
  130. */
  131. NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
  132. NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
  133. "Script global object not ready");
  134. mDocument->AddObserver(this);
  135. WillBuildModelImpl();
  136. GetDocument()->BeginLoad();
  137. return NS_OK;
  138. }
  139. /**
  140. * Emits EOF.
  141. */
  142. NS_IMETHOD DidBuildModel(bool aTerminated);
  143. /**
  144. * Forwards to nsContentSink
  145. */
  146. NS_IMETHOD WillInterrupt();
  147. /**
  148. * Unimplemented. For interface compat only.
  149. */
  150. NS_IMETHOD WillResume();
  151. /**
  152. * Sets the parser.
  153. */
  154. NS_IMETHOD SetParser(nsParserBase* aParser);
  155. /**
  156. * No-op for backwards compat.
  157. */
  158. virtual void FlushPendingNotifications(mozFlushType aType);
  159. /**
  160. * Don't call. For interface compat only.
  161. */
  162. NS_IMETHOD SetDocumentCharset(nsACString& aCharset) {
  163. NS_NOTREACHED("No one should call this.");
  164. return NS_ERROR_NOT_IMPLEMENTED;
  165. }
  166. /**
  167. * Returns the document.
  168. */
  169. virtual nsISupports *GetTarget();
  170. // nsContentSink methods
  171. virtual void UpdateChildCounts();
  172. virtual nsresult FlushTags();
  173. virtual void ContinueInterruptedParsingAsync();
  174. /**
  175. * Sets up style sheet load / parse
  176. */
  177. void UpdateStyleSheet(nsIContent* aElement);
  178. // Getters and setters for fields from nsContentSink
  179. nsIDocument* GetDocument() {
  180. return mDocument;
  181. }
  182. nsNodeInfoManager* GetNodeInfoManager() {
  183. return mNodeInfoManager;
  184. }
  185. nsIDocShell* GetDocShell() {
  186. return mDocShell;
  187. }
  188. bool IsScriptExecuting() {
  189. return IsScriptExecutingImpl();
  190. }
  191. void SetNodeInfoManager(nsNodeInfoManager* aManager) {
  192. mNodeInfoManager = aManager;
  193. }
  194. // Not from interface
  195. void SetDocumentCharsetAndSource(nsACString& aCharset, PRInt32 aCharsetSource);
  196. void SetStreamParser(nsHtml5StreamParser* aStreamParser) {
  197. mStreamParser = aStreamParser;
  198. }
  199. void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine);
  200. bool IsScriptEnabled();
  201. /**
  202. * Enables the fragment mode.
  203. *
  204. * @param aPreventScriptExecution if true, scripts are prevented from
  205. * executing; don't set to false when parsing a fragment directly into
  206. * a document--only when parsing to an actual DOM fragment
  207. */
  208. void EnableFragmentMode(bool aPreventScriptExecution) {
  209. mPreventScriptExecution = aPreventScriptExecution;
  210. }
  211. void PreventScriptExecution() {
  212. mPreventScriptExecution = true;
  213. }
  214. bool BelongsToStringParser() {
  215. return mRunsToCompletion;
  216. }
  217. /**
  218. * Marks this parser as broken and tells the stream parser (if any) to
  219. * terminate.
  220. */
  221. void MarkAsBroken();
  222. /**
  223. * Checks if this parser is broken.
  224. */
  225. inline bool IsBroken() {
  226. NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  227. return mBroken;
  228. }
  229. inline void BeginDocUpdate() {
  230. NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
  231. NS_PRECONDITION(mParser, "Started update without parser.");
  232. mFlushState = eInDocUpdate;
  233. mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
  234. }
  235. inline void EndDocUpdate() {
  236. NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync");
  237. if (mFlushState == eInDocUpdate) {
  238. FlushPendingAppendNotifications();
  239. mFlushState = eInFlush;
  240. mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
  241. }
  242. }
  243. void PostPendingAppendNotification(nsIContent* aParent, nsIContent* aChild) {
  244. bool newParent = true;
  245. const nsIContentPtr* first = mElementsSeenInThisAppendBatch.Elements();
  246. const nsIContentPtr* last = first + mElementsSeenInThisAppendBatch.Length() - 1;
  247. for (const nsIContentPtr* iter = last; iter >= first; --iter) {
  248. #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
  249. sAppendBatchSlotsExamined++;
  250. #endif
  251. if (*iter == aParent) {
  252. newParent = false;
  253. break;
  254. }
  255. }
  256. if (aChild->IsElement()) {
  257. mElementsSeenInThisAppendBatch.AppendElement(aChild);
  258. }
  259. mElementsSeenInThisAppendBatch.AppendElement(aParent);
  260. if (newParent) {
  261. mPendingNotifications.AppendElement(aParent);
  262. }
  263. #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
  264. sAppendBatchExaminations++;
  265. #endif
  266. }
  267. void FlushPendingAppendNotifications() {
  268. NS_PRECONDITION(mFlushState == eInDocUpdate, "Notifications flushed outside update");
  269. mFlushState = eNotifying;
  270. const nsHtml5PendingNotification* start = mPendingNotifications.Elements();
  271. const nsHtml5PendingNotification* end = start + mPendingNotifications.Length();
  272. for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) {
  273. iter->Fire();
  274. }
  275. mPendingNotifications.Clear();
  276. #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
  277. if (mElementsSeenInThisAppendBatch.Length() > sAppendBatchMaxSize) {
  278. sAppendBatchMaxSize = mElementsSeenInThisAppendBatch.Length();
  279. }
  280. #endif
  281. mElementsSeenInThisAppendBatch.Clear();
  282. NS_ASSERTION(mFlushState == eNotifying, "mFlushState out of sync");
  283. mFlushState = eInDocUpdate;
  284. }
  285. inline bool HaveNotified(nsIContent* aNode) {
  286. NS_PRECONDITION(aNode, "HaveNotified called with null argument.");
  287. const nsHtml5PendingNotification* start = mPendingNotifications.Elements();
  288. const nsHtml5PendingNotification* end = start + mPendingNotifications.Length();
  289. for (;;) {
  290. nsIContent* parent = aNode->GetParent();
  291. if (!parent) {
  292. return true;
  293. }
  294. for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) {
  295. if (iter->Contains(parent)) {
  296. return iter->HaveNotifiedIndex(parent->IndexOf(aNode));
  297. }
  298. }
  299. aNode = parent;
  300. }
  301. }
  302. void StartLayout();
  303. void SetDocumentMode(nsHtml5DocumentMode m);
  304. nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
  305. nsISupports* aContainer, nsIChannel* aChannel);
  306. void FlushSpeculativeLoads();
  307. void RunFlushLoop();
  308. void FlushDocumentWrite();
  309. void MaybeSuspend();
  310. void Start();
  311. void NeedsCharsetSwitchTo(const char* aEncoding, PRInt32 aSource);
  312. bool IsComplete() {
  313. return !mParser;
  314. }
  315. bool HasStarted() {
  316. return mStarted;
  317. }
  318. bool IsFlushing() {
  319. return mFlushState >= eInFlush;
  320. }
  321. #ifdef DEBUG
  322. bool IsInFlushLoop() {
  323. return mRunFlushLoopOnStack;
  324. }
  325. #endif
  326. void RunScript(nsIContent* aScriptElement);
  327. void Reset();
  328. inline void HoldElement(nsIContent* aContent) {
  329. mOwnedElements.AppendObject(aContent);
  330. }
  331. void DropHeldElements();
  332. /**
  333. * Flush the operations from the tree operations from the argument
  334. * queue unconditionally. (This is for the main thread case.)
  335. */
  336. virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
  337. nsHtml5TreeOpStage* GetStage() {
  338. return &mStage;
  339. }
  340. void StartReadingFromStage() {
  341. mReadingFromStage = true;
  342. }
  343. void StreamEnded();
  344. #ifdef DEBUG
  345. void AssertStageEmpty() {
  346. mStage.AssertEmpty();
  347. }
  348. #endif
  349. nsIURI* GetViewSourceBaseURI();
  350. void PreloadScript(const nsAString& aURL,
  351. const nsAString& aCharset,
  352. const nsAString& aType);
  353. void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
  354. void PreloadImage(const nsAString& aURL, const nsAString& aCrossOrigin);
  355. void SetSpeculationBase(const nsAString& aURL);
  356. private:
  357. nsHtml5Parser* GetParser();
  358. /**
  359. * Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
  360. */
  361. already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
  362. };
  363. #endif // nsHtml5TreeOpExecutor_h__